arcane-os 0.95.0 → 0.96.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.
@@ -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, prepare, executionDevices} = {}) {
124
+ export function createLocalAIService(configuration, {appRoot, runtimes = [], signal, onEvent, prepare, executionDevices, autoStart = true} = {}) {
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;
@@ -130,6 +130,8 @@ export function createLocalAIService(configuration, {appRoot, runtimes = [], sig
130
130
  const owner = {};
131
131
  const events = createArcaneEventSource(owner, {source: 'core-local-ai', eventTypes: ['localai.state']});
132
132
  let context;
133
+ let preparing;
134
+ let prepared = false;
133
135
  let modelAssetOwner;
134
136
  let closed = false;
135
137
  let onnx;
@@ -738,17 +740,20 @@ export function createLocalAIService(configuration, {appRoot, runtimes = [], sig
738
740
  });
739
741
  }
740
742
 
741
- const service = {
742
- name: 'local-ai',
743
- current: snapshot,
744
- getONNXRuntime() {
745
- return requireONNX();
746
- },
747
- async start(currentContext) {
748
- context = currentContext;
743
+ function prepareEngines(requestSignal) {
744
+ lifetimeSignal.throwIfAborted();
745
+ requestSignal?.throwIfAborted();
746
+ if (prepared) return Promise.resolve(snapshot());
747
+ if (preparing) return awaitReadiness(preparing, requestSignal ?? lifetimeSignal);
748
+ const operationSignal = requestSignal ? AbortSignal.any([lifetimeSignal, requestSignal]) : lifetimeSignal;
749
+ const task = Promise.resolve().then(async function prepareSelectedEngines() {
750
+ operationSignal.throwIfAborted();
749
751
  if (prepare) {
750
- runtimes = await prepare({signal: lifetimeSignal, onEvent});
751
- lifetimeSignal.throwIfAborted();
752
+ runtimes = await prepare({signal: operationSignal, onEvent: function preparationProgress(event) {
753
+ context?.emit('localai.preparation', event);
754
+ return onEvent?.(event);
755
+ }});
756
+ operationSignal.throwIfAborted();
752
757
  onnxRecord = runtimes.find(function installedONNX(runtime) { return runtime.id === 'onnx'; });
753
758
  }
754
759
  for (const requirement of config.runtimes) {
@@ -764,11 +769,40 @@ export function createLocalAIService(configuration, {appRoot, runtimes = [], sig
764
769
  state: {id: requirement.id, installed: runtimes.some(function installed(record) { return record.id === requirement.id && record.executable; }), available: false, state: 'starting'}
765
770
  };
766
771
  engines.set(engine.id, engine);
767
- startEngine(engine);
772
+ try { startEngine(engine); }
773
+ catch (error) {
774
+ engines.delete(engine.id);
775
+ throw error;
776
+ }
768
777
  }
769
778
  publish();
779
+ prepared = true;
780
+ });
781
+ preparing = task;
782
+ jobs.add(task);
783
+ function releasePreparation() {
784
+ jobs.delete(task);
785
+ if (preparing === task) preparing = null;
786
+ }
787
+ task.then(releasePreparation, releasePreparation);
788
+ return task;
789
+ }
790
+
791
+ const service = {
792
+ name: 'local-ai',
793
+ current: snapshot,
794
+ getONNXRuntime() {
795
+ return requireONNX();
796
+ },
797
+ async start(currentContext) {
798
+ context = currentContext;
799
+ if (autoStart === false) return;
800
+ return prepareEngines(lifetimeSignal);
770
801
  },
771
802
  methods: {
803
+ 'localai.prepare': function prepareSelectedRuntime(_parameters, request) {
804
+ return prepareEngines(request.signal).then(snapshot);
805
+ },
772
806
  'localai.services.recover': async function recoverServices({runtimes: selected = [...engines.keys(), ...(onnxSelected ? ['onnx'] : [])]} = {}, request) {
773
807
  request.signal.throwIfAborted();
774
808
  const selectedEngines = selected.map(function selectedEngine(id) {