@bendyline/gezel-inference 1.0.6 → 1.0.8
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.js +1 -0
- 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 +3 -1
- package/dist/http/routes/v1-images.js +48 -99
- package/dist/inference-service.js +2 -1
- package/dist/knowledge/manager.d.ts +3 -27
- package/dist/knowledge/manager.js +8 -364
- package/dist/knowledge/unified-search.d.ts +61 -0
- package/dist/knowledge/unified-search.js +373 -0
- 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 +368 -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/mlx/async-file-handoff.d.ts +9 -1
- package/dist/providers/mlx/async-file-handoff.js +32 -4
- package/dist/providers/mlx/provider.js +41 -20
- package/dist/providers/mlx/python/gezel_mlx_server.py +9 -2
- package/dist/providers/mlx/python/spec_decode.py +44 -5
- package/dist/providers/mlx/runtime-diagnostics.d.ts +6 -0
- package/dist/providers/mlx/runtime-diagnostics.js +18 -1
- package/dist/providers/mlx/tool-call-protocol.d.ts +4 -3
- package/dist/providers/mlx/tool-call-protocol.js +4 -3
- 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 +5 -1
- package/dist/providers/native/device-capacity-ledger.js +60 -14
- 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 +40 -0
- package/dist/providers/native/process-identity.js +156 -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 +19 -5
- package/dist/runtime-lock.js +116 -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
|
@@ -40,12 +40,12 @@
|
|
|
40
40
|
* but logs that it could not anchor to a baked-in digest.
|
|
41
41
|
*/
|
|
42
42
|
/** Native release version this build pins. Placeholder until first public release. */
|
|
43
|
-
export declare const NATIVE_ENGINE_RELEASE = "0.1.
|
|
43
|
+
export declare const NATIVE_ENGINE_RELEASE = "0.1.57";
|
|
44
44
|
/** sha256 of the pinned release's `SHA256SUMS` asset. All-zeros = unpinned. */
|
|
45
|
-
export declare const SHA256SUMS_DIGEST = "
|
|
46
|
-
/** Exact SHA256 values for every engine archive published by native-v0.1.
|
|
45
|
+
export declare const SHA256SUMS_DIGEST = "977b0ad4cdb8983ed44d7a3223267038b1f35b2806d8a1807d7d15c5c7e94f47";
|
|
46
|
+
/** Exact SHA256 values for every engine archive published by native-v0.1.57. */
|
|
47
47
|
export declare const NATIVE_ENGINE_ARCHIVE_SHA256: Readonly<Record<string, string>>;
|
|
48
|
-
/** Download size in bytes of every engine archive published by native-v0.1.
|
|
48
|
+
/** Download size in bytes of every engine archive published by native-v0.1.57. */
|
|
49
49
|
export declare const NATIVE_ENGINE_ARCHIVE_BYTES: Readonly<Record<string, number>>;
|
|
50
50
|
/**
|
|
51
51
|
* True only when the pinned release's standalone macOS archives were
|
|
@@ -40,47 +40,47 @@
|
|
|
40
40
|
* but logs that it could not anchor to a baked-in digest.
|
|
41
41
|
*/
|
|
42
42
|
/** Native release version this build pins. Placeholder until first public release. */
|
|
43
|
-
export const NATIVE_ENGINE_RELEASE = '0.1.
|
|
43
|
+
export const NATIVE_ENGINE_RELEASE = '0.1.57';
|
|
44
44
|
/** sha256 of the pinned release's `SHA256SUMS` asset. All-zeros = unpinned. */
|
|
45
|
-
export const SHA256SUMS_DIGEST = '
|
|
45
|
+
export const SHA256SUMS_DIGEST = '977b0ad4cdb8983ed44d7a3223267038b1f35b2806d8a1807d7d15c5c7e94f47';
|
|
46
46
|
// BEGIN PINNED NATIVE ARCHIVE HASHES
|
|
47
|
-
/** Exact SHA256 values for every engine archive published by native-v0.1.
|
|
47
|
+
/** Exact SHA256 values for every engine archive published by native-v0.1.57. */
|
|
48
48
|
export const NATIVE_ENGINE_ARCHIVE_SHA256 = Object.freeze({
|
|
49
|
-
'gezel-native-0.1.
|
|
50
|
-
'gezel-native-0.1.
|
|
51
|
-
'gezel-native-0.1.
|
|
52
|
-
'gezel-native-0.1.
|
|
53
|
-
'gezel-native-0.1.
|
|
54
|
-
'gezel-native-0.1.
|
|
55
|
-
'gezel-native-0.1.
|
|
56
|
-
'gezel-native-0.1.
|
|
57
|
-
'gezel-native-0.1.
|
|
58
|
-
'gezel-native-0.1.
|
|
59
|
-
'gezel-native-0.1.
|
|
60
|
-
'gezel-native-0.1.
|
|
61
|
-
'gezel-native-0.1.
|
|
62
|
-
'gezel-native-0.1.
|
|
63
|
-
'gezel-native-0.1.
|
|
64
|
-
'gezel-native-0.1.
|
|
49
|
+
'gezel-native-0.1.57-darwin-arm64-metal.tar.gz': '6b97be648b032980cf73663db4424140f4cc04577972dc283b956af677ce884a',
|
|
50
|
+
'gezel-native-0.1.57-darwin-arm64.tar.gz': 'e8ca070c4d3961e95946ed32fe7376ddbc97e407dd3f5b78e26cab20c8fb286a',
|
|
51
|
+
'gezel-native-0.1.57-linux-arm64-cpu.tar.gz': 'cc87822123602a0024c63a0d65789232a665140cb2fe37b106d5bd680d12f795',
|
|
52
|
+
'gezel-native-0.1.57-linux-arm64-cuda.tar.gz': '315a441e5698800945f7030e9da961e20f3cf379a4b75c98e1d6ba88c8fee1c5',
|
|
53
|
+
'gezel-native-0.1.57-linux-arm64.tar.gz': '0684b77d23f008f8a2aa0d9d8931437bacd8569fb6c10d3ddab61a5fb25b6a06',
|
|
54
|
+
'gezel-native-0.1.57-linux-x64-cpu.tar.gz': 'b2100842f51c393b92667535798e49932173d52626f5ae72c40005b3408f1201',
|
|
55
|
+
'gezel-native-0.1.57-linux-x64-cuda.tar.gz': '2fca7720614687e8491be17366060c604551257199d395f795b27a87216bcde9',
|
|
56
|
+
'gezel-native-0.1.57-linux-x64-vulkan.tar.gz': 'c1014a1fe1d241ad1ee9abfa5badafcce208cbff8d38b4b4deb4779f31a95bef',
|
|
57
|
+
'gezel-native-0.1.57-linux-x64.tar.gz': 'a7f3dbe1941c3d03ed121c32be1bdd199aea47080b03d89989590a0f5d13c5ff',
|
|
58
|
+
'gezel-native-0.1.57-win32-arm64-cpu.zip': '12d66681cec1872f23b015c7d0bd45a25ed262342da6b0ecea8bea303adf2105',
|
|
59
|
+
'gezel-native-0.1.57-win32-arm64-cuda.zip': 'b437a9f0894673179f91a1af75d5d7d058dafa1b4f48a57eab123596f1c21ca4',
|
|
60
|
+
'gezel-native-0.1.57-win32-arm64.zip': '21cddb8b733f9a2669d64b1ada18002f60bb2779f2d5e2aacfb88214db00da7e',
|
|
61
|
+
'gezel-native-0.1.57-win32-x64-cpu.zip': 'a81f7c6754f1954a0ed5de1efc47d6194dc60d912138a9d2b8e4f992168b12c6',
|
|
62
|
+
'gezel-native-0.1.57-win32-x64-cuda.zip': '9e3f21713fe77c23536dd4124244ac3834f90c82d6e470cb6836a73bb4f7c592',
|
|
63
|
+
'gezel-native-0.1.57-win32-x64-vulkan.zip': '0d473300317b4f6cb072bdd7875ce1536a85fe50e781c435e1aef1c4cbdb08e5',
|
|
64
|
+
'gezel-native-0.1.57-win32-x64.zip': '89a073abcfc97e50b9a0c39f56862b51ac21139b7ca4a3e7a75105dca56292a2',
|
|
65
65
|
});
|
|
66
|
-
/** Download size in bytes of every engine archive published by native-v0.1.
|
|
66
|
+
/** Download size in bytes of every engine archive published by native-v0.1.57. */
|
|
67
67
|
export const NATIVE_ENGINE_ARCHIVE_BYTES = Object.freeze({
|
|
68
|
-
'gezel-native-0.1.
|
|
69
|
-
'gezel-native-0.1.
|
|
70
|
-
'gezel-native-0.1.
|
|
71
|
-
'gezel-native-0.1.
|
|
72
|
-
'gezel-native-0.1.
|
|
73
|
-
'gezel-native-0.1.
|
|
74
|
-
'gezel-native-0.1.
|
|
75
|
-
'gezel-native-0.1.
|
|
76
|
-
'gezel-native-0.1.
|
|
77
|
-
'gezel-native-0.1.
|
|
78
|
-
'gezel-native-0.1.
|
|
79
|
-
'gezel-native-0.1.
|
|
80
|
-
'gezel-native-0.1.
|
|
81
|
-
'gezel-native-0.1.
|
|
82
|
-
'gezel-native-0.1.
|
|
83
|
-
'gezel-native-0.1.
|
|
68
|
+
'gezel-native-0.1.57-darwin-arm64-metal.tar.gz': 7387963,
|
|
69
|
+
'gezel-native-0.1.57-darwin-arm64.tar.gz': 42223786,
|
|
70
|
+
'gezel-native-0.1.57-linux-arm64-cpu.tar.gz': 9223249,
|
|
71
|
+
'gezel-native-0.1.57-linux-arm64-cuda.tar.gz': 621626283,
|
|
72
|
+
'gezel-native-0.1.57-linux-arm64.tar.gz': 42040003,
|
|
73
|
+
'gezel-native-0.1.57-linux-x64-cpu.tar.gz': 13398337,
|
|
74
|
+
'gezel-native-0.1.57-linux-x64-cuda.tar.gz': 1000325316,
|
|
75
|
+
'gezel-native-0.1.57-linux-x64-vulkan.tar.gz': 26290440,
|
|
76
|
+
'gezel-native-0.1.57-linux-x64.tar.gz': 57190295,
|
|
77
|
+
'gezel-native-0.1.57-win32-arm64-cpu.zip': 6049234,
|
|
78
|
+
'gezel-native-0.1.57-win32-arm64-cuda.zip': 243595554,
|
|
79
|
+
'gezel-native-0.1.57-win32-arm64.zip': 42236484,
|
|
80
|
+
'gezel-native-0.1.57-win32-x64-cpu.zip': 9131460,
|
|
81
|
+
'gezel-native-0.1.57-win32-x64-cuda.zip': 756439338,
|
|
82
|
+
'gezel-native-0.1.57-win32-x64-vulkan.zip': 21673895,
|
|
83
|
+
'gezel-native-0.1.57-win32-x64.zip': 55668381,
|
|
84
84
|
});
|
|
85
85
|
// END PINNED NATIVE ARCHIVE HASHES
|
|
86
86
|
/**
|
package/dist/engines/resolver.js
CHANGED
|
@@ -18,6 +18,18 @@ declare module 'hono' {
|
|
|
18
18
|
projectId?: string;
|
|
19
19
|
gezelId?: string;
|
|
20
20
|
team?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* The signed-in account behind an account session (ADR 0029, the
|
|
23
|
+
* principal from ADR 0004). Absent for app, device and owner
|
|
24
|
+
* credentials.
|
|
25
|
+
*/
|
|
26
|
+
account?: {
|
|
27
|
+
id: string;
|
|
28
|
+
source: 'password' | 'device' | 'os';
|
|
29
|
+
label: string;
|
|
30
|
+
};
|
|
31
|
+
/** The account session this request came in on, for "sign out this browser". */
|
|
32
|
+
accountSessionId?: string;
|
|
21
33
|
};
|
|
22
34
|
}
|
|
23
35
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Hono } from 'hono';
|
|
2
|
+
import type { ImageModelPullRegistry } from '../../providers/image/pull-registry.js';
|
|
3
|
+
/** One resumable download protocol, with separate registries for models and styles. */
|
|
4
|
+
export declare function registerImageEnsureRoutes(app: Hono, prefix: string, pulls: ImageModelPullRegistry, qualified: (id: string) => string): void;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { streamSSE } from 'hono/streaming';
|
|
2
|
+
function apiError(code, message) {
|
|
3
|
+
return { error: { message, type: 'invalid_request_error', code } };
|
|
4
|
+
}
|
|
5
|
+
function ensureEvent(jobId, event, totalBytes, qualified) {
|
|
6
|
+
const job = { jobId, modelId: qualified(jobId) };
|
|
7
|
+
switch (event.type) {
|
|
8
|
+
case 'progress':
|
|
9
|
+
return {
|
|
10
|
+
...job,
|
|
11
|
+
type: 'progress',
|
|
12
|
+
bytesWritten: event.bytesWritten,
|
|
13
|
+
totalBytes: event.totalBytes ?? totalBytes,
|
|
14
|
+
};
|
|
15
|
+
case 'retrying':
|
|
16
|
+
return {
|
|
17
|
+
...job,
|
|
18
|
+
type: 'retrying',
|
|
19
|
+
attempt: event.attempt,
|
|
20
|
+
maxAttempts: event.maxAttempts,
|
|
21
|
+
delayMs: event.delayMs,
|
|
22
|
+
reason: event.reason,
|
|
23
|
+
};
|
|
24
|
+
case 'error':
|
|
25
|
+
return { ...job, type: 'error', error: event.error };
|
|
26
|
+
case 'done':
|
|
27
|
+
return { ...job, type: 'done' };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function pullStatus(pull) {
|
|
31
|
+
if (!pull.finished)
|
|
32
|
+
return 'running';
|
|
33
|
+
return pull.error ? 'error' : 'done';
|
|
34
|
+
}
|
|
35
|
+
/** One resumable download protocol, with separate registries for models and styles. */
|
|
36
|
+
export function registerImageEnsureRoutes(app, prefix, pulls, qualified) {
|
|
37
|
+
app.get(`${prefix}/:jobId`, (c) => {
|
|
38
|
+
const pull = pulls.get(c.req.param('jobId'));
|
|
39
|
+
if (!pull)
|
|
40
|
+
return c.json(apiError('job_not_found', 'job not found'), 404);
|
|
41
|
+
return c.json({
|
|
42
|
+
job_id: pull.id,
|
|
43
|
+
model_id: qualified(pull.id),
|
|
44
|
+
status: pullStatus(pull),
|
|
45
|
+
started_at: Date.parse(pull.startedAt),
|
|
46
|
+
bytes_written: pull.bytesWritten,
|
|
47
|
+
total_bytes: pull.totalBytes,
|
|
48
|
+
...(pull.error ? { error: pull.error } : {}),
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
app.get(`${prefix}/:jobId/events`, (c) => {
|
|
52
|
+
const jobId = c.req.param('jobId');
|
|
53
|
+
const snapshot = pulls.get(jobId);
|
|
54
|
+
if (!snapshot)
|
|
55
|
+
return c.json(apiError('job_not_found', 'job not found'), 404);
|
|
56
|
+
return streamSSE(c, async (stream) => {
|
|
57
|
+
let closed = false;
|
|
58
|
+
let sawError = false;
|
|
59
|
+
const unsubscribe = pulls.subscribe(jobId, (event) => {
|
|
60
|
+
// A failed pull ends with an error and then its done; the error is terminal.
|
|
61
|
+
if (sawError)
|
|
62
|
+
return;
|
|
63
|
+
if (event.type === 'error')
|
|
64
|
+
sawError = true;
|
|
65
|
+
const terminal = event.type === 'done' || event.type === 'error';
|
|
66
|
+
void stream
|
|
67
|
+
.writeSSE({
|
|
68
|
+
data: JSON.stringify(ensureEvent(jobId, event, snapshot.totalBytes, qualified)),
|
|
69
|
+
})
|
|
70
|
+
.then(() => {
|
|
71
|
+
if (terminal)
|
|
72
|
+
closed = true;
|
|
73
|
+
})
|
|
74
|
+
.catch(() => {
|
|
75
|
+
closed = true;
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
if (!unsubscribe)
|
|
79
|
+
return;
|
|
80
|
+
stream.onAbort(() => {
|
|
81
|
+
closed = true;
|
|
82
|
+
unsubscribe();
|
|
83
|
+
});
|
|
84
|
+
while (!closed) {
|
|
85
|
+
await stream.sleep(1000);
|
|
86
|
+
try {
|
|
87
|
+
await stream.writeSSE({ event: 'ping', data: '' });
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
closed = true;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
unsubscribe();
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
app.delete(`${prefix}/:jobId`, (c) => {
|
|
97
|
+
if (!pulls.cancel(c.req.param('jobId'))) {
|
|
98
|
+
return c.json(apiError('job_not_found', 'No download in progress for that job'), 404);
|
|
99
|
+
}
|
|
100
|
+
return c.json({ cancelled: true });
|
|
101
|
+
});
|
|
102
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import type { ImageModelPullRegistry } from '../../providers/image/pull-registry.js';
|
|
3
|
+
import type { ImagesRouteContext } from './v1-images.js';
|
|
4
|
+
/** App-scoped access to catalog LoRAs; no paths, URLs or engine arguments from callers. */
|
|
5
|
+
export declare function v1ImageStylesRoutes(ctx: ImagesRouteContext, pulls: ImageModelPullRegistry): Hono;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { isSafeModelId } from '../../models/model-id.js';
|
|
4
|
+
import { registerImageEnsureRoutes } from './image-ensure-routes.js';
|
|
5
|
+
const styleId = z.string().min(1).max(512).refine(isSafeModelId);
|
|
6
|
+
const ensureRequest = z.object({ style: styleId }).strict();
|
|
7
|
+
const apiError = (code, message) => ({
|
|
8
|
+
error: { code, message, type: 'invalid_request_error' },
|
|
9
|
+
});
|
|
10
|
+
/** App-scoped access to catalog LoRAs; no paths, URLs or engine arguments from callers. */
|
|
11
|
+
export function v1ImageStylesRoutes(ctx, pulls) {
|
|
12
|
+
const app = new Hono();
|
|
13
|
+
app.get('/', async (c) => {
|
|
14
|
+
const provider = await ctx.images.current();
|
|
15
|
+
if (!provider.listLoras || !provider.pullLora || !provider.deleteLora) {
|
|
16
|
+
return c.json(apiError('styles_unsupported', 'Update Gezel to add image styles.'), 409);
|
|
17
|
+
}
|
|
18
|
+
const [installed, catalog] = await Promise.all([
|
|
19
|
+
provider.listLoras(),
|
|
20
|
+
ctx.catalog.list('image-lora'),
|
|
21
|
+
]);
|
|
22
|
+
const data = [];
|
|
23
|
+
for (const item of catalog) {
|
|
24
|
+
const m = item.manifest;
|
|
25
|
+
if (m.kind !== 'image-lora' || m.purpose !== 'style')
|
|
26
|
+
continue;
|
|
27
|
+
data.push({
|
|
28
|
+
id: m.id,
|
|
29
|
+
object: 'image_style',
|
|
30
|
+
name: m.name,
|
|
31
|
+
description: m.description,
|
|
32
|
+
availability: installed.some((l) => l.id === m.id)
|
|
33
|
+
? 'available'
|
|
34
|
+
: pulls.get(m.id)?.finished === false
|
|
35
|
+
? 'downloading'
|
|
36
|
+
: 'download-required',
|
|
37
|
+
base_models: m.baseModels.map((id) => `sd-cpp:${id}`),
|
|
38
|
+
download_bytes: m.approxSizeBytes,
|
|
39
|
+
...((m.licenseShortName ?? m.license) ? { license: m.licenseShortName ?? m.license } : {}),
|
|
40
|
+
commercial_use: m.commercialUse,
|
|
41
|
+
...(m.licenseUrl ? { license_url: m.licenseUrl } : {}),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
for (const l of installed) {
|
|
45
|
+
if (l.purpose !== 'style' || data.some((s) => s.id === l.id))
|
|
46
|
+
continue;
|
|
47
|
+
data.push({
|
|
48
|
+
id: l.id,
|
|
49
|
+
object: 'image_style',
|
|
50
|
+
name: l.name,
|
|
51
|
+
availability: 'available',
|
|
52
|
+
download_bytes: l.approxSizeBytes,
|
|
53
|
+
base_models: l.baseModels.map((id) => `sd-cpp:${id}`),
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return c.json({ object: 'list', data });
|
|
57
|
+
});
|
|
58
|
+
app.post('/ensure', async (c) => {
|
|
59
|
+
const { style } = ensureRequest.parse(await c.req.json());
|
|
60
|
+
const provider = await ctx.images.current();
|
|
61
|
+
if (!provider.listLoras || !provider.pullLora)
|
|
62
|
+
return c.json(apiError('styles_unsupported', 'Update Gezel to add image styles.'), 409);
|
|
63
|
+
const installed = await provider.listLoras();
|
|
64
|
+
if (installed.some((l) => l.id === style && l.purpose === 'style'))
|
|
65
|
+
return c.json({ status: 'ready', model_id: style });
|
|
66
|
+
const item = await ctx.catalog.get('image-lora', style);
|
|
67
|
+
if (item?.manifest.kind !== 'image-lora' || item.manifest.purpose !== 'style')
|
|
68
|
+
return c.json(apiError('unknown_style', 'That style is not in the catalog.'), 404);
|
|
69
|
+
const models = await provider.listInstalledModels();
|
|
70
|
+
if (!models.some((model) => item.manifest.kind === 'image-lora' && item.manifest.baseModels.includes(model.id))) {
|
|
71
|
+
return c.json(apiError('style_model_required', 'Download a compatible image model before adding this style.'), 409);
|
|
72
|
+
}
|
|
73
|
+
await pulls.start(style);
|
|
74
|
+
return c.json({ status: 'downloading', model_id: style, job_id: style }, 202);
|
|
75
|
+
});
|
|
76
|
+
registerImageEnsureRoutes(app, '/ensure', pulls, (id) => id);
|
|
77
|
+
app.delete('/:id', async (c) => {
|
|
78
|
+
const id = styleId.parse(c.req.param('id'));
|
|
79
|
+
if (pulls.get(id)?.finished === false)
|
|
80
|
+
return c.json(apiError('style_downloading', 'Cancel the style download before removing it.'), 409);
|
|
81
|
+
await ctx.images.reset();
|
|
82
|
+
const provider = await ctx.images.current();
|
|
83
|
+
if (!provider.deleteLora || !provider.listLoras)
|
|
84
|
+
return c.json(apiError('styles_unsupported', 'Update Gezel to manage image styles.'), 409);
|
|
85
|
+
if (!(await provider.listLoras()).some((l) => l.id === id && l.purpose === 'style'))
|
|
86
|
+
return c.json(apiError('unknown_style', 'That style is not installed.'), 404);
|
|
87
|
+
await provider.deleteLora(id);
|
|
88
|
+
return c.body(null, 204);
|
|
89
|
+
});
|
|
90
|
+
return app;
|
|
91
|
+
}
|
|
@@ -16,6 +16,7 @@ export interface ImagesRouteContext {
|
|
|
16
16
|
/** Stop the engine, draining renders, before its model files change. */
|
|
17
17
|
reset(): Promise<void>;
|
|
18
18
|
pulls: ImageModelPullRegistry;
|
|
19
|
+
loraPulls?: ImageModelPullRegistry;
|
|
19
20
|
};
|
|
20
21
|
}
|
|
21
22
|
/**
|
|
@@ -24,7 +25,8 @@ export interface ImagesRouteContext {
|
|
|
24
25
|
* GET /models Installed and downloadable image models,
|
|
25
26
|
* with this machine's fit. Never part of
|
|
26
27
|
* `/v1/models`, so chat pickers never
|
|
27
|
-
* offer them.
|
|
28
|
+
* offer them. Models that only change a
|
|
29
|
+
* given picture are left out.
|
|
28
30
|
* POST /models/ensure `{model: "sd-cpp:<id>"}` → ready, or a
|
|
29
31
|
* background download (`job_id`).
|
|
30
32
|
* GET /models/ensure/:jobId Snapshot of a download.
|
|
@@ -4,8 +4,11 @@ import { streamSSE } from 'hono/streaming';
|
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { imageCatalogFits, imageModelFit, wontRunRefusal } from '../../models/image-fit.js';
|
|
6
6
|
import { ReadOnlyModelError, modelExistsOnlyReadOnly, modelStorageRoots, } from '../../models/storage-roots.js';
|
|
7
|
+
import { resolveImageEditCapability } from '../../providers/image/img2img-support.js';
|
|
7
8
|
import { NoLocalImageModelError } from '../../providers/image/local-factory.js';
|
|
8
|
-
import { ImageCapacityError, ImageGenerationCancelledError, } from '../../providers/image/types.js';
|
|
9
|
+
import { ImageCapacityError, ImageGenerationCancelledError, ImageSourceRequiredError, } from '../../providers/image/types.js';
|
|
10
|
+
import { registerImageEnsureRoutes } from './image-ensure-routes.js';
|
|
11
|
+
import { v1ImageStylesRoutes } from './v1-image-styles.js';
|
|
9
12
|
const log = createLogger('v1-images');
|
|
10
13
|
/** The one local image engine this host serves. */
|
|
11
14
|
const ENGINE = 'sd-cpp';
|
|
@@ -26,6 +29,17 @@ function catalogIdOf(model) {
|
|
|
26
29
|
function qualified(catalogId) {
|
|
27
30
|
return `${ENGINE}:${catalogId}`;
|
|
28
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Made only to change a picture it is given. Generations here take no source
|
|
34
|
+
* picture, so such a model can never render one: it is left out of the list
|
|
35
|
+
* and refused by name, never handed a blank page.
|
|
36
|
+
*/
|
|
37
|
+
function editsOnly(modelId, declared) {
|
|
38
|
+
return resolveImageEditCapability({ modelId, editOnly: declared }).editOnly;
|
|
39
|
+
}
|
|
40
|
+
function editOnlyError(name) {
|
|
41
|
+
return apiError('edit_only_model', `${name} only changes a picture it is given and cannot draw a new one. Pick another image model.`);
|
|
42
|
+
}
|
|
29
43
|
function downloadBytes(manifest) {
|
|
30
44
|
return (manifest.approxSizeBytes + manifest.auxiliaryFiles.reduce((n, f) => n + f.approxSizeBytes, 0));
|
|
31
45
|
}
|
|
@@ -36,6 +50,7 @@ function describeManifest(manifest) {
|
|
|
36
50
|
description: manifest.description,
|
|
37
51
|
download_bytes: downloadBytes(manifest),
|
|
38
52
|
...(license ? { license } : {}),
|
|
53
|
+
...(manifest.licenseUrl ? { license_url: manifest.licenseUrl } : {}),
|
|
39
54
|
commercial_use: manifest.commercialUse,
|
|
40
55
|
...(manifest.rendersText !== undefined ? { renders_text: manifest.rendersText } : {}),
|
|
41
56
|
...(manifest.promptStyle ? { prompt_style: manifest.promptStyle } : {}),
|
|
@@ -49,36 +64,6 @@ function describeManifest(manifest) {
|
|
|
49
64
|
: {}),
|
|
50
65
|
};
|
|
51
66
|
}
|
|
52
|
-
function ensureEvent(jobId, event, totalBytes) {
|
|
53
|
-
const job = { jobId, modelId: qualified(jobId) };
|
|
54
|
-
switch (event.type) {
|
|
55
|
-
case 'progress':
|
|
56
|
-
return {
|
|
57
|
-
...job,
|
|
58
|
-
type: 'progress',
|
|
59
|
-
bytesWritten: event.bytesWritten,
|
|
60
|
-
totalBytes: event.totalBytes ?? totalBytes,
|
|
61
|
-
};
|
|
62
|
-
case 'retrying':
|
|
63
|
-
return {
|
|
64
|
-
...job,
|
|
65
|
-
type: 'retrying',
|
|
66
|
-
attempt: event.attempt,
|
|
67
|
-
maxAttempts: event.maxAttempts,
|
|
68
|
-
delayMs: event.delayMs,
|
|
69
|
-
reason: event.reason,
|
|
70
|
-
};
|
|
71
|
-
case 'error':
|
|
72
|
-
return { ...job, type: 'error', error: event.error };
|
|
73
|
-
case 'done':
|
|
74
|
-
return { ...job, type: 'done' };
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
function pullStatus(pull) {
|
|
78
|
-
if (!pull.finished)
|
|
79
|
-
return 'running';
|
|
80
|
-
return pull.error ? 'error' : 'done';
|
|
81
|
-
}
|
|
82
67
|
function generationError(err) {
|
|
83
68
|
const message = err instanceof Error ? err.message : String(err);
|
|
84
69
|
if (err instanceof ImageGenerationCancelledError)
|
|
@@ -87,6 +72,8 @@ function generationError(err) {
|
|
|
87
72
|
return { code: err.code, message };
|
|
88
73
|
if (err instanceof NoLocalImageModelError)
|
|
89
74
|
return { code: err.code, message };
|
|
75
|
+
if (err instanceof ImageSourceRequiredError)
|
|
76
|
+
return { code: err.code, message };
|
|
90
77
|
const code = err?.code;
|
|
91
78
|
if (code === 'capacity-denied')
|
|
92
79
|
return { code: 'capacity_denied', message };
|
|
@@ -98,7 +85,8 @@ function generationError(err) {
|
|
|
98
85
|
* GET /models Installed and downloadable image models,
|
|
99
86
|
* with this machine's fit. Never part of
|
|
100
87
|
* `/v1/models`, so chat pickers never
|
|
101
|
-
* offer them.
|
|
88
|
+
* offer them. Models that only change a
|
|
89
|
+
* given picture are left out.
|
|
102
90
|
* POST /models/ensure `{model: "sd-cpp:<id>"}` → ready, or a
|
|
103
91
|
* background download (`job_id`).
|
|
104
92
|
* GET /models/ensure/:jobId Snapshot of a download.
|
|
@@ -117,6 +105,8 @@ function generationError(err) {
|
|
|
117
105
|
*/
|
|
118
106
|
export function v1ImagesRoutes(ctx) {
|
|
119
107
|
const app = new Hono();
|
|
108
|
+
if (ctx.images.loraPulls)
|
|
109
|
+
app.route('/styles', v1ImageStylesRoutes(ctx, ctx.images.loraPulls));
|
|
120
110
|
const fitContext = { catalog: ctx.catalog, readConfig: () => ctx.store.readConfig() };
|
|
121
111
|
app.get('/models', async (c) => {
|
|
122
112
|
const provider = await ctx.images.current();
|
|
@@ -142,6 +132,8 @@ export function v1ImagesRoutes(ctx) {
|
|
|
142
132
|
if (manifest.kind !== 'image-model')
|
|
143
133
|
continue;
|
|
144
134
|
seen.add(manifest.id);
|
|
135
|
+
if (editsOnly(manifest.id, manifest.editOnly))
|
|
136
|
+
continue;
|
|
145
137
|
const fit = fitById.get(manifest.id);
|
|
146
138
|
const isInstalled = installedIds.has(manifest.id);
|
|
147
139
|
data.push({
|
|
@@ -162,7 +154,7 @@ export function v1ImagesRoutes(ctx) {
|
|
|
162
154
|
}
|
|
163
155
|
// Installed models the catalog no longer lists still render.
|
|
164
156
|
for (const model of installed) {
|
|
165
|
-
if (seen.has(model.id))
|
|
157
|
+
if (seen.has(model.id) || editsOnly(model.id, model.editOnly))
|
|
166
158
|
continue;
|
|
167
159
|
data.push({
|
|
168
160
|
id: qualified(model.id),
|
|
@@ -182,8 +174,17 @@ export function v1ImagesRoutes(ctx) {
|
|
|
182
174
|
return c.json(apiError('unsupported_backend', `Image models are ${ENGINE}:<id>`), 400);
|
|
183
175
|
}
|
|
184
176
|
const provider = await ctx.images.current();
|
|
185
|
-
const installed = await
|
|
186
|
-
|
|
177
|
+
const [installed, item] = await Promise.all([
|
|
178
|
+
provider.listInstalledModels(),
|
|
179
|
+
ctx.catalog.get('image-model', id).catch(() => null),
|
|
180
|
+
]);
|
|
181
|
+
const local = installed.find((m) => m.id === id);
|
|
182
|
+
const listed = item?.manifest.kind === 'image-model' ? item.manifest : undefined;
|
|
183
|
+
// Downloading one would fetch gigabytes no generation here can use.
|
|
184
|
+
if (editsOnly(id, local?.editOnly ?? listed?.editOnly)) {
|
|
185
|
+
return c.json(editOnlyError(local?.name ?? listed?.name ?? model), 409);
|
|
186
|
+
}
|
|
187
|
+
if (local) {
|
|
187
188
|
return c.json({ status: 'ready', model_id: qualified(id) });
|
|
188
189
|
}
|
|
189
190
|
const refusal = await wontRunRefusal(() => imageModelFit(fitContext, id));
|
|
@@ -200,69 +201,7 @@ export function v1ImagesRoutes(ctx) {
|
|
|
200
201
|
}
|
|
201
202
|
return c.json({ status: 'downloading', model_id: qualified(id), job_id: id }, 202);
|
|
202
203
|
});
|
|
203
|
-
app
|
|
204
|
-
const pull = ctx.images.pulls.get(c.req.param('jobId'));
|
|
205
|
-
if (!pull)
|
|
206
|
-
return c.json(apiError('job_not_found', 'job not found'), 404);
|
|
207
|
-
return c.json({
|
|
208
|
-
job_id: pull.id,
|
|
209
|
-
model_id: qualified(pull.id),
|
|
210
|
-
status: pullStatus(pull),
|
|
211
|
-
started_at: Date.parse(pull.startedAt),
|
|
212
|
-
bytes_written: pull.bytesWritten,
|
|
213
|
-
total_bytes: pull.totalBytes,
|
|
214
|
-
...(pull.error ? { error: pull.error } : {}),
|
|
215
|
-
});
|
|
216
|
-
});
|
|
217
|
-
app.get('/models/ensure/:jobId/events', (c) => {
|
|
218
|
-
const jobId = c.req.param('jobId');
|
|
219
|
-
const snapshot = ctx.images.pulls.get(jobId);
|
|
220
|
-
if (!snapshot)
|
|
221
|
-
return c.json(apiError('job_not_found', 'job not found'), 404);
|
|
222
|
-
return streamSSE(c, async (stream) => {
|
|
223
|
-
let closed = false;
|
|
224
|
-
let sawError = false;
|
|
225
|
-
const unsubscribe = ctx.images.pulls.subscribe(jobId, (event) => {
|
|
226
|
-
// A failed pull ends with an error and then its done; the error is terminal.
|
|
227
|
-
if (sawError)
|
|
228
|
-
return;
|
|
229
|
-
if (event.type === 'error')
|
|
230
|
-
sawError = true;
|
|
231
|
-
const terminal = event.type === 'done' || event.type === 'error';
|
|
232
|
-
void stream
|
|
233
|
-
.writeSSE({ data: JSON.stringify(ensureEvent(jobId, event, snapshot.totalBytes)) })
|
|
234
|
-
.then(() => {
|
|
235
|
-
if (terminal)
|
|
236
|
-
closed = true;
|
|
237
|
-
})
|
|
238
|
-
.catch(() => {
|
|
239
|
-
closed = true;
|
|
240
|
-
});
|
|
241
|
-
});
|
|
242
|
-
if (!unsubscribe)
|
|
243
|
-
return;
|
|
244
|
-
stream.onAbort(() => {
|
|
245
|
-
closed = true;
|
|
246
|
-
unsubscribe();
|
|
247
|
-
});
|
|
248
|
-
while (!closed) {
|
|
249
|
-
await stream.sleep(1000);
|
|
250
|
-
try {
|
|
251
|
-
await stream.writeSSE({ event: 'ping', data: '' });
|
|
252
|
-
}
|
|
253
|
-
catch {
|
|
254
|
-
closed = true;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
unsubscribe();
|
|
258
|
-
});
|
|
259
|
-
});
|
|
260
|
-
app.delete('/models/ensure/:jobId', (c) => {
|
|
261
|
-
if (!ctx.images.pulls.cancel(c.req.param('jobId'))) {
|
|
262
|
-
return c.json(apiError('job_not_found', 'No download in progress for that job'), 404);
|
|
263
|
-
}
|
|
264
|
-
return c.json({ cancelled: true });
|
|
265
|
-
});
|
|
204
|
+
registerImageEnsureRoutes(app, '/models/ensure', ctx.images.pulls, qualified);
|
|
266
205
|
app.delete('/models/:id', async (c) => {
|
|
267
206
|
const id = catalogIdOf(decodeURIComponent(c.req.param('id')));
|
|
268
207
|
if (!id)
|
|
@@ -296,12 +235,21 @@ export function v1ImagesRoutes(ctx) {
|
|
|
296
235
|
return c.json(apiError('unsupported_backend', `Image models are ${ENGINE}:<id>`), 400);
|
|
297
236
|
}
|
|
298
237
|
const modelId = requested ?? (await provider.resolveDefaultModelId?.());
|
|
299
|
-
|
|
238
|
+
const chosen = modelId ? installed.find((m) => m.id === modelId) : undefined;
|
|
239
|
+
if (!modelId || !chosen) {
|
|
300
240
|
const error = requested
|
|
301
241
|
? apiError('model_not_installed', `${request.model} is not installed; ensure it first`)
|
|
302
242
|
: apiError('no_local_image_model', new NoLocalImageModelError().message);
|
|
303
243
|
return c.json(error, 409);
|
|
304
244
|
}
|
|
245
|
+
if (editsOnly(modelId, chosen.editOnly))
|
|
246
|
+
return c.json(editOnlyError(chosen.name), 409);
|
|
247
|
+
const style = request.style
|
|
248
|
+
? chosen.loras?.find((lora) => lora.id === request.style && lora.purpose === 'style')
|
|
249
|
+
: undefined;
|
|
250
|
+
if (request.style && !style) {
|
|
251
|
+
return c.json(apiError('style_not_available', 'That style is not installed for this image model.'), 409);
|
|
252
|
+
}
|
|
305
253
|
const item = await ctx.catalog.get('image-model', modelId).catch(() => null);
|
|
306
254
|
const manifest = item?.manifest.kind === 'image-model' ? item.manifest : undefined;
|
|
307
255
|
const { width, height } = resolveImageSize({
|
|
@@ -338,6 +286,7 @@ export function v1ImagesRoutes(ctx) {
|
|
|
338
286
|
prompt: request.prompt,
|
|
339
287
|
...(request.negative_prompt ? { negativePrompt: request.negative_prompt } : {}),
|
|
340
288
|
model: modelId,
|
|
289
|
+
...(style ? { loras: [{ id: style.id }] } : {}),
|
|
341
290
|
width,
|
|
342
291
|
height,
|
|
343
292
|
...(request.seed !== undefined ? { seed: request.seed } : {}),
|
|
@@ -2,7 +2,7 @@ import { randomBytes, randomUUID, timingSafeEqual } from 'node:crypto';
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { GEZEL_API_GENERATION, GEZEL_API_GENERATION_FLOOR, GEZEL_VERSION, GezelConfigSchema, createLogger, getLogOutput, nowIso, setLogOutput, startSuspendMonitor, stopSuspendMonitor, } from '@bendyline/gezel';
|
|
4
4
|
import { BundledSource, CatalogService } from '@bendyline/gezel-catalog';
|
|
5
|
-
import { discoverNativeBinaries } from '@bendyline/gezel/native';
|
|
5
|
+
import { disableOnnxRuntimeTelemetry, discoverNativeBinaries } from '@bendyline/gezel/native';
|
|
6
6
|
import { gezelHome } from '@bendyline/gezel/paths';
|
|
7
7
|
import { Hono } from 'hono';
|
|
8
8
|
import { bodyLimit } from 'hono/body-limit';
|
|
@@ -37,6 +37,7 @@ import './http/auth-context.js';
|
|
|
37
37
|
const log = createLogger('inference');
|
|
38
38
|
/** Direct Fetch host: no listener, product store, background downloads or daemon discovery. */
|
|
39
39
|
export async function startService(options = {}) {
|
|
40
|
+
disableOnnxRuntimeTelemetry();
|
|
40
41
|
const previousLogOutput = getLogOutput();
|
|
41
42
|
const restoreLogOutput = () => setLogOutput(previousLogOutput);
|
|
42
43
|
if (options.logOutput)
|