arcane-os 0.60.0 → 0.61.0
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/CHANGELOG.md +23 -0
- package/README.md +1 -1
- package/browser-runtime/ai/core-image.mjs +360 -0
- package/docs/reference/local-image-generation.md +267 -0
- package/docs/reference/model-assets.md +10 -7
- package/docs/reference/sdk-api.md +32 -0
- package/package.json +6 -1
- package/schemas/arcane-app.schema.json +2 -2
- package/schemas/arcane-package.schema.json +2 -2
- package/src/core/services/image.mjs +563 -0
- package/src/core/services/local-ai.mjs +8 -3
- package/src/import-map.mjs +2 -0
- package/src/local-ai/config.mjs +41 -1
- package/src/local-ai/dev.mjs +46 -9
- package/src/local-ai/image-library.mjs +985 -0
- package/src/local-ai/image-models/sd14.mjs +15 -0
- package/src/local-ai/image-png.mjs +74 -0
- package/src/local-ai/image-runtime.mjs +336 -0
- package/src/local-ai/image-worker.mjs +218 -0
- package/src/local-ai/install.mjs +326 -10
- package/src/native/portable-layout.mjs +28 -2
|
@@ -121,7 +121,7 @@ function collectCompletion(completion, chunk) {
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
/** Core owns native processes; applications own runtime and model selection. */
|
|
124
|
-
export function createLocalAIService(configuration, {appRoot, runtimes = [], signal, onEvent} = {}) {
|
|
124
|
+
export function createLocalAIService(configuration, {appRoot, runtimes = [], signal, onEvent, prepare} = {}) {
|
|
125
125
|
const config = normalizeLocalAIConfig(configuration) ?? {runtimes: []};
|
|
126
126
|
const lifetime = new AbortController();
|
|
127
127
|
const lifetimeSignal = signal ? AbortSignal.any([signal, lifetime.signal]) : lifetime.signal;
|
|
@@ -136,7 +136,7 @@ export function createLocalAIService(configuration, {appRoot, runtimes = [], sig
|
|
|
136
136
|
let onnxRecoveryError;
|
|
137
137
|
let stopONNXSubscription;
|
|
138
138
|
const onnxSelected = config.runtimes.some(function selectedONNX(requirement) { return requirement.id === 'onnx'; });
|
|
139
|
-
|
|
139
|
+
let onnxRecord = runtimes.find(function installedONNX(runtime) { return runtime.id === 'onnx'; });
|
|
140
140
|
|
|
141
141
|
function onnxStatus() {
|
|
142
142
|
const current = onnx?.current();
|
|
@@ -493,8 +493,13 @@ export function createLocalAIService(configuration, {appRoot, runtimes = [], sig
|
|
|
493
493
|
const service = {
|
|
494
494
|
name: 'local-ai',
|
|
495
495
|
current: snapshot,
|
|
496
|
-
start(currentContext) {
|
|
496
|
+
async start(currentContext) {
|
|
497
497
|
context = currentContext;
|
|
498
|
+
if (prepare) {
|
|
499
|
+
runtimes = await prepare({signal: lifetimeSignal, onEvent});
|
|
500
|
+
lifetimeSignal.throwIfAborted();
|
|
501
|
+
onnxRecord = runtimes.find(function installedONNX(runtime) { return runtime.id === 'onnx'; });
|
|
502
|
+
}
|
|
498
503
|
for (const requirement of config.runtimes) {
|
|
499
504
|
if (!['llama.cpp', 'ollama', 'onnx'].includes(requirement.id)) {
|
|
500
505
|
throw failure('LOCAL_AI_RUNTIME_UNAVAILABLE', `Core runtime ${requirement.id} is not available in this service.`);
|
package/src/import-map.mjs
CHANGED
|
@@ -51,6 +51,8 @@ const SDK_BROWSER_SELF_IMPORTS=new Map([
|
|
|
51
51
|
['arcane-os/ai/browser-wasm',SDK_BROWSER_AI_ENTRY],
|
|
52
52
|
['arcane-os/ai/core-local','sdk/ai/core-local.mjs'],
|
|
53
53
|
['arcane-os/ai/core-onnx','sdk/ai/core-onnx.mjs'],
|
|
54
|
+
['arcane-os/ai/core-image','sdk/ai/core-image.mjs'],
|
|
55
|
+
['arcane-os/ai/core-model-assets','sdk/ai/core-model-assets.mjs'],
|
|
54
56
|
['arcane-os/ai/browser-decisions','sdk/ai/browser-decisions.mjs'],
|
|
55
57
|
['arcane-os/ai/tool-text-stream','sdk/ai/tool-text-stream.mjs'],
|
|
56
58
|
['arcane-os/ai/twin-cloud','sdk/ai/twin-cloud.mjs'],
|
package/src/local-ai/config.mjs
CHANGED
|
@@ -2,9 +2,48 @@ import {readFile, writeFile} from 'node:fs/promises';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import Is from 'strong-type';
|
|
4
4
|
import {ArcaneError, ERROR_CODES, throwIfAborted} from '../errors.mjs';
|
|
5
|
+
import {SD14_MODEL} from './image-models/sd14.mjs';
|
|
5
6
|
|
|
6
7
|
const is = new Is(false);
|
|
7
|
-
export const LOCAL_AI_RUNTIME_IDS = ['llama.cpp', 'ollama', 'onnx'];
|
|
8
|
+
export const LOCAL_AI_RUNTIME_IDS = ['llama.cpp', 'ollama', 'onnx', 'stable-diffusion.cpp'];
|
|
9
|
+
|
|
10
|
+
/** Image requirements describe assets without acquiring or loading a model. */
|
|
11
|
+
export function normalizeImageRuntimeRequirement(record) {
|
|
12
|
+
const backend = record.backend ?? 'auto';
|
|
13
|
+
if (!['auto', 'metal', 'cpu'].includes(backend)) {
|
|
14
|
+
throw new ArcaneError(ERROR_CODES.usage, `Unknown stable-diffusion.cpp backend: ${String(backend)}.`);
|
|
15
|
+
}
|
|
16
|
+
if (record.models !== undefined && !is.array(record.models)) {
|
|
17
|
+
throw new ArcaneError(ERROR_CODES.usage, 'stable-diffusion.cpp models must be an array of model IDs or descriptors.');
|
|
18
|
+
}
|
|
19
|
+
const models = (record.models ?? []).map(
|
|
20
|
+
function selectedImageModel(value) {
|
|
21
|
+
const model = value === 'sd14' ? SD14_MODEL : value;
|
|
22
|
+
if (!model || !is.object(model) || is.array(model) || !is.string(model.id) || !model.id) {
|
|
23
|
+
throw new ArcaneError(ERROR_CODES.usage, 'An image model needs a descriptor with an id, or the built-in sd14 ID.');
|
|
24
|
+
}
|
|
25
|
+
if (!model.resources || !is.object(model.resources) || is.array(model.resources)) {
|
|
26
|
+
throw new ArcaneError(ERROR_CODES.usage, `Image model ${model.id} needs resources keyed by model role.`);
|
|
27
|
+
}
|
|
28
|
+
if (model.operations !== undefined && !is.array(model.operations)) {
|
|
29
|
+
throw new ArcaneError(ERROR_CODES.usage, `Image model ${model.id} operations must be an array.`);
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
...model,
|
|
33
|
+
resources: {...model.resources},
|
|
34
|
+
context: {...model.context},
|
|
35
|
+
defaults: {...model.defaults},
|
|
36
|
+
operations: [...(model.operations ?? [])]
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
return {
|
|
41
|
+
...record,
|
|
42
|
+
version: record.version ?? 'master-929-3f8527a',
|
|
43
|
+
backend,
|
|
44
|
+
models
|
|
45
|
+
};
|
|
46
|
+
}
|
|
8
47
|
|
|
9
48
|
/** Runtime requirements are application configuration, separate from models. */
|
|
10
49
|
export function normalizeLocalAIConfig(value) {
|
|
@@ -17,6 +56,7 @@ export function normalizeLocalAIConfig(value) {
|
|
|
17
56
|
if (!record || !LOCAL_AI_RUNTIME_IDS.includes(record.id)) {
|
|
18
57
|
throw new ArcaneError(ERROR_CODES.usage, `Unknown local AI runtime: ${String(record?.id ?? item)}.`);
|
|
19
58
|
}
|
|
59
|
+
if (record.id === 'stable-diffusion.cpp') return normalizeImageRuntimeRequirement(record);
|
|
20
60
|
return {...record};
|
|
21
61
|
});
|
|
22
62
|
return {...value, runtimes};
|
package/src/local-ai/dev.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import Is from 'strong-type';
|
|
2
2
|
import {createCoreRuntime} from '../core/runtime.mjs';
|
|
3
3
|
import {createLocalAIService} from '../core/services/local-ai.mjs';
|
|
4
|
+
import {createLocalImageService} from '../core/services/image.mjs';
|
|
5
|
+
import {createModelAssetService} from '../core/services/model-assets.mjs';
|
|
4
6
|
import {CORE_PROTOCOL, CoreError, serializeCoreError} from '../../browser-runtime/core/contracts.mjs';
|
|
5
7
|
import {normalizeLocalAIConfig} from './config.mjs';
|
|
6
8
|
import {discoverLocalAIRuntimes} from './discover.mjs';
|
|
@@ -81,6 +83,8 @@ export function createDevelopmentLocalAI({config, appRoot, directory, applicatio
|
|
|
81
83
|
const incoming = new Set();
|
|
82
84
|
let runtime;
|
|
83
85
|
let service;
|
|
86
|
+
let imageService;
|
|
87
|
+
let modelAssets;
|
|
84
88
|
let stopFrames;
|
|
85
89
|
let preparationError = null;
|
|
86
90
|
let closing = null;
|
|
@@ -157,17 +161,49 @@ export function createDevelopmentLocalAI({config, appRoot, directory, applicatio
|
|
|
157
161
|
writeFrame(connection, event('core.state', current));
|
|
158
162
|
for (const record of current.services) writeFrame(connection, event('core.service.state', record));
|
|
159
163
|
writeFrame(connection, event('localai.state', service.current()));
|
|
164
|
+
if (imageService) writeFrame(connection, event('image.state', imageService.current()));
|
|
165
|
+
if (modelAssets) writeFrame(connection, event('modelAssets.state', modelAssets.current()));
|
|
160
166
|
}
|
|
161
167
|
|
|
162
|
-
const ready = Promise.resolve().then(
|
|
168
|
+
const ready = Promise.resolve().then(function prepareLocalAI() {
|
|
163
169
|
operationSignal.throwIfAborted();
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
170
|
+
const localConfig = {
|
|
171
|
+
...selected,
|
|
172
|
+
runtimes: selected.runtimes.filter(function selectedChatRuntime(requirement) {
|
|
173
|
+
return requirement.id !== 'stable-diffusion.cpp';
|
|
174
|
+
})
|
|
175
|
+
};
|
|
176
|
+
service = createLocalAIService(
|
|
177
|
+
localConfig,
|
|
178
|
+
{
|
|
179
|
+
appRoot, signal: operationSignal, onEvent,
|
|
180
|
+
async prepare({signal, onEvent: report}) {
|
|
181
|
+
const discovered = await discoverLocalAIRuntimes({config: localConfig, appRoot, signal});
|
|
182
|
+
const installed = await ensureLocalAIRuntimes({runtimes: discovered.missing, directory, signal, onEvent: report});
|
|
183
|
+
return [...discovered.available, ...installed];
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
);
|
|
187
|
+
const services = [service];
|
|
188
|
+
if (selected.runtimes.some(function selectedImageRuntime(requirement) {
|
|
189
|
+
return requirement.id === 'stable-diffusion.cpp';
|
|
190
|
+
})) {
|
|
191
|
+
modelAssets = createModelAssetService({appRoot});
|
|
192
|
+
imageService = createLocalImageService(
|
|
193
|
+
selected,
|
|
194
|
+
{
|
|
195
|
+
appRoot, modelAssets, signal: operationSignal, onEvent,
|
|
196
|
+
async prepare({requirement, signal, onEvent: report}) {
|
|
197
|
+
const installed = await ensureLocalAIRuntimes(
|
|
198
|
+
{runtimes: [requirement], directory, signal, onEvent: report}
|
|
199
|
+
);
|
|
200
|
+
return installed[0];
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
);
|
|
204
|
+
services.push(modelAssets, imageService);
|
|
205
|
+
}
|
|
206
|
+
runtime = createCoreRuntime({application, version, services});
|
|
171
207
|
stopFrames = runtime.onFrame(routeFrame);
|
|
172
208
|
runtime.start();
|
|
173
209
|
return runtime;
|
|
@@ -350,7 +386,8 @@ export function createDevelopmentLocalAI({config, appRoot, directory, applicatio
|
|
|
350
386
|
function current() {
|
|
351
387
|
return {state: closed ? 'closed' : preparationError ? 'error' : runtime ? 'ready' : 'preparing',
|
|
352
388
|
error: preparationError ? serializeCoreError(preparationError) : null,
|
|
353
|
-
core: runtime?.current() ?? null, localAI: service?.current() ?? null
|
|
389
|
+
core: runtime?.current() ?? null, localAI: service?.current() ?? null,
|
|
390
|
+
image: imageService?.current() ?? null};
|
|
354
391
|
}
|
|
355
392
|
|
|
356
393
|
function close() {
|