arcane-os 0.2.0 → 0.2.2
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 +35 -0
- package/README.md +8 -8
- package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +5 -5
- package/browser-runtime/ai/browser-speech-providers.mjs +331 -25
- package/docs/architecture.md +2 -2
- package/docs/reference/README.md +79 -13
- package/docs/reference/ai/browser-speech.md +336 -0
- package/docs/reference/ai/browser-wasm.md +207 -82
- package/docs/reference/availability-and-normalization.md +30 -4
- package/docs/reference/behavioral-testing.md +4 -1
- package/docs/reference/cli.md +29 -10
- package/docs/reference/core/arcane-ai-contracts.md +43 -9
- package/docs/reference/inventory/package-api.json +110 -14
- package/docs/reference/inventory/runtime-components.json +19 -6
- package/docs/reference/inventory/runtime-modules.json +113 -9
- package/docs/reference/protocols.md +260 -38
- package/docs/reference/runtime-components.md +108 -15
- package/docs/reference/runtime-modules.md +422 -8
- package/docs/reference/sdk-api.md +626 -85
- package/package.json +1 -1
- package/runtime/ARCANE_RUNTIME_RELEASE.json +19 -19
- package/runtime/arcane/components/chat.html +288 -52
- package/runtime/arcane/components/speech.html +339 -15
- package/runtime/arcane/modules/AI.js +1245 -136
- package/runtime/arcane/modules/AIProviderRuntime.js +299 -30
- package/runtime/arcane/modules/AIRuntimeState.js +23 -4
- package/runtime/arcane/modules/ConfiguredAIChatSession.js +93 -8
- package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +448 -24
- package/runtime/arcane/modules/LocalAIReadinessController.js +1 -1
- package/schemas/arcane-lock.schema.json +6 -4
- package/src/dev-server.mjs +29 -13
- package/src/doctor.mjs +1 -3
- package/src/import-map.mjs +134 -83
- package/src/packager/core.mjs +311 -39
- package/src/scaffold.mjs +45 -17
- package/src/templates/workspace-template.mjs +23 -4
- package/src/toolchain.mjs +10 -2
- package/src/workspace.mjs +177 -24
|
@@ -53,6 +53,17 @@ function usageCount(value){
|
|
|
53
53
|
return Number.isSafeInteger(value)&&value>=0?value:null;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
function providerUsageCount(value,label){
|
|
57
|
+
if(value===undefined) return null;
|
|
58
|
+
if(!Number.isSafeInteger(value)||value<0){
|
|
59
|
+
throw coded(
|
|
60
|
+
new TypeError(`${label} must be a nonnegative integer when provided.`),
|
|
61
|
+
'AI_CHAT_INVALID_RESPONSE',
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
|
|
56
67
|
function signalLike(value){
|
|
57
68
|
return value===undefined||value===null||(
|
|
58
69
|
typeof value==='object'
|
|
@@ -271,14 +282,14 @@ async function configuredArcaneChat(request){
|
|
|
271
282
|
return api.chat(request);
|
|
272
283
|
}
|
|
273
284
|
|
|
274
|
-
function
|
|
275
|
-
if(!isPlainRecord(
|
|
285
|
+
function normalizeAssistantResponseMessage(value,maxMessageCharacters,{requireRole=false}={}){
|
|
286
|
+
if(!isPlainRecord(value)){
|
|
276
287
|
throw coded(
|
|
277
288
|
new TypeError('The chat provider returned an invalid response.'),
|
|
278
289
|
'AI_CHAT_INVALID_RESPONSE',
|
|
279
290
|
);
|
|
280
291
|
}
|
|
281
|
-
if(
|
|
292
|
+
if((requireRole&&value.role!=='assistant')||(value.role!==undefined&&value.role!=='assistant')){
|
|
282
293
|
throw coded(
|
|
283
294
|
new TypeError('The chat provider response must contain an assistant message.'),
|
|
284
295
|
'AI_CHAT_INVALID_RESPONSE',
|
|
@@ -288,7 +299,7 @@ function normalizeResponse(response,maxMessageCharacters){
|
|
|
288
299
|
let responseMessage;
|
|
289
300
|
try{
|
|
290
301
|
responseMessage=normalizeMessage(
|
|
291
|
-
{...
|
|
302
|
+
{...value,role:'assistant'},
|
|
292
303
|
'The assistant message',
|
|
293
304
|
maxMessageCharacters,
|
|
294
305
|
new Set(['assistant']),
|
|
@@ -296,6 +307,14 @@ function normalizeResponse(response,maxMessageCharacters){
|
|
|
296
307
|
}catch(error){
|
|
297
308
|
throw coded(error,'AI_CHAT_INVALID_RESPONSE');
|
|
298
309
|
}
|
|
310
|
+
return responseMessage;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function normalizeSessionResponse(response,maxMessageCharacters){
|
|
314
|
+
const responseMessage=normalizeAssistantResponseMessage(
|
|
315
|
+
response.message,
|
|
316
|
+
maxMessageCharacters,
|
|
317
|
+
);
|
|
299
318
|
|
|
300
319
|
return Object.freeze({
|
|
301
320
|
provider:optionalMetadata(response.provider,'The provider name',128),
|
|
@@ -308,6 +327,63 @@ function normalizeResponse(response,maxMessageCharacters){
|
|
|
308
327
|
});
|
|
309
328
|
}
|
|
310
329
|
|
|
330
|
+
function normalizeOpenAICompatibleResponse(response,maxMessageCharacters){
|
|
331
|
+
if(!Array.isArray(response.choices)||response.choices.length!==1){
|
|
332
|
+
throw coded(
|
|
333
|
+
new TypeError('The chat provider completion must contain exactly one choice.'),
|
|
334
|
+
'AI_CHAT_INVALID_RESPONSE',
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
const choice=response.choices[0];
|
|
338
|
+
if(!isPlainRecord(choice)||(choice.index!==undefined&&choice.index!==0)){
|
|
339
|
+
throw coded(
|
|
340
|
+
new TypeError('The chat provider completion contains an invalid choice.'),
|
|
341
|
+
'AI_CHAT_INVALID_RESPONSE',
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
if(response.usage!==undefined&&response.usage!==null&&!isPlainRecord(response.usage)){
|
|
345
|
+
throw coded(
|
|
346
|
+
new TypeError('The chat provider completion usage must be a plain object when provided.'),
|
|
347
|
+
'AI_CHAT_INVALID_RESPONSE',
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
const usage=response.usage??{};
|
|
351
|
+
const responseMessage=normalizeAssistantResponseMessage(
|
|
352
|
+
choice.message,
|
|
353
|
+
maxMessageCharacters,
|
|
354
|
+
{requireRole:true},
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
return Object.freeze({
|
|
358
|
+
provider:optionalMetadata(response.provider,'The provider name',128),
|
|
359
|
+
model:optionalMetadata(response.model,'The model name',256),
|
|
360
|
+
message:responseMessage,
|
|
361
|
+
done:true,
|
|
362
|
+
doneReason:optionalMetadata(choice.finish_reason,'The completion reason',128),
|
|
363
|
+
promptEvalCount:providerUsageCount(
|
|
364
|
+
usage.prompt_tokens,
|
|
365
|
+
'The provider prompt token count',
|
|
366
|
+
),
|
|
367
|
+
evalCount:providerUsageCount(
|
|
368
|
+
usage.completion_tokens,
|
|
369
|
+
'The provider completion token count',
|
|
370
|
+
),
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function normalizeResponse(response,maxMessageCharacters){
|
|
375
|
+
if(!isPlainRecord(response)){
|
|
376
|
+
throw coded(
|
|
377
|
+
new TypeError('The chat provider returned an invalid response.'),
|
|
378
|
+
'AI_CHAT_INVALID_RESPONSE',
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
if(Object.hasOwn(response,'message')){
|
|
382
|
+
return normalizeSessionResponse(response,maxMessageCharacters);
|
|
383
|
+
}
|
|
384
|
+
return normalizeOpenAICompatibleResponse(response,maxMessageCharacters);
|
|
385
|
+
}
|
|
386
|
+
|
|
311
387
|
/**
|
|
312
388
|
* Maintains one bounded, in-memory conversation through a configured chat provider.
|
|
313
389
|
*
|
|
@@ -315,7 +391,9 @@ function normalizeResponse(response,maxMessageCharacters){
|
|
|
315
391
|
* provider selection. Applications own their prompt policy and may inject an
|
|
316
392
|
* asynchronous contextBuilder that returns additional system text for each send.
|
|
317
393
|
* An explicitly supplied responseLength augments only this conversational
|
|
318
|
-
* session's system prompt through the shared response-length policy.
|
|
394
|
+
* session's system prompt through the shared response-length policy. An injected
|
|
395
|
+
* chat function may return either the normalized session response or one
|
|
396
|
+
* non-stream OpenAI-compatible completion containing exactly one choice.
|
|
319
397
|
*/
|
|
320
398
|
export default class ConfiguredAIChatSession{
|
|
321
399
|
#chat;
|
|
@@ -498,12 +576,19 @@ export default class ConfiguredAIChatSession{
|
|
|
498
576
|
?this.#conversation.length-toolCallIndex+transientTail.length
|
|
499
577
|
:transientTail.length,
|
|
500
578
|
);
|
|
501
|
-
|
|
502
|
-
|
|
579
|
+
let providerResponse;
|
|
580
|
+
try{
|
|
581
|
+
providerResponse=await this.#chat({
|
|
503
582
|
...this.#request,
|
|
504
583
|
...(options.signal?{signal:options.signal}:{}),
|
|
505
584
|
messages:requestMessages.map(publicMessage),
|
|
506
|
-
})
|
|
585
|
+
});
|
|
586
|
+
}catch(error){
|
|
587
|
+
if(options.signal?.aborted) throw abortError();
|
|
588
|
+
throw error;
|
|
589
|
+
}
|
|
590
|
+
const response=normalizeResponse(
|
|
591
|
+
providerResponse,
|
|
507
592
|
this.#limits.maxMessageCharacters,
|
|
508
593
|
);
|
|
509
594
|
if(options.signal?.aborted) throw abortError();
|