arcane-os 0.79.1 → 0.81.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 +43 -0
- package/README.md +1 -1
- package/browser-runtime/ai/browser-wasm-llm-provider.mjs +97 -19
- package/browser-runtime/ai/dbopfs-model-parts.mjs +12 -5
- package/browser-runtime/ai/dbopfs-model-resources.mjs +2 -5
- package/docs/reference/ai/browser-speech.md +11 -0
- package/docs/reference/ai/browser-wasm.md +21 -10
- package/docs/reference/core-native-packaging.md +9 -0
- package/docs/reference/inventory/package-api.json +8 -8
- package/docs/reference/model-assets.md +33 -0
- package/docs/reference/native-decisions.md +147 -17
- package/docs/reference/sdk-api.md +22 -6
- package/package.json +1 -1
- package/runtime/arcane/modules/AI.js +10 -5
- package/src/core/hosts/windows/ArcaneHost.cs +21 -5
- package/src/core/services/decisions.mjs +116 -43
- package/src/local-ai/decision-tokenizer-worker.mjs +1 -1
- package/src/local-ai/decision-tokenizer.mjs +2 -2
- package/src/local-ai/decisions.mjs +123 -33
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,48 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.81.0
|
|
4
|
+
|
|
5
|
+
- Add per-activation native decision `family`, `model`, `revision`, and `dtype`
|
|
6
|
+
selection through the direct runtime and Core service. Support Laya FP16 and
|
|
7
|
+
Julia 1 FP32 with their own model companions and tokenizer family, preserving
|
|
8
|
+
Laya FP32 defaults, complete input rows and actual output tensors. Keep
|
|
9
|
+
requested replacement metadata separate from the active model and retain
|
|
10
|
+
prepared files through actual worker exit.
|
|
11
|
+
- Report cached model loading as real stored-part progress and preserve known
|
|
12
|
+
GGUF shard totals. Forward complete progress details and cancellation through
|
|
13
|
+
cache reads without presenting cached data as a download or inventing totals
|
|
14
|
+
for an unfinished network stream.
|
|
15
|
+
- Add object-form `AI.streamRequest({speech:false})` for text-only operations.
|
|
16
|
+
Completion, failure and cancellation leave unrelated prepared, native and
|
|
17
|
+
buffered speech alone. Existing defaults, positional methods and explicit
|
|
18
|
+
speech APIs retain their behavior.
|
|
19
|
+
- Close and drain the Windows host after WebView2 `BrowserProcessExited`,
|
|
20
|
+
preserving its full diagnostic instead of leaving a blank window awaiting
|
|
21
|
+
manual closure. Existing renderer/Core-only handling and saved profiles stay
|
|
22
|
+
unchanged. This source correction does not establish the cause of the browser
|
|
23
|
+
exit; executable consumers require the matching rebuilt host archive.
|
|
24
|
+
- Keep dependencies, app-control, native GGUF activation and native Kokoro
|
|
25
|
+
routing unchanged. Source/package verification is separate from actual model,
|
|
26
|
+
audio or application execution.
|
|
27
|
+
|
|
28
|
+
## 0.80.0
|
|
29
|
+
|
|
30
|
+
- Add `assetProjectionId` and `resourcePaths` to native decision factories and
|
|
31
|
+
direct, service and Core `decisions.load` calls. A supplied ready projection
|
|
32
|
+
uses complete already-prepared model/tokenizer files through the existing
|
|
33
|
+
model-assets owner, without upstream preparation or another download.
|
|
34
|
+
- Retain projected files through actual native ONNX and tokenizer exit, including
|
|
35
|
+
failure and cancellation. Acquire incoming ownership before retiring a prior
|
|
36
|
+
activation; coalesce only equal source, complete mapping and device selections.
|
|
37
|
+
Surface provisional cleanup failures through load, unload and disposal.
|
|
38
|
+
- Preserve caller preparation ownership, original companion-file paths, complete
|
|
39
|
+
rows and outputs, configured native paths and ordinary explicit upstream
|
|
40
|
+
loading. Explicit `assetProjectionId:null` returns to that configured source.
|
|
41
|
+
Missing projections or mapped members report their actual errors.
|
|
42
|
+
- Keep native Laya FP32 selection and existing runtime dependencies unchanged.
|
|
43
|
+
This release adds no model variant, GGUF route, driver, model download on
|
|
44
|
+
startup or automatic preference change.
|
|
45
|
+
|
|
3
46
|
## 0.79.1
|
|
4
47
|
|
|
5
48
|
- Classify generic speech-model progress callbacks as loading, reserving
|
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ selected SDK projection or an explicit live SDK source mount. Both profiles
|
|
|
20
20
|
share the theme, packaging, event, cancellation,
|
|
21
21
|
and browser run contracts while retaining their selected application layout.
|
|
22
22
|
|
|
23
|
-
This checkout defines the `0.
|
|
23
|
+
This checkout defines the `0.81.0` SDK contract. Development applications declare
|
|
24
24
|
`arcane-os` as `latest` and retain their lockfile. Exact SDK pins begin when Roshi
|
|
25
25
|
designates a production build. Follow [Consumer updates](docs/consumer-updates.md)
|
|
26
26
|
to refresh and compare the installed SDK and lockfile with the published channel;
|
|
@@ -667,19 +667,21 @@ export function createDbopfsModelStore({
|
|
|
667
667
|
}
|
|
668
668
|
}
|
|
669
669
|
|
|
670
|
-
function
|
|
670
|
+
function createModelProgressReporter(members, onProgress, retainFailure) {
|
|
671
671
|
const shards = members.map(function initialMemberProgress(member) {
|
|
672
672
|
return {name: member.name, completed: 0, total: null, unit: 'shards'};
|
|
673
673
|
});
|
|
674
674
|
let completed = 0;
|
|
675
|
+
let phase = "cache-check";
|
|
675
676
|
let activeTransfers = 0;
|
|
676
677
|
let transferMode = members.length === 1 ? "probing" : "files";
|
|
677
678
|
let timer = null;
|
|
678
679
|
let lastPublishedAt = 0;
|
|
680
|
+
let progressFailure = null;
|
|
679
681
|
|
|
680
682
|
function progressRecord() {
|
|
681
683
|
return completeValue({
|
|
682
|
-
phase
|
|
684
|
+
phase,
|
|
683
685
|
completed,
|
|
684
686
|
total: members.length,
|
|
685
687
|
unit: "files",
|
|
@@ -713,18 +715,28 @@ export function createDbopfsModelStore({
|
|
|
713
715
|
return true;
|
|
714
716
|
} catch (error) {
|
|
715
717
|
stopTimer();
|
|
718
|
+
progressFailure ??= error;
|
|
716
719
|
retainFailure(error);
|
|
717
720
|
return false;
|
|
718
721
|
}
|
|
719
722
|
}
|
|
720
723
|
|
|
724
|
+
function assertProgress() {
|
|
725
|
+
if (progressFailure !== null) throw progressFailure;
|
|
726
|
+
}
|
|
727
|
+
|
|
721
728
|
return completeValue({
|
|
722
729
|
reportShards(memberIndex, value) {
|
|
730
|
+
assertProgress();
|
|
723
731
|
shards[memberIndex] = {...value, name: members[memberIndex].name};
|
|
732
|
+
if (value.phase === "download" || activeTransfers > 0) phase = "download";
|
|
733
|
+
else if (value.phase === "load") phase = "load";
|
|
724
734
|
publishSafely({force: true});
|
|
735
|
+
if (value.phase === "load") assertProgress();
|
|
725
736
|
},
|
|
726
737
|
beginTransfer() {
|
|
727
738
|
activeTransfers += 1;
|
|
739
|
+
phase = "download";
|
|
728
740
|
publishSafely({ force: true });
|
|
729
741
|
},
|
|
730
742
|
completeMember() {
|
|
@@ -745,14 +757,17 @@ export function createDbopfsModelStore({
|
|
|
745
757
|
publishSafely({ force: true });
|
|
746
758
|
},
|
|
747
759
|
restoreMember(memberIndex) {
|
|
748
|
-
|
|
760
|
+
assertProgress();
|
|
761
|
+
shards[memberIndex] = {...shards[memberIndex], phase: 'load', cached: true, state: 'cached'};
|
|
762
|
+
if (activeTransfers === 0) phase = "load";
|
|
749
763
|
completed += 1;
|
|
750
764
|
publishSafely({ force: true });
|
|
765
|
+
assertProgress();
|
|
751
766
|
},
|
|
752
767
|
start() {
|
|
753
768
|
if (!publishSafely({ force: true })) return;
|
|
754
769
|
if (onProgress !== null && timer === null) {
|
|
755
|
-
timer = setInterval(function
|
|
770
|
+
timer = setInterval(function publishModelProgressTick() {
|
|
756
771
|
publishSafely();
|
|
757
772
|
}, MODEL_DOWNLOAD_PROGRESS_INTERVAL_MS);
|
|
758
773
|
}
|
|
@@ -795,6 +810,9 @@ export function createDbopfsModelStore({
|
|
|
795
810
|
const partFiles = new Array(ranges.length);
|
|
796
811
|
const pendingRangeIndexes = [];
|
|
797
812
|
let completedParts = 0;
|
|
813
|
+
progress?.reportShards(memberIndex, {
|
|
814
|
+
phase: 'load', completed: 0, total: ranges.length, unit: 'shards', cached: true,
|
|
815
|
+
});
|
|
798
816
|
for (let rangeIndex = 0; rangeIndex < ranges.length; rangeIndex += 1) {
|
|
799
817
|
throwIfAborted(signal, "install");
|
|
800
818
|
const range = ranges[rangeIndex];
|
|
@@ -804,15 +822,20 @@ export function createDbopfsModelStore({
|
|
|
804
822
|
if (partFile?.size === expected) {
|
|
805
823
|
partFiles[rangeIndex] = partFile;
|
|
806
824
|
completedParts += 1;
|
|
825
|
+
progress?.reportShards(memberIndex, {
|
|
826
|
+
phase: 'load', completed: completedParts, total: ranges.length, unit: 'shards', cached: true,
|
|
827
|
+
});
|
|
807
828
|
} else {
|
|
808
829
|
if (partFile) await removeEntry(partName);
|
|
809
830
|
pendingRangeIndexes.push(rangeIndex);
|
|
810
831
|
}
|
|
811
832
|
}
|
|
812
833
|
progress?.reportShards(memberIndex, {
|
|
834
|
+
phase: 'load',
|
|
813
835
|
completed: completedParts,
|
|
814
836
|
total: ranges.length,
|
|
815
|
-
unit: 'shards'
|
|
837
|
+
unit: 'shards',
|
|
838
|
+
cached: true,
|
|
816
839
|
});
|
|
817
840
|
const linked = linkAbortSignal(signal);
|
|
818
841
|
const downloadSignal = linked.controller.signal;
|
|
@@ -868,6 +891,7 @@ export function createDbopfsModelStore({
|
|
|
868
891
|
throwIfAborted(downloadSignal, "install");
|
|
869
892
|
completedParts += 1;
|
|
870
893
|
progress?.reportShards(memberIndex, {
|
|
894
|
+
phase: 'download',
|
|
871
895
|
completed: completedParts,
|
|
872
896
|
total: ranges.length,
|
|
873
897
|
unit: 'shards'
|
|
@@ -1120,13 +1144,27 @@ export function createDbopfsModelStore({
|
|
|
1120
1144
|
});
|
|
1121
1145
|
}
|
|
1122
1146
|
|
|
1123
|
-
async function availableModelFiles(names, signal) {
|
|
1147
|
+
async function availableModelFiles(names, signal, progress) {
|
|
1124
1148
|
const modelFiles = [];
|
|
1125
|
-
for (
|
|
1149
|
+
for (let memberIndex = 0; memberIndex < names.models.length; memberIndex += 1) {
|
|
1150
|
+
const entry = names.models[memberIndex];
|
|
1126
1151
|
throwIfAborted(signal, "install");
|
|
1127
1152
|
// Existing whole-file caches remain readable; new downloads use parts.
|
|
1128
|
-
|
|
1153
|
+
let modelFile = await parts.read(entry.name, {
|
|
1154
|
+
signal,
|
|
1155
|
+
onProgress: function reportStoredShards(value) {
|
|
1156
|
+
progress?.reportShards(memberIndex, value);
|
|
1157
|
+
},
|
|
1158
|
+
});
|
|
1159
|
+
if (!modelFile) {
|
|
1160
|
+
modelFile = await file(entry.name);
|
|
1161
|
+
if (modelFile) progress?.reportShards(memberIndex, {
|
|
1162
|
+
phase: 'load', completed: 1, total: 1, unit: 'shards', cached: true,
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1129
1165
|
modelFiles.push(modelFile);
|
|
1166
|
+
if (modelFile) progress?.restoreMember(memberIndex);
|
|
1167
|
+
throwIfAborted(signal, "install");
|
|
1130
1168
|
}
|
|
1131
1169
|
return modelFiles;
|
|
1132
1170
|
}
|
|
@@ -1136,9 +1174,16 @@ export function createDbopfsModelStore({
|
|
|
1136
1174
|
ranges,
|
|
1137
1175
|
signal,
|
|
1138
1176
|
availableNames = null,
|
|
1177
|
+
onProgress = null,
|
|
1139
1178
|
) {
|
|
1140
1179
|
const partFiles = [];
|
|
1141
1180
|
let lastModified = 0;
|
|
1181
|
+
function reportStoredRange() {
|
|
1182
|
+
throwIfAborted(signal, "install");
|
|
1183
|
+
onProgress?.({phase: 'load', completed: partFiles.length,
|
|
1184
|
+
total: ranges.length, unit: 'shards', cached: true});
|
|
1185
|
+
}
|
|
1186
|
+
reportStoredRange();
|
|
1142
1187
|
for (const range of ranges) {
|
|
1143
1188
|
throwIfAborted(signal, "install");
|
|
1144
1189
|
const partName = rangePartName(modelName, range);
|
|
@@ -1151,10 +1196,12 @@ export function createDbopfsModelStore({
|
|
|
1151
1196
|
return null;
|
|
1152
1197
|
}
|
|
1153
1198
|
partFiles.push(partFile);
|
|
1199
|
+
reportStoredRange();
|
|
1154
1200
|
if (is.finite(partFile.lastModified)) {
|
|
1155
1201
|
lastModified = Math.max(lastModified, partFile.lastModified);
|
|
1156
1202
|
}
|
|
1157
1203
|
}
|
|
1204
|
+
throwIfAborted(signal, "install");
|
|
1158
1205
|
return completeValue({
|
|
1159
1206
|
file: new Blob(partFiles, { type: "application/octet-stream" }),
|
|
1160
1207
|
lastModified,
|
|
@@ -1168,6 +1215,7 @@ export function createDbopfsModelStore({
|
|
|
1168
1215
|
total,
|
|
1169
1216
|
signal,
|
|
1170
1217
|
availableNames = null,
|
|
1218
|
+
onProgress = null,
|
|
1171
1219
|
) {
|
|
1172
1220
|
const ranges = rangePlanForInstall(total, signal);
|
|
1173
1221
|
return storedRangeCandidateForPlan(
|
|
@@ -1175,10 +1223,11 @@ export function createDbopfsModelStore({
|
|
|
1175
1223
|
ranges,
|
|
1176
1224
|
signal,
|
|
1177
1225
|
availableNames,
|
|
1226
|
+
onProgress,
|
|
1178
1227
|
);
|
|
1179
1228
|
}
|
|
1180
1229
|
|
|
1181
|
-
async function storedRangeMember(member, modelName, signal) {
|
|
1230
|
+
async function storedRangeMember(member, modelName, signal, onProgress) {
|
|
1182
1231
|
const names = await memberRangePartNames(modelName);
|
|
1183
1232
|
const availableNames = names === null ? null : new Set(names);
|
|
1184
1233
|
const declaredTotal = is.safeInteger(member.bytes) && member.bytes > 0
|
|
@@ -1190,6 +1239,7 @@ export function createDbopfsModelStore({
|
|
|
1190
1239
|
declaredTotal,
|
|
1191
1240
|
signal,
|
|
1192
1241
|
availableNames,
|
|
1242
|
+
onProgress,
|
|
1193
1243
|
);
|
|
1194
1244
|
if (declared) {
|
|
1195
1245
|
await removeStaleRangePartsAfterCompletion(
|
|
@@ -1214,6 +1264,7 @@ export function createDbopfsModelStore({
|
|
|
1214
1264
|
total,
|
|
1215
1265
|
signal,
|
|
1216
1266
|
availableNames,
|
|
1267
|
+
onProgress,
|
|
1217
1268
|
);
|
|
1218
1269
|
if (
|
|
1219
1270
|
candidate
|
|
@@ -1224,16 +1275,20 @@ export function createDbopfsModelStore({
|
|
|
1224
1275
|
) selected = candidate;
|
|
1225
1276
|
}
|
|
1226
1277
|
if (selected === null) return null;
|
|
1278
|
+
throwIfAborted(signal, "install");
|
|
1227
1279
|
await removeStaleRangePartsAfterCompletion(
|
|
1228
1280
|
modelName,
|
|
1229
1281
|
selected.ranges,
|
|
1230
1282
|
);
|
|
1283
|
+
onProgress?.({phase: 'load', completed: selected.ranges.length,
|
|
1284
|
+
total: selected.ranges.length, unit: 'shards', cached: true});
|
|
1285
|
+
throwIfAborted(signal, "install");
|
|
1231
1286
|
return selected.file;
|
|
1232
1287
|
}
|
|
1233
1288
|
|
|
1234
|
-
async function availableResumableModelFiles(source, names, signal) {
|
|
1289
|
+
async function availableResumableModelFiles(source, names, signal, progress) {
|
|
1235
1290
|
const members = sourceMetadata(source).files;
|
|
1236
|
-
const modelFiles = await availableModelFiles(names, signal);
|
|
1291
|
+
const modelFiles = await availableModelFiles(names, signal, progress);
|
|
1237
1292
|
await removeRangePartsBehindWholeFiles(members, names, modelFiles);
|
|
1238
1293
|
for (let memberIndex = 0; memberIndex < modelFiles.length; memberIndex += 1) {
|
|
1239
1294
|
if (modelFiles[memberIndex]) continue;
|
|
@@ -1241,20 +1296,40 @@ export function createDbopfsModelStore({
|
|
|
1241
1296
|
members[memberIndex],
|
|
1242
1297
|
names.models[memberIndex].name,
|
|
1243
1298
|
signal,
|
|
1299
|
+
function reportStoredRangeShards(value) {
|
|
1300
|
+
progress?.reportShards(memberIndex, value);
|
|
1301
|
+
},
|
|
1244
1302
|
);
|
|
1303
|
+
if (modelFiles[memberIndex]) progress?.restoreMember(memberIndex);
|
|
1304
|
+
throwIfAborted(signal, "install");
|
|
1245
1305
|
}
|
|
1246
1306
|
return modelFiles;
|
|
1247
1307
|
}
|
|
1248
1308
|
|
|
1249
1309
|
async function openCached(source, {
|
|
1250
1310
|
signal,
|
|
1311
|
+
onProgress = null,
|
|
1251
1312
|
} = {}) {
|
|
1252
1313
|
const names = storageName(source);
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
files: completeValue(modelFiles),
|
|
1314
|
+
let failure = null;
|
|
1315
|
+
const progress = createModelProgressReporter(sourceMetadata(source).files, onProgress, function retainProgressFailure(error) {
|
|
1316
|
+
failure ??= error;
|
|
1257
1317
|
});
|
|
1318
|
+
try {
|
|
1319
|
+
progress.start();
|
|
1320
|
+
if (failure !== null) throw failure;
|
|
1321
|
+
const modelFiles = await availableResumableModelFiles(source, names, signal, progress);
|
|
1322
|
+
if (failure !== null) throw failure;
|
|
1323
|
+
if (!modelFiles.every(Boolean)) return null;
|
|
1324
|
+
progress.finish();
|
|
1325
|
+
if (failure !== null) throw failure;
|
|
1326
|
+
throwIfAborted(signal, "install");
|
|
1327
|
+
return completeValue({files: completeValue(modelFiles)});
|
|
1328
|
+
} catch (error) {
|
|
1329
|
+
throw failure ?? error;
|
|
1330
|
+
} finally {
|
|
1331
|
+
progress.dispose();
|
|
1332
|
+
}
|
|
1258
1333
|
}
|
|
1259
1334
|
|
|
1260
1335
|
async function install(source, { signal, onProgress = null } = {}) {
|
|
@@ -1263,7 +1338,7 @@ export function createDbopfsModelStore({
|
|
|
1263
1338
|
}
|
|
1264
1339
|
const names = storageName(source);
|
|
1265
1340
|
const members = sourceMetadata(source).files;
|
|
1266
|
-
|
|
1341
|
+
let modelFiles;
|
|
1267
1342
|
const linked = linkAbortSignal(signal);
|
|
1268
1343
|
const downloadSignal = linked.controller.signal;
|
|
1269
1344
|
let nextMemberIndex = 0;
|
|
@@ -1273,7 +1348,7 @@ export function createDbopfsModelStore({
|
|
|
1273
1348
|
if (failure === null) failure = error;
|
|
1274
1349
|
}
|
|
1275
1350
|
|
|
1276
|
-
const progress =
|
|
1351
|
+
const progress = createModelProgressReporter(members, onProgress, retainFailure);
|
|
1277
1352
|
|
|
1278
1353
|
async function installMember(memberIndex) {
|
|
1279
1354
|
let modelFile = null;
|
|
@@ -1338,12 +1413,14 @@ export function createDbopfsModelStore({
|
|
|
1338
1413
|
|
|
1339
1414
|
try {
|
|
1340
1415
|
progress.start();
|
|
1416
|
+
if (failure !== null) throw failure;
|
|
1417
|
+
modelFiles = await availableResumableModelFiles(source, names, downloadSignal, progress);
|
|
1418
|
+
if (failure !== null) throw failure;
|
|
1341
1419
|
let pendingMembers = members.length;
|
|
1342
1420
|
for (let memberIndex = 0; memberIndex < modelFiles.length; memberIndex += 1) {
|
|
1343
1421
|
const modelFile = modelFiles[memberIndex];
|
|
1344
1422
|
if (!modelFile) continue;
|
|
1345
1423
|
pendingMembers -= 1;
|
|
1346
|
-
progress.restoreMember(memberIndex);
|
|
1347
1424
|
}
|
|
1348
1425
|
const workers = [];
|
|
1349
1426
|
const workerCount = Math.min(workerLimit, pendingMembers);
|
|
@@ -1392,10 +1469,11 @@ export function createDbopfsModelStore({
|
|
|
1392
1469
|
unit: "files",
|
|
1393
1470
|
heartbeat: false,
|
|
1394
1471
|
}));
|
|
1395
|
-
const cached = await openCached(source, { signal });
|
|
1472
|
+
const cached = await openCached(source, { signal, onProgress });
|
|
1396
1473
|
if (cached) {
|
|
1397
1474
|
const storage = await storagePolicy({ cached: true });
|
|
1398
1475
|
onCapabilityPolicy?.(storage);
|
|
1476
|
+
throwIfAborted(signal, "install");
|
|
1399
1477
|
return completeValue({ ...cached, cache: "cached", storage });
|
|
1400
1478
|
}
|
|
1401
1479
|
if (offline) {
|
|
@@ -178,13 +178,20 @@ export function createDbopfsModelPartStore({dbopfs, tableName}) {
|
|
|
178
178
|
throw error;
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
|
-
async function assemble(record, signal) {
|
|
181
|
+
async function assemble(record, signal, onProgress) {
|
|
182
182
|
const parts = [];
|
|
183
|
+
function reportStoredPart() {
|
|
184
|
+
throwIfModelAborted(signal);
|
|
185
|
+
onProgress?.({phase: 'load', message: 'Reading stored model resource',
|
|
186
|
+
completed: parts.length, total: record.parts.length, unit: 'shards', cached: true});
|
|
187
|
+
}
|
|
188
|
+
reportStoredPart();
|
|
183
189
|
for (const name of record.parts) {
|
|
184
190
|
throwIfModelAborted(signal);
|
|
185
191
|
const part = await file(name);
|
|
186
192
|
if (!part) return null;
|
|
187
193
|
parts.push(part);
|
|
194
|
+
reportStoredPart();
|
|
188
195
|
}
|
|
189
196
|
throwIfModelAborted(signal);
|
|
190
197
|
return new Blob(parts);
|
|
@@ -209,18 +216,18 @@ export function createDbopfsModelPartStore({dbopfs, tableName}) {
|
|
|
209
216
|
return readState(name);
|
|
210
217
|
}, signal);
|
|
211
218
|
}
|
|
212
|
-
function read(name, {signal} = {}) {
|
|
219
|
+
function read(name, {signal, onProgress} = {}) {
|
|
213
220
|
return locked(name, async function readCompleteModelParts() {
|
|
214
221
|
throwIfModelAborted(signal);
|
|
215
222
|
const record = await readState(name);
|
|
216
|
-
return record?.complete ? assemble(record, signal) : null;
|
|
223
|
+
return record?.complete ? assemble(record, signal, onProgress) : null;
|
|
217
224
|
}, signal);
|
|
218
225
|
}
|
|
219
|
-
function readPartial(name, {signal} = {}) {
|
|
226
|
+
function readPartial(name, {signal, onProgress} = {}) {
|
|
220
227
|
return locked(name, async function readClosedModelParts() {
|
|
221
228
|
throwIfModelAborted(signal);
|
|
222
229
|
const record = await readState(name);
|
|
223
|
-
return record ? assemble(record, signal) : null;
|
|
230
|
+
return record ? assemble(record, signal, onProgress) : null;
|
|
224
231
|
}, signal);
|
|
225
232
|
}
|
|
226
233
|
function remove(name, {signal} = {}) {
|
|
@@ -93,11 +93,8 @@ export function createDbopfsResourceStore({dbopfs, tableName, fetchImpl = global
|
|
|
93
93
|
const reusable = request.method === 'GET' && !request.headers.has('range')
|
|
94
94
|
&& !['reload', 'no-cache', 'no-store'].includes(request.cache);
|
|
95
95
|
if (reusable && entry.metadata?.status === 200) {
|
|
96
|
-
const file = await parts.read(entry.name, {signal});
|
|
96
|
+
const file = await parts.read(entry.name, {signal, onProgress: reportResourceProgress});
|
|
97
97
|
if (file) {
|
|
98
|
-
const state = await parts.state(entry.name, {signal});
|
|
99
|
-
onProgress?.({phase: 'load', message: 'Reading stored model resource', completed: state.parts.length,
|
|
100
|
-
total: state.parts.length, unit: 'shards', cached: true, url: request.url});
|
|
101
98
|
return {...entry.metadata, file};
|
|
102
99
|
}
|
|
103
100
|
}
|
|
@@ -111,7 +108,7 @@ export function createDbopfsResourceStore({dbopfs, tableName, fetchImpl = global
|
|
|
111
108
|
const encoding = previousHeaders.get('content-encoding');
|
|
112
109
|
const partial = reusable && entry.metadata?.status === 200
|
|
113
110
|
&& (!encoding || encoding === 'identity')
|
|
114
|
-
? await parts.readPartial(entry.name, {signal}) : null;
|
|
111
|
+
? await parts.readPartial(entry.name, {signal, onProgress: reportResourceProgress}) : null;
|
|
115
112
|
try {
|
|
116
113
|
if (partial?.size) {
|
|
117
114
|
const headers = new Headers(request.headers);
|
|
@@ -1116,6 +1116,17 @@ prepared playback without invoking `finishTTS()` or `streamTTS()`. Unmuted
|
|
|
1116
1116
|
completion keeps its ordinary speech flush. Explicit calls to either public
|
|
1117
1117
|
speech method retain their existing behavior.
|
|
1118
1118
|
|
|
1119
|
+
For a text-only request, pass `speech:false` to the object-form
|
|
1120
|
+
`ai.streamRequest({messages, speech:false, onChunk, signal})`. Its completion,
|
|
1121
|
+
failure, and cancellation leave buffered speech, prepared playback, and native
|
|
1122
|
+
playback alone. The option controls only the request's automatic speech
|
|
1123
|
+
finalization and stopping; it is not sent to the language provider and does not
|
|
1124
|
+
mute or unload TTS. Explicit speech calls made by your callbacks still run with
|
|
1125
|
+
their ordinary behavior. Omitted `speech` or `speech:true` preserves the
|
|
1126
|
+
automatic lifecycle above, and positional `streamMessage(...)` is unchanged.
|
|
1127
|
+
Explicit `streamTTS()`, `finishTTS()`, and global `stopAudio()` keep their existing
|
|
1128
|
+
scope.
|
|
1129
|
+
|
|
1119
1130
|
## Automatic speech-input formatting cleanup
|
|
1120
1131
|
|
|
1121
1132
|
The shared streaming, audio-file, and `SpeechPlayback` paths remove repeated
|
|
@@ -115,13 +115,17 @@ The controller emits `statechange` and `progress` through
|
|
|
115
115
|
current mutable status snapshot. Provider states are `unloaded`, `loading`, `ready`,
|
|
116
116
|
`unloading`, and `error`.
|
|
117
117
|
|
|
118
|
-
Browser-WASM model loads report `cache-check`, `download`, and `initialize`
|
|
119
|
-
phases.
|
|
118
|
+
Browser-WASM model loads report `cache-check`, `load`, `download`, and `initialize`
|
|
119
|
+
phases. Cache-read and download records preserve the ordered model-file fields `completed`,
|
|
120
120
|
`total`, and `unit:'files'`. Their ordered `members` records contain each file's
|
|
121
121
|
`name`, committed-part `completed`, `total`, and `unit:'shards'`. A streaming
|
|
122
122
|
response has `total:null` until its end is observed; completion then supplies
|
|
123
|
-
the actual committed-part count.
|
|
124
|
-
`
|
|
123
|
+
the actual committed-part count. Reading saved parts reports `phase:'load'`,
|
|
124
|
+
`cached:true`, and the known saved-part total before the first read and after
|
|
125
|
+
each part is opened. An existing HTTP Range plan likewise supplies its actual
|
|
126
|
+
shard total. Previously complete members identify `state:'cached'`; their
|
|
127
|
+
per-member records remain cache reads even if another member is downloading.
|
|
128
|
+
A fully cached activation emits no download phase. `activeTransfers` is the current transfer-worker count,
|
|
125
129
|
`transferLimit` is the selected concurrency, and `transferMode` identifies
|
|
126
130
|
`probing`, `files`, `ranges`, or `single`. No byte progress, rate, or estimated
|
|
127
131
|
completion time is produced. Closed-part and lifecycle changes publish promptly;
|
|
@@ -136,7 +140,7 @@ These messages do not change the runtime's log level or replace its complete
|
|
|
136
140
|
diagnostic output.
|
|
137
141
|
|
|
138
142
|
Initialization has `total:null` and omits `completed` and `unit`, so its progress
|
|
139
|
-
bar is indeterminate.
|
|
143
|
+
bar is indeterminate. Cached or downloaded file completion is not activation completion.
|
|
140
144
|
The metadata tensor count and GPU layer assignment are descriptive only:
|
|
141
145
|
upstream reports assigned layers before model-weight loading has finished.
|
|
142
146
|
The pinned runtime does not expose a measured overall initialization fraction
|
|
@@ -658,10 +662,13 @@ contains `kind`, `tableName`, `downloadConcurrency`, the original `adapter`,
|
|
|
658
662
|
and `ready`, `install`, `ensure`, `remove`, and `fetchResource`. `ensure()` preserves the complete
|
|
659
663
|
ordered model set and reports whether it was cached or installed.
|
|
660
664
|
`install(source,{signal,onProgress})` and
|
|
661
|
-
`ensure(source,{signal,onProgress,offline})` publish `cache-check
|
|
662
|
-
`download` records using ordered file counts and per-member closed-part progress
|
|
665
|
+
`ensure(source,{signal,onProgress,offline})` publish `cache-check`, cache-reading
|
|
666
|
+
`load`, and actual network `download` records using ordered file counts and per-member closed-part progress
|
|
663
667
|
when `onProgress` is supplied. Each member reports `completed`, `total`, and
|
|
664
|
-
`unit:'shards'`; streaming totals remain `null` until completion.
|
|
668
|
+
`unit:'shards'`; streaming totals remain `null` until completion. Cache reads
|
|
669
|
+
publish the known stored-part plan from zero through each opened part, with
|
|
670
|
+
`cached:true`; the existing Range plan supplies known totals during resume.
|
|
671
|
+
No model length is converted into product progress. Records retain
|
|
665
672
|
`activeTransfers`, `transferLimit`, and `transferMode`, with prompt part/lifecycle
|
|
666
673
|
updates and an existing 250 ms heartbeat. They contain no transfer rate or ETA.
|
|
667
674
|
Multi-file sources use up to that many concurrent member workers
|
|
@@ -693,8 +700,12 @@ loaders share this owner for actual configuration, tokenizer, graph,
|
|
|
693
700
|
external-data and support-file requests. Complete successful GET resources may
|
|
694
701
|
be reused. Cancellation joins reader/writer cleanup and preserves closed parts
|
|
695
702
|
for supported HTTP resume; a full response restarts that resource without
|
|
696
|
-
duplicating its saved prefix. Resource progress uses `unit:'shards'
|
|
697
|
-
`total:null` until completion
|
|
703
|
+
duplicating its saved prefix. Resource progress uses `unit:'shards'`. A network
|
|
704
|
+
response without a known shard plan retains `total:null` until completion;
|
|
705
|
+
opening a saved complete or partial resource reports `phase:'load'`,
|
|
706
|
+
`cached:true`, and the known number of stored parts before and during the read.
|
|
707
|
+
The resource URL accompanies every update. These counts describe resource
|
|
708
|
+
preparation, never engine activation. Independent resource requests remain concurrent.
|
|
698
709
|
|
|
699
710
|
### Availability and normalization
|
|
700
711
|
|
|
@@ -592,6 +592,15 @@ before the window closes; complete engineering errors go to the supplied
|
|
|
592
592
|
diagnostic/error callbacks. Callbacks must not synchronously wait for the
|
|
593
593
|
window's lifetime promises.
|
|
594
594
|
|
|
595
|
+
A WebView2 `BrowserProcessExited` failure retains its complete diagnostic,
|
|
596
|
+
fails a pending `Ready`, and begins the existing Core drain and window close.
|
|
597
|
+
It does not wait for a user to close a blank window or invoke document callbacks
|
|
598
|
+
and script cleanup in the closed browser. The launcher reports the failure
|
|
599
|
+
after that drain. Main-renderer exits and Core-only transport failures retain
|
|
600
|
+
their existing handling. This terminal-flow correction does not identify or
|
|
601
|
+
prevent the underlying browser-process exit, restart the application, or change
|
|
602
|
+
its profile and saved data.
|
|
603
|
+
|
|
595
604
|
`ArcaneLauncher.cs` supplies the executable entry point for that composition.
|
|
596
605
|
`tools/build-core-windows-host.mjs` builds the selected SDK host output from the
|
|
597
606
|
approved WebView2 package, existing .NET Framework compiler and selected Node
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"sdkVersion": "0.
|
|
3
|
+
"sdkVersion": "0.80.0",
|
|
4
4
|
"environment": {
|
|
5
5
|
"runtime": "Node.js for Node entrypoints; browser for browser-only entrypoints",
|
|
6
6
|
"minimumVersion": "22.23.2 for Node entrypoints",
|
|
@@ -45,14 +45,14 @@
|
|
|
45
45
|
"name": "createNativeDecisionModel",
|
|
46
46
|
"displayName": "createNativeDecisionModel()",
|
|
47
47
|
"kind": "function",
|
|
48
|
-
"signature": "createNativeDecisionModel({onnx,modelAssets,workingDirectory,paths,model='onnx-community/laya-typed-decisions-ONNX',revision='main',dtype='fp32',sessionOptions,executionPreference='gpu',executionTarget,signal}={})",
|
|
48
|
+
"signature": "createNativeDecisionModel({onnx,modelAssets,workingDirectory,paths,assetProjectionId,resourcePaths,family='laya',model='onnx-community/laya-typed-decisions-ONNX',revision='main',dtype='fp32',sessionOptions,executionPreference='gpu',executionTarget,signal}={})",
|
|
49
49
|
"entrypoints": ["arcane-os/local-ai/decisions"],
|
|
50
50
|
"primaryImport": "arcane-os/local-ai/decisions",
|
|
51
51
|
"group": "Native typed decisions",
|
|
52
|
-
"summary": "Explicit native Laya FP32 option scoring through an existing ONNX owner and a tokenizer Worker.",
|
|
52
|
+
"summary": "Explicit native Laya FP32/FP16 or Julia FP32 option scoring through an existing ONNX owner and a tokenizer Worker.",
|
|
53
53
|
"availability": "Node on Windows/Linux/macOS with selected ONNX Runtime and model files; Android needs host adaptation",
|
|
54
|
-
"protocol": "Shared browser/native decision encoding with native named tensor results",
|
|
55
|
-
"normalization": "Returns load/evaluate/classify/current/status/subscribe/unload/dispose; preserves complete separate state/question/options/type and original rows, raw scores and every graph output.
|
|
54
|
+
"protocol": "Shared browser/native decision encoding with native named tensor results. Factory and each load accept family/model/revision/dtype; omission or undefined retains the selected value. Laya FP32 defaults remain. Laya FP16 selects onnx/model_fp16.onnx and onnx/model_fp16.onnx_data; Julia FP32 selects model.onnx and model.onnx.data, with its own tokenizer files. Complete new-family selection and matching files are caller-owned. All model fields participate in activation equality; top-level metadata stays attributable to the retiring activation while pendingActivation records its requested successor. Decoder preserves raw probabilities, every original row and every actual output without inventing Julia action logits.",
|
|
55
|
+
"normalization": "Returns load/evaluate/classify/current/status/subscribe/unload/dispose; preserves complete separate state/question/options/type and original rows, raw scores and every graph output. load({family,model,revision,dtype,assetProjectionId,resourcePaths,executionTarget,signal}) retains omitted or undefined family, model, revision, dtype, source, mapping and target fields from the latest selection, initially the factory configuration. A supplied ready projection takes precedence over configured paths and upstream settings; the existing modelAssets owner retains it directly without upstream preparation or fetching. resourcePaths maps model, tokenizer and tokenizerConfig to exact projection member.path values; complete companion files remain at their original relative paths. Missing projections or mapped members report their actual failure. assetProjectionId:null returns to configured paths or existing upstream preparation when no paths were configured. Separately, executionTarget:null requests automatic resolution, while {deviceId} forwards the saved physical selection to the existing ONNX owner. With no target selection, GPU preference still attempts advertised providers then CPU and explicit session providers retain precedence. Equal family, model, revision, dtype, source, complete mapping and target selections share activation; a changed selection cancels the earlier request and joins its actual ONNX/tokenizer cleanup before replacement. Incoming projection use is retained before retiring the previous activation; caller preparation ownership is independent, and native files remain retained through actual ONNX and tokenizer Worker exit. Unload ends native use; loading again after all ownership is released requires a fresh projection. pendingActivation reports the requested replacement and projected source/mapping separately; current().execution remains the complete ONNX load record, with requested/resolved/configured/observed target diagnostics in current().execution.execution. Configuration alone does not prove physical execution. Cancellation retires the shared activation. No preference writes, chat history or duplicate inference engine."
|
|
56
56
|
},
|
|
57
57
|
{
|
|
58
58
|
"id": "core-decisions:createNativeDecisionService",
|
|
@@ -63,10 +63,10 @@
|
|
|
63
63
|
"entrypoints": ["arcane-os/core/decisions"],
|
|
64
64
|
"primaryImport": "arcane-os/core/decisions",
|
|
65
65
|
"group": "Native typed decisions",
|
|
66
|
-
"summary": "Renderer-independent native Laya service reusing the registered local-AI and model-assets owners.",
|
|
66
|
+
"summary": "Renderer-independent native Laya/Julia service reusing the registered local-AI and model-assets owners.",
|
|
67
67
|
"availability": "Node Core with selected native ONNX and model files",
|
|
68
|
-
"protocol": "arcane/1 decisions.status/load/evaluate/unload and decisions.state",
|
|
69
|
-
"normalization": "Registration starts no model or tokenizer. Explicit load obtains dependency-ready same-process owners and reacquires getONNXRuntime after recovery. Factory configuration accepts executionTarget; direct load({executionTarget,signal}) and Core decisions.load({executionTarget}) preserve omitted/undefined
|
|
68
|
+
"protocol": "arcane/1 decisions.status/load/evaluate/unload and decisions.state. Factory, direct load and decisions.load accept family/model/revision/dtype with the direct model's exact variant, omission and replacement semantics. The service persists across explicit model changes; top-level activation metadata and pendingActivation distinguish the retiring and requested selections.",
|
|
69
|
+
"normalization": "Registration starts no model or tokenizer. Explicit load obtains dependency-ready same-process owners and reacquires getONNXRuntime after recovery. Factory configuration accepts family, model, revision, dtype, assetProjectionId, resourcePaths and executionTarget; direct load({family,model,revision,dtype,assetProjectionId,resourcePaths,executionTarget,signal}) and Core decisions.load({family,model,revision,dtype,assetProjectionId,resourcePaths,executionTarget}) preserve omitted/undefined selections, with the Core request signal forwarded separately. resourcePaths maps model, tokenizer and tokenizerConfig to exact ready projection member.path values; complete companion members retain their relative paths. Supplied projections take precedence over configured paths/upstream settings, are retained through the existing model-assets owner, and never invoke upstream preparation or fetching; unavailable projections or mapped members fail directly. assetProjectionId:null returns to configured paths or existing upstream preparation, independently of executionTarget:null requesting automatic device selection. Equal family, model, revision, dtype, source, complete mapping and target selections coalesce; changed selections cancel pending work and join the prior model's disposal before replacement. Incoming retained ownership protects projection files while the previous activation retires; caller preparation ownership remains independent, and native use retains files through actual ONNX/tokenizer exit. pendingActivation includes a projected source/mapping separately from the full activation execution record and its nested execution diagnostics. Native load/evaluate/classify/current/subscribe/unload/dispose preserve complete results; RPC uses the existing tensor codec. Unload has accepted service lifetime; host shutdown cancels request-lifetime load/evaluation."
|
|
70
70
|
},
|
|
71
71
|
{
|
|
72
72
|
"id": "core-decisions:default",
|
|
@@ -168,6 +168,39 @@ Closing a browser accessor does not by itself establish native unload.
|
|
|
168
168
|
Explicitly release unused prepared projections. Core disposal also releases
|
|
169
169
|
preparation ownership and cleans up after retained engine uses end.
|
|
170
170
|
|
|
171
|
+
### Native Laya decisions
|
|
172
|
+
|
|
173
|
+
The existing native decision owner also consumes ready projections through
|
|
174
|
+
`decisions.load({assetProjectionId, resourcePaths, executionTarget?})` over Core,
|
|
175
|
+
or the direct/service `load` method with an optional `signal`. Supply all three
|
|
176
|
+
resource roles with exact projection member paths:
|
|
177
|
+
|
|
178
|
+
```js
|
|
179
|
+
try {
|
|
180
|
+
await client.invoke('decisions.load', {
|
|
181
|
+
assetProjectionId: projection.id,
|
|
182
|
+
resourcePaths: {
|
|
183
|
+
model: 'onnx/model.onnx',
|
|
184
|
+
tokenizer: 'tokenizer.json',
|
|
185
|
+
tokenizerConfig: 'tokenizer_config.json'
|
|
186
|
+
}
|
|
187
|
+
}, {signal, timeoutMs: 0});
|
|
188
|
+
} finally {
|
|
189
|
+
await projection.release();
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The complete projection must also retain the graph's companion files, including
|
|
194
|
+
`onnx/model.onnx_data` for the selected Laya FP32 graph. The native owner resolves
|
|
195
|
+
the three roles from `retain(id).members` and uses those files directly without
|
|
196
|
+
calling `prepare` or downloading again. It releases its native use only after
|
|
197
|
+
actual ONNX and tokenizer exit; it never releases the caller's preparation
|
|
198
|
+
ownership. Replacement acquires incoming ownership before retiring the prior
|
|
199
|
+
activation, including when only its device changes. Unavailable or incomplete
|
|
200
|
+
selections report their errors without an upstream fallback. See
|
|
201
|
+
[native decisions](native-decisions.md#load-complete-files-already-stored-in-dbopfs)
|
|
202
|
+
for source selection, cancellation and complete row/output contracts.
|
|
203
|
+
|
|
171
204
|
`current()` returns `{closing,projections}`. Each projection reports its `id`,
|
|
172
205
|
`directory`, `state`, ordered `members`, `preparationOwned`, number of native
|
|
173
206
|
`uses`, and `error`. Core emits `modelAssets.state` for lifecycle changes.
|