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
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
# Native
|
|
1
|
+
# Native typed decisions
|
|
2
2
|
|
|
3
3
|
`arcane-os/local-ai/decisions` and `arcane-os/core/decisions` run the selected
|
|
4
|
-
Laya FP32 graph through the SDK's existing native ONNX owner. Tokenization runs
|
|
4
|
+
Laya FP32, Laya FP16 or Julia FP32 graph through the SDK's existing native ONNX owner. Tokenization runs
|
|
5
5
|
in its own Node Worker. A Core service can load and evaluate decisions without
|
|
6
6
|
a renderer, browser session, WebGPU browser context or chat provider.
|
|
7
7
|
|
|
@@ -58,7 +58,7 @@ name to `decisions`. It exposes `load`, `evaluate`, `classify` (an alias for
|
|
|
58
58
|
| Method | Parameters and result |
|
|
59
59
|
| --- | --- |
|
|
60
60
|
| `decisions.status` | Current lifecycle snapshot; does not load anything. |
|
|
61
|
-
| `decisions.load` | `{
|
|
61
|
+
| `decisions.load` | `{family?, model?, revision?, dtype?, assetProjectionId?, resourcePaths?, executionTarget?}`; explicit activation or model/source/device replacement, returning the ready lifecycle snapshot. The request signal cancels activation. A target is `{deviceId:string}` or `null`. |
|
|
62
62
|
| `decisions.evaluate` | `{rows, runOptions?}`; returns `{decisions, outputs}`. The request signal cancels the active model operation. |
|
|
63
63
|
| `decisions.unload` | Releases the activation. Service-lifetime cleanup continues after the caller disconnects. |
|
|
64
64
|
|
|
@@ -94,11 +94,89 @@ const decisions = createNativeDecisionModel({
|
|
|
94
94
|
These direct paths are native filesystem paths. Keep the graph's external data
|
|
95
95
|
beside it under the exact filename required by the graph. The Core service
|
|
96
96
|
resolves configured `paths` relative to `appRoot`; it does not copy or remove
|
|
97
|
-
caller-owned files.
|
|
98
|
-
`onnx-community/laya-typed-decisions-ONNX`, revision
|
|
97
|
+
caller-owned files. Factory defaults remain `family:'laya'`,
|
|
98
|
+
`model:'onnx-community/laya-typed-decisions-ONNX'`, `revision:'main'` and
|
|
99
|
+
`dtype:'fp32'`. SDK upstream preparation selects these exact layouts:
|
|
99
100
|
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
| Family and dtype | Graph | Complete external data |
|
|
102
|
+
| --- | --- | --- |
|
|
103
|
+
| `laya`, `fp32` | `onnx/model.onnx` | `onnx/model.onnx_data` |
|
|
104
|
+
| `laya`, `fp16` | `onnx/model_fp16.onnx` | `onnx/model_fp16.onnx_data` |
|
|
105
|
+
| `julia`, `fp32` | `model.onnx` | `model.onnx.data` |
|
|
106
|
+
|
|
107
|
+
Each selection uses `tokenizer.json` and `tokenizer_config.json` from its own
|
|
108
|
+
selected repository. Julia's public repository is `SupersonicLabs/Julia-1-ONNX`.
|
|
109
|
+
|
|
110
|
+
## Load complete files already stored in DBOPFS
|
|
111
|
+
|
|
112
|
+
Use the existing [model-assets projection](model-assets.md) for complete files
|
|
113
|
+
already obtained through the application's DBOPFS model store. Pass every
|
|
114
|
+
original member, including `onnx/model.onnx_data`, to `prepareCoreModelAssets`.
|
|
115
|
+
The original relative paths and complete content remain unchanged:
|
|
116
|
+
|
|
117
|
+
```js
|
|
118
|
+
import {prepareCoreModelAssets} from 'arcane-os/ai/core-model-assets';
|
|
119
|
+
|
|
120
|
+
const projection = await prepareCoreModelAssets({
|
|
121
|
+
client,
|
|
122
|
+
workingDirectory: selectedWorkingDirectory,
|
|
123
|
+
members: [
|
|
124
|
+
{path: 'onnx/model.onnx', file: storedGraph},
|
|
125
|
+
{path: 'onnx/model.onnx_data', file: storedGraphData},
|
|
126
|
+
{path: 'tokenizer.json', file: storedTokenizer},
|
|
127
|
+
{path: 'tokenizer_config.json', file: storedTokenizerConfig}
|
|
128
|
+
],
|
|
129
|
+
signal
|
|
130
|
+
});
|
|
131
|
+
try {
|
|
132
|
+
await client.invoke('decisions.load', {
|
|
133
|
+
assetProjectionId: projection.id,
|
|
134
|
+
resourcePaths: {
|
|
135
|
+
model: 'onnx/model.onnx',
|
|
136
|
+
tokenizer: 'tokenizer.json',
|
|
137
|
+
tokenizerConfig: 'tokenizer_config.json'
|
|
138
|
+
},
|
|
139
|
+
executionTarget: {deviceId: 'cpu'}
|
|
140
|
+
}, {signal, timeoutMs: 0});
|
|
141
|
+
} finally {
|
|
142
|
+
// The native activation owns a separate retain handle after loading.
|
|
143
|
+
await projection.release();
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Here the four `stored*` values are the complete original Blob/File members
|
|
148
|
+
from the application's existing store. This projects those files; it performs
|
|
149
|
+
no model download. The decision service must already be registered as above.
|
|
150
|
+
After loading, invoke `decisions.evaluate` with the complete `rows`, then
|
|
151
|
+
`decisions.unload` when that native activation is no longer needed.
|
|
152
|
+
|
|
153
|
+
Direct model and service calls accept
|
|
154
|
+
`load({family, model, revision, dtype, assetProjectionId, resourcePaths, executionTarget, signal})`; their
|
|
155
|
+
factory configuration accepts the same source selection. A direct model needs
|
|
156
|
+
the existing `modelAssets` owner when selecting a projection. `resourcePaths`
|
|
157
|
+
maps all three roles (`model`, `tokenizer`, `tokenizerConfig`) to exact
|
|
158
|
+
`member.path` values in a ready projection. Additional companion members stay
|
|
159
|
+
in its retained directory; the graph finds its external data under the exact
|
|
160
|
+
relative filename it references. The SDK does not rename or reconstruct them.
|
|
161
|
+
|
|
162
|
+
A supplied projection is retained and used directly. It takes precedence over
|
|
163
|
+
configured `paths` and upstream settings and never invokes upstream preparation
|
|
164
|
+
or retries with downloaded files. Missing, unfinished or released projections,
|
|
165
|
+
and missing mapped resources, report their actual failure.
|
|
166
|
+
|
|
167
|
+
Omitted or `undefined` source fields retain the selected source and mappings.
|
|
168
|
+
`assetProjectionId: null` returns to the factory's configured `paths`, or its
|
|
169
|
+
existing upstream preparation when no paths were configured. Selecting another
|
|
170
|
+
projection or changing any resource mapping replaces the activation even when
|
|
171
|
+
the execution target stays the same. Equal family, model, revision, dtype,
|
|
172
|
+
source, mapping and device selections
|
|
173
|
+
coalesce. The incoming retain is acquired before the prior activation retires,
|
|
174
|
+
so device replacement can reuse a projection after the caller has released its
|
|
175
|
+
preparation ownership. Superseded pending retains are released as their cleanup
|
|
176
|
+
settles. Unload ends the native use; if no preparation or other use remains,
|
|
177
|
+
create a fresh projection from the stored originals before loading again.
|
|
178
|
+
|
|
179
|
+
## Existing upstream preparation
|
|
102
180
|
|
|
103
181
|
`model` selects the upstream Hugging Face repository and `revision` its
|
|
104
182
|
revision. Native preparation performs normal upstream fetches only during
|
|
@@ -108,8 +186,12 @@ model store. The approved `@huggingface/tokenizers@0.2.0` dependency reads the
|
|
|
108
186
|
selected tokenizer files in its Worker. No Transformers or second ONNX runtime
|
|
109
187
|
is loaded by this API.
|
|
110
188
|
|
|
111
|
-
|
|
112
|
-
|
|
189
|
+
Native graph selections support Laya `fp32` and `fp16`, and Julia `fp32`.
|
|
190
|
+
Other family/precision combinations report an unsupported selection. Laya FP16
|
|
191
|
+
uses the published mixed-precision graph, which retains selected operations
|
|
192
|
+
and outputs in FP32; it does not claim every graph operation uses FP16. Julia
|
|
193
|
+
uses its typed five-input ONNX graph and raw option logits, without the upstream
|
|
194
|
+
display-rounded `predict()` wrapper or invented action outputs.
|
|
113
195
|
`sessionOptions` and `runOptions` pass to the existing native ONNX API unchanged.
|
|
114
196
|
With no `executionTarget` selection, `executionPreference` defaults to `gpu`
|
|
115
197
|
here: actual advertised native GPU provider creation is attempted, followed by
|
|
@@ -119,6 +201,49 @@ continues to default to CPU. Accepted provider configuration is not evidence
|
|
|
119
201
|
that GPU nodes executed. Full provider-attempt diagnostics remain in
|
|
120
202
|
`execution`; see [native ONNX sessions](local-ai.md#native-onnx-sessions).
|
|
121
203
|
|
|
204
|
+
## Select an activation's model
|
|
205
|
+
|
|
206
|
+
Both factories and each direct/Core `load` accept `family`, `model`, `revision`
|
|
207
|
+
and `dtype`. The same service persists while its selected activation changes:
|
|
208
|
+
|
|
209
|
+
```js
|
|
210
|
+
await client.invoke('decisions.load', {
|
|
211
|
+
family: 'julia',
|
|
212
|
+
model: 'SupersonicLabs/Julia-1-ONNX',
|
|
213
|
+
revision: 'main',
|
|
214
|
+
dtype: 'fp32',
|
|
215
|
+
assetProjectionId: juliaProjection.id,
|
|
216
|
+
resourcePaths: {
|
|
217
|
+
model: 'model.onnx',
|
|
218
|
+
tokenizer: 'tokenizer.json',
|
|
219
|
+
tokenizerConfig: 'tokenizer_config.json'
|
|
220
|
+
}
|
|
221
|
+
}, {signal, timeoutMs: 0});
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Here `juliaProjection` is the caller's ready projection containing Julia's
|
|
225
|
+
complete graph, `model.onnx.data` and its own tokenizer files. The caller may
|
|
226
|
+
release its preparation ownership after loading, as in the projection example.
|
|
227
|
+
For upstream preparation without configured paths, select the same four model
|
|
228
|
+
fields with `assetProjectionId: null`.
|
|
229
|
+
|
|
230
|
+
Omitting a model field, or supplying `undefined`, retains its most recent
|
|
231
|
+
selection, initially the factory default. The selection survives unload for a
|
|
232
|
+
later explicit load. Changing any field replaces the activation; equal complete
|
|
233
|
+
selections share it. Supply the complete new model selection when changing
|
|
234
|
+
family, including its matching projection/mapping or configured native files.
|
|
235
|
+
Changing `family` alone does not infer a repository, change precision, replace
|
|
236
|
+
configured files, or rewrite a projection. Metadata describes the caller's
|
|
237
|
+
selection; the SDK does not inspect weights to establish checkpoint identity.
|
|
238
|
+
Configured `paths` and a selected projection keep their existing precedence
|
|
239
|
+
over upstream preparation.
|
|
240
|
+
|
|
241
|
+
Top-level `current().family`, `model`, `revision` and `dtype` follow the
|
|
242
|
+
activation being loaded, used or retired. While replacement waits for prior
|
|
243
|
+
cleanup, those fields and `execution` still describe the prior activation;
|
|
244
|
+
`pendingActivation` carries the requested successor separately. The new
|
|
245
|
+
selection takes ownership when its activation begins.
|
|
246
|
+
|
|
122
247
|
## Select an activation's execution device
|
|
123
248
|
|
|
124
249
|
Both factories accept `executionTarget: {deviceId:string}` or `null` in their
|
|
@@ -158,15 +283,17 @@ resolution, accepted configuration and actual execution evidence. An unknown
|
|
|
158
283
|
`observedTarget` remains `null`; successful configuration alone does not establish
|
|
159
284
|
which device executed graph nodes.
|
|
160
285
|
|
|
161
|
-
`pendingActivation` is separately `null` or
|
|
162
|
-
|
|
163
|
-
|
|
286
|
+
`pendingActivation` is separately `null` or a selection containing `family`,
|
|
287
|
+
`model`, `revision`, `dtype` and `executionTarget` while a load or replacement
|
|
288
|
+
is pending; its `executionTarget` property is omitted at JSON
|
|
289
|
+
transport when the selection is `undefined`. A projected selection also includes
|
|
290
|
+
`assetProjectionId` and its complete `resourcePaths` mapping. A pending target never overwrites the previous
|
|
164
291
|
activation's `execution` record. The old record remains attributable to the
|
|
165
292
|
retiring activation until its cleanup completes, then the new activation owns
|
|
166
293
|
its own record. A request is not a claim that the requested device is running.
|
|
167
294
|
|
|
168
|
-
Concurrent loads for the same target share their
|
|
169
|
-
different
|
|
295
|
+
Concurrent loads for the same family, model, revision, dtype, source, resource mapping and target share their
|
|
296
|
+
pending activation. A different selection supersedes the earlier pending request, which rejects with
|
|
170
297
|
cancellation instead of returning another target's ready result. Replacement
|
|
171
298
|
joins this model's actual tokenizer/native cleanup before creating its
|
|
172
299
|
successor. Independent model owners retain their independent workers and
|
|
@@ -209,7 +336,8 @@ progress includes model preparation, tokenization and evaluation; downloaded
|
|
|
209
336
|
members report files, not byte progress. Complete technical errors belong in
|
|
210
337
|
developer diagnostics, while the application owns its user-facing status.
|
|
211
338
|
|
|
212
|
-
Concurrent callers selecting the same
|
|
339
|
+
Concurrent callers selecting the same family, model, revision, dtype, source,
|
|
340
|
+
mapping and target share one explicit activation.
|
|
213
341
|
Cancelling a load or evaluation cancels that activation and its outstanding operations, because the
|
|
214
342
|
native session and tokenizer have one owner. Use separately owned models when
|
|
215
343
|
independent cancellation is required. The construction `signal` governs the
|
|
@@ -219,7 +347,8 @@ cleanup; `dispose()` is terminal. Readiness is revoked as disposal begins.
|
|
|
219
347
|
Retained model files remain owned until the ONNX and tokenizer Workers really
|
|
220
348
|
exit. A rejected load, inference or termination request alone does not permit
|
|
221
349
|
file deletion. Cleanup joins active operations and native output delivery,
|
|
222
|
-
preserves cleanup errors, then releases
|
|
350
|
+
preserves cleanup errors, then releases its native-use handle. Preparation
|
|
351
|
+
ownership of a supplied projection remains with its caller. It never
|
|
223
352
|
deletes supplied `paths`. A failed ordinary row does not destroy a healthy
|
|
224
353
|
loaded session or cancel sibling evaluations. The ONNX owner's `stopping:true`
|
|
225
354
|
retires decision readiness immediately on terminal Worker failure or shutdown,
|
|
@@ -232,5 +361,6 @@ availability. Android requires the host's supported native adaptation. Static
|
|
|
232
361
|
source review and synthetic authored fixtures are not model execution or a
|
|
233
362
|
claim of tested platform, precision or accelerator behavior.
|
|
234
363
|
|
|
235
|
-
Upstream selection: [Laya
|
|
364
|
+
Upstream selection: [Laya model files](https://huggingface.co/onnx-community/laya-typed-decisions-ONNX/tree/main),
|
|
365
|
+
[Julia-1 model files](https://huggingface.co/SupersonicLabs/Julia-1-ONNX/tree/main),
|
|
236
366
|
[Tokenizer package](https://www.npmjs.com/package/@huggingface/tokenizers).
|
|
@@ -69,7 +69,7 @@ runtime layouts.
|
|
|
69
69
|
| `arcane-os/core/local-ai` | Native llama.cpp, Ollama and ONNX Core service, model readiness, complete responses and owned lifecycle. See [local AI through Core](local-ai.md). |
|
|
70
70
|
| `arcane-os/local-ai` | Official runtime installation and native artifact bundling for selected llama.cpp/Ollama/ONNX/image requirements and NeMo libraries. |
|
|
71
71
|
| `arcane-os/local-ai/onnx` | Retained native ONNX worker sessions with explicit graph loading and complete tensor inference. |
|
|
72
|
-
| `arcane-os/local-ai/decisions` | Explicit native Laya FP32 decisions using the existing ONNX owner and a tokenizer Worker. |
|
|
72
|
+
| `arcane-os/local-ai/decisions` | Explicit native Laya FP32/FP16 and Julia FP32 decisions using the existing ONNX owner and a tokenizer Worker. |
|
|
73
73
|
| `arcane-os/core/decisions` | Renderer-independent typed-decision service composed with existing local-AI and model-assets owners. |
|
|
74
74
|
| `arcane-os/ai/core-onnx` | Optional browser access to system ONNX through an existing Core connection. |
|
|
75
75
|
| `arcane-os/local-ai/image` | Retained native stable-diffusion.cpp context, complete PNG generation and owned worker lifetime. |
|
|
@@ -186,7 +186,7 @@ The remaining data-only subpaths are eight JSON Schemas and package metadata.
|
|
|
186
186
|
| `acceptWebSocket()` | function | `arcane-os`, `arcane-os/websocket` | WebSocket protocol | Node; browser-native client interoperability |
|
|
187
187
|
| `WS` | class | `arcane-os`, `arcane-os/websocket-client` | Shared WebSocket clients | Node and native-WebSocket browser hosts |
|
|
188
188
|
| `createBrowserDecisionModel()` | function | `arcane-os/ai/browser-decisions` | Browser typed decisions | Browser module Workers and the caller-selected inference backend |
|
|
189
|
-
| `createNativeDecisionModel()` | function | `arcane-os/local-ai/decisions` | Native typed decisions | Node with an existing native ONNX owner and selected Laya FP32 files |
|
|
189
|
+
| `createNativeDecisionModel()` | function | `arcane-os/local-ai/decisions` | Native typed decisions | Node with an existing native ONNX owner and selected Laya FP32/FP16 or Julia FP32 files |
|
|
190
190
|
| `createNativeDecisionService()` | function | `arcane-os/core/decisions` | Native typed decisions | Node Core with existing local-AI and model-assets services |
|
|
191
191
|
| `createNativeDecisionService default export` | function | `arcane-os/core/decisions` | Native typed decisions | Same native decision service factory |
|
|
192
192
|
| `fetchRequest()` | function | `arcane-os/ai/twin-cloud` | TWiN Cloud requests | Node and Browser; remote HTTPS provider |
|
|
@@ -9505,23 +9505,35 @@ native installation eligibility and URL-bar promotion.
|
|
|
9505
9505
|
|
|
9506
9506
|
### Overview
|
|
9507
9507
|
|
|
9508
|
-
Runs explicitly selected Laya FP32 option scoring through an existing native
|
|
9508
|
+
Runs explicitly selected Laya FP32/FP16 or Julia FP32 option scoring through an existing native
|
|
9509
9509
|
ONNX owner, with tokenization in a Node Worker. It creates no second inference
|
|
9510
9510
|
engine and starts no model download at construction.
|
|
9511
9511
|
|
|
9512
9512
|
### Signature and result
|
|
9513
9513
|
|
|
9514
9514
|
```text
|
|
9515
|
-
createNativeDecisionModel({onnx,modelAssets,workingDirectory,paths,model='onnx-community/laya-typed-decisions-ONNX',revision='main',dtype='fp32',sessionOptions,executionPreference='gpu',signal}={})
|
|
9515
|
+
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}={})
|
|
9516
9516
|
```
|
|
9517
9517
|
|
|
9518
9518
|
Import from `arcane-os/local-ai/decisions`. Supply the existing `onnx` owner and
|
|
9519
9519
|
either `modelAssets` plus `workingDirectory`, or native `paths` for the model,
|
|
9520
|
-
tokenizer and tokenizer configuration
|
|
9520
|
+
tokenizer and tokenizer configuration, or a ready `assetProjectionId` with
|
|
9521
|
+
`resourcePaths` for those three roles. Supported selections are Laya `fp32`,
|
|
9522
|
+
Laya `fp16` and Julia `fp32`; constructor defaults remain Laya FP32. The
|
|
9521
9523
|
handle exposes `load`, `evaluate`, `classify`, `current`, `status`, `subscribe`,
|
|
9522
9524
|
`unload` and terminal `dispose`; `classify` aliases `evaluate` and `status`
|
|
9523
9525
|
aliases `current`.
|
|
9524
9526
|
|
|
9527
|
+
`load({family,model,revision,dtype,assetProjectionId,resourcePaths,executionTarget,signal})`
|
|
9528
|
+
selects each activation. Omitted or `undefined` fields retain their prior
|
|
9529
|
+
selection. A changed model field, source, mapping or device replaces the
|
|
9530
|
+
activation after actual cleanup; equal complete selections coalesce. Supply
|
|
9531
|
+
all matching model fields and files when changing family: metadata does not
|
|
9532
|
+
infer a repository or rewrite a supplied projection. Top-level model metadata
|
|
9533
|
+
continues to describe the retiring activation while `pendingActivation` records
|
|
9534
|
+
its successor. See [model selection](native-decisions.md#select-an-activations-model)
|
|
9535
|
+
for exact upstream graph/companion layouts and retained projection ownership.
|
|
9536
|
+
|
|
9525
9537
|
`evaluate(rows,{signal,runOptions})` preserves each complete `state`, `question`,
|
|
9526
9538
|
`options` and optional `type` separately. It returns `{decisions,outputs}` with
|
|
9527
9539
|
original rows, typed values, raw logits, unrounded probabilities and every
|
|
@@ -9574,7 +9586,11 @@ reacquires `getONNXRuntime()` after any recovery. Native members are `load`,
|
|
|
9574
9586
|
`start(context)`.
|
|
9575
9587
|
|
|
9576
9588
|
RPC exposes `decisions.status`, `decisions.load`, `decisions.evaluate` and
|
|
9577
|
-
`decisions.unload`.
|
|
9589
|
+
`decisions.unload`. Load accepts optional `family`, `model`, `revision`, `dtype`,
|
|
9590
|
+
`assetProjectionId`, `resourcePaths` and `executionTarget`, with the same
|
|
9591
|
+
per-activation selection and omission behavior as direct native load; the Core
|
|
9592
|
+
request signal owns cancellation. The service persists across model changes.
|
|
9593
|
+
Evaluation accepts `{rows,runOptions?}` and returns complete
|
|
9578
9594
|
`{decisions,outputs}` using the existing tensor codec at the JSON boundary.
|
|
9579
9595
|
`decisions.state` and replaying `subscribe(listener,{emitCurrent:true,signal})`
|
|
9580
9596
|
report lifecycle. Unload is accepted service-lifetime cleanup; host shutdown
|
package/package.json
CHANGED
|
@@ -5018,6 +5018,7 @@ class AI {
|
|
|
5018
5018
|
model,
|
|
5019
5019
|
structuredOutput=false,
|
|
5020
5020
|
localOnly=false,
|
|
5021
|
+
speech=true,
|
|
5021
5022
|
onChunk=function ignoreStreamChunk(){},
|
|
5022
5023
|
toolText,
|
|
5023
5024
|
onToolText,
|
|
@@ -5062,6 +5063,9 @@ class AI {
|
|
|
5062
5063
|
if(localOnly!==true&&localOnly!==false){
|
|
5063
5064
|
throw new TypeError('AI localOnly must be a boolean.');
|
|
5064
5065
|
}
|
|
5066
|
+
if(speech!==true&&speech!==false){
|
|
5067
|
+
throw new TypeError('AI speech must be a boolean.');
|
|
5068
|
+
}
|
|
5065
5069
|
if(localOnly&&!this.#hasLocalRoute('llm',this.llmService)){
|
|
5066
5070
|
const error=new Error(
|
|
5067
5071
|
'This AI request requires a configured local model.'
|
|
@@ -5163,7 +5167,7 @@ class AI {
|
|
|
5163
5167
|
if(signal?.aborted){
|
|
5164
5168
|
throw normalizeAIRequestAbort();
|
|
5165
5169
|
}
|
|
5166
|
-
this.#finishStreamingSpeech();
|
|
5170
|
+
if(speech)this.#finishStreamingSpeech();
|
|
5167
5171
|
return result;
|
|
5168
5172
|
}catch(error){
|
|
5169
5173
|
if(handle){
|
|
@@ -5171,7 +5175,7 @@ class AI {
|
|
|
5171
5175
|
function retainProviderStreamCleanupFailure() {}
|
|
5172
5176
|
);
|
|
5173
5177
|
}
|
|
5174
|
-
this.stopAudio();
|
|
5178
|
+
if(speech)this.stopAudio();
|
|
5175
5179
|
throw isAIRequestAbort(error,signal)
|
|
5176
5180
|
?normalizeAIRequestAbort(error)
|
|
5177
5181
|
:error;
|
|
@@ -5223,10 +5227,10 @@ class AI {
|
|
|
5223
5227
|
if(signal?.aborted){
|
|
5224
5228
|
throw normalizeAIRequestAbort();
|
|
5225
5229
|
}
|
|
5226
|
-
this.#finishStreamingSpeech();
|
|
5230
|
+
if(speech)this.#finishStreamingSpeech();
|
|
5227
5231
|
return result;
|
|
5228
5232
|
}catch(error){
|
|
5229
|
-
this.stopAudio();
|
|
5233
|
+
if(speech)this.stopAudio();
|
|
5230
5234
|
throw isAIRequestAbort(error,signal)
|
|
5231
5235
|
?normalizeAIRequestAbort(error)
|
|
5232
5236
|
:error;
|
|
@@ -6026,7 +6030,8 @@ class AI {
|
|
|
6026
6030
|
}
|
|
6027
6031
|
throw error;
|
|
6028
6032
|
}finally{
|
|
6029
|
-
|
|
6033
|
+
// Provider transports leave speech lifecycle to their public caller.
|
|
6034
|
+
if(finishSpeech&&!speechTurnCompleted){
|
|
6030
6035
|
this.stopAudio();
|
|
6031
6036
|
}
|
|
6032
6037
|
}
|
|
@@ -124,6 +124,7 @@ namespace Arcane.Core.Hosts.Windows
|
|
|
124
124
|
private bool started;
|
|
125
125
|
private bool documentReady;
|
|
126
126
|
private bool deliveryScheduled;
|
|
127
|
+
private bool browserProcessExited;
|
|
127
128
|
private bool closing;
|
|
128
129
|
private bool closeAllowed;
|
|
129
130
|
|
|
@@ -551,11 +552,26 @@ namespace Arcane.Core.Hosts.Windows
|
|
|
551
552
|
{
|
|
552
553
|
error = new AggregateException("Reading WebView2 process-failure diagnostics failed.", error, diagnosticError);
|
|
553
554
|
}
|
|
554
|
-
|
|
555
|
+
if (kind == CoreWebView2ProcessFailedKind.BrowserProcessExited)
|
|
556
|
+
{
|
|
557
|
+
// WebView2 has closed this browser. Complete the owned host
|
|
558
|
+
// lifetime without trying to notify its vanished document.
|
|
559
|
+
browserProcessExited = true;
|
|
560
|
+
lock (stateLock)
|
|
561
|
+
{
|
|
562
|
+
if (transportFailure == null) transportFailure = error;
|
|
563
|
+
}
|
|
564
|
+
Report(error);
|
|
565
|
+
ready.TrySetException(error);
|
|
566
|
+
if (bridge != null) bridge.Stop(error);
|
|
567
|
+
if (activeBridge != null) activeBridge.Stop(error);
|
|
568
|
+
BeginClose();
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
// Loss of the top-level renderer ends its bridge.
|
|
555
572
|
// GPU/utility/plugin helpers recover independently; subframe loss,
|
|
556
573
|
// unresponsiveness and unspecified failures remain full diagnostics.
|
|
557
|
-
if (kind == CoreWebView2ProcessFailedKind.
|
|
558
|
-
|| kind == CoreWebView2ProcessFailedKind.RenderProcessExited) CoreFailed(error);
|
|
574
|
+
if (kind == CoreWebView2ProcessFailedKind.RenderProcessExited) CoreFailed(error);
|
|
559
575
|
else Report(error);
|
|
560
576
|
}
|
|
561
577
|
|
|
@@ -665,7 +681,7 @@ namespace Arcane.Core.Hosts.Windows
|
|
|
665
681
|
|
|
666
682
|
private void NotifyTransportFailure()
|
|
667
683
|
{
|
|
668
|
-
if (!documentReady || webView.CoreWebView2 == null || transportFailure == null
|
|
684
|
+
if (closing || browserProcessExited || !documentReady || webView.CoreWebView2 == null || transportFailure == null) return;
|
|
669
685
|
if (failureNotifiedGeneration == generation) return;
|
|
670
686
|
failureNotifiedGeneration = generation;
|
|
671
687
|
Task failureNotification = NotifyTransportFailureAsync(transportFailure);
|
|
@@ -761,7 +777,7 @@ namespace Arcane.Core.Hosts.Windows
|
|
|
761
777
|
ready.TrySetCanceled();
|
|
762
778
|
try
|
|
763
779
|
{
|
|
764
|
-
if (webView.CoreWebView2 != null)
|
|
780
|
+
if (!browserProcessExited && webView.CoreWebView2 != null)
|
|
765
781
|
{
|
|
766
782
|
webView.CoreWebView2.RemoveHostObjectFromScript("arcaneBridge");
|
|
767
783
|
if (injectedScript != null) webView.CoreWebView2.RemoveScriptToExecuteOnDocumentCreated(injectedScript);
|