@omote/core 0.5.4 → 0.5.6

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/dist/index.mjs CHANGED
@@ -510,6 +510,7 @@ var A2EProcessor = class {
510
510
  this.backend = config.backend;
511
511
  this.sampleRate = config.sampleRate ?? 16e3;
512
512
  this.chunkSize = config.chunkSize ?? config.backend.chunkSize ?? 16e3;
513
+ this.identityIndex = config.identityIndex ?? 0;
513
514
  this.onFrame = config.onFrame;
514
515
  this.onError = config.onError;
515
516
  this.bufferCapacity = this.chunkSize * 2;
@@ -717,7 +718,7 @@ var A2EProcessor = class {
717
718
  const { chunk, timestamp } = this.pendingChunks.shift();
718
719
  try {
719
720
  const t0 = performance.now();
720
- const result = await this.backend.infer(chunk);
721
+ const result = await this.backend.infer(chunk, this.identityIndex);
721
722
  const inferMs = Math.round(performance.now() - t0);
722
723
  const actualDuration = chunk.length / this.sampleRate;
723
724
  const actualFrameCount = Math.ceil(actualDuration * FRAME_RATE);
@@ -1812,6 +1813,16 @@ function getModelCache() {
1812
1813
  return cacheInstance;
1813
1814
  }
1814
1815
  var MAX_CACHE_SIZE_BYTES = 500 * 1024 * 1024;
1816
+ function fetchWithTimeout(url, timeoutMs, signal) {
1817
+ const controller = new AbortController();
1818
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
1819
+ const onCallerAbort = () => controller.abort();
1820
+ signal?.addEventListener("abort", onCallerAbort, { once: true });
1821
+ return fetch(url, { signal: controller.signal }).finally(() => {
1822
+ clearTimeout(timer);
1823
+ signal?.removeEventListener("abort", onCallerAbort);
1824
+ });
1825
+ }
1815
1826
  async function fetchWithCache(url, optionsOrProgress) {
1816
1827
  let options = {};
1817
1828
  if (typeof optionsOrProgress === "function") {
@@ -1865,61 +1876,84 @@ async function fetchWithCache(url, optionsOrProgress) {
1865
1876
  }
1866
1877
  span?.setAttributes({ "fetch.cache_hit": false });
1867
1878
  console.log(`[ModelCache] Cache miss, fetching: ${url}`);
1868
- try {
1869
- const response = await fetch(url);
1870
- if (!response.ok) {
1871
- throw new Error(`Failed to fetch ${url}: ${response.status}`);
1872
- }
1873
- const contentLength = response.headers.get("content-length");
1874
- const total = contentLength ? parseInt(contentLength, 10) : 0;
1875
- const etag = response.headers.get("etag") ?? void 0;
1876
- const tooLargeForCache = total > MAX_CACHE_SIZE_BYTES;
1877
- if (tooLargeForCache) {
1878
- console.log(`[ModelCache] File too large for IndexedDB (${(total / 1024 / 1024).toFixed(0)}MB > 500MB), using HTTP cache only`);
1879
- }
1880
- if (!response.body) {
1881
- const data2 = await response.arrayBuffer();
1879
+ const timeout = options.timeoutMs ?? 12e4;
1880
+ const maxRetries = options.maxRetries ?? 2;
1881
+ let lastError = null;
1882
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
1883
+ if (options.signal?.aborted) {
1884
+ throw new Error(`Fetch aborted for ${url}`);
1885
+ }
1886
+ if (attempt > 0) {
1887
+ const backoff = Math.min(2e3 * Math.pow(2, attempt - 1), 16e3);
1888
+ console.log(`[ModelCache] Retry ${attempt}/${maxRetries} after ${backoff}ms: ${url}`);
1889
+ await new Promise((r) => setTimeout(r, backoff));
1890
+ }
1891
+ try {
1892
+ const response = await fetchWithTimeout(url, timeout, options.signal);
1893
+ if (!response.ok) {
1894
+ throw new Error(`Failed to fetch ${url}: ${response.status}`);
1895
+ }
1896
+ const contentLength = response.headers.get("content-length");
1897
+ const total = contentLength ? parseInt(contentLength, 10) : 0;
1898
+ const etag = response.headers.get("etag") ?? void 0;
1899
+ const tooLargeForCache = total > MAX_CACHE_SIZE_BYTES;
1900
+ if (tooLargeForCache) {
1901
+ console.log(`[ModelCache] File too large for IndexedDB (${(total / 1024 / 1024).toFixed(0)}MB > 500MB), using HTTP cache only`);
1902
+ }
1903
+ if (!response.body) {
1904
+ const data2 = await response.arrayBuffer();
1905
+ if (!tooLargeForCache) {
1906
+ await cache.set(cacheKey, data2, etag, version);
1907
+ }
1908
+ span?.setAttributes({
1909
+ "fetch.size_bytes": data2.byteLength,
1910
+ "fetch.cached_to_indexeddb": !tooLargeForCache,
1911
+ ...attempt > 0 && { "fetch.retry_count": attempt }
1912
+ });
1913
+ span?.end();
1914
+ return data2;
1915
+ }
1916
+ const reader = response.body.getReader();
1917
+ const chunks = [];
1918
+ let loaded = 0;
1919
+ while (true) {
1920
+ const { done, value } = await reader.read();
1921
+ if (done) break;
1922
+ chunks.push(value);
1923
+ loaded += value.length;
1924
+ onProgress?.(loaded, total || loaded);
1925
+ }
1926
+ const data = new Uint8Array(loaded);
1927
+ let offset = 0;
1928
+ for (const chunk of chunks) {
1929
+ data.set(chunk, offset);
1930
+ offset += chunk.length;
1931
+ }
1932
+ const buffer = data.buffer;
1882
1933
  if (!tooLargeForCache) {
1883
- await cache.set(cacheKey, data2, etag, version);
1934
+ await cache.set(cacheKey, buffer, etag, version);
1935
+ console.log(`[ModelCache] Cached: ${url} (${(buffer.byteLength / 1024 / 1024).toFixed(1)}MB)`);
1884
1936
  }
1885
1937
  span?.setAttributes({
1886
- "fetch.size_bytes": data2.byteLength,
1887
- "fetch.cached_to_indexeddb": !tooLargeForCache
1938
+ "fetch.size_bytes": buffer.byteLength,
1939
+ "fetch.cached_to_indexeddb": !tooLargeForCache,
1940
+ ...attempt > 0 && { "fetch.retry_count": attempt }
1888
1941
  });
1889
1942
  span?.end();
1890
- return data2;
1891
- }
1892
- const reader = response.body.getReader();
1893
- const chunks = [];
1894
- let loaded = 0;
1895
- while (true) {
1896
- const { done, value } = await reader.read();
1897
- if (done) break;
1898
- chunks.push(value);
1899
- loaded += value.length;
1900
- onProgress?.(loaded, total || loaded);
1901
- }
1902
- const data = new Uint8Array(loaded);
1903
- let offset = 0;
1904
- for (const chunk of chunks) {
1905
- data.set(chunk, offset);
1906
- offset += chunk.length;
1907
- }
1908
- const buffer = data.buffer;
1909
- if (!tooLargeForCache) {
1910
- await cache.set(cacheKey, buffer, etag, version);
1911
- console.log(`[ModelCache] Cached: ${url} (${(buffer.byteLength / 1024 / 1024).toFixed(1)}MB)`);
1943
+ return buffer;
1944
+ } catch (error) {
1945
+ lastError = error instanceof Error ? error : new Error(String(error));
1946
+ if (options.signal?.aborted) {
1947
+ span?.endWithError(lastError);
1948
+ throw lastError;
1949
+ }
1950
+ if (attempt < maxRetries) {
1951
+ console.warn(`[ModelCache] Attempt ${attempt + 1} failed for ${url}: ${lastError.message}`);
1952
+ }
1912
1953
  }
1913
- span?.setAttributes({
1914
- "fetch.size_bytes": buffer.byteLength,
1915
- "fetch.cached_to_indexeddb": !tooLargeForCache
1916
- });
1917
- span?.end();
1918
- return buffer;
1919
- } catch (error) {
1920
- span?.endWithError(error instanceof Error ? error : new Error(String(error)));
1921
- throw error;
1922
1954
  }
1955
+ span?.endWithError(lastError);
1956
+ throw lastError;
1923
1957
  }
1924
1958
  async function preloadModels(urls, onProgress) {
1925
1959
  const cache = getModelCache();
@@ -2168,6 +2202,15 @@ function getSessionOptions(backend) {
2168
2202
  graphOptimizationLevel: "all"
2169
2203
  };
2170
2204
  }
2205
+ function withTimeout(promise, ms, label) {
2206
+ return new Promise((resolve, reject) => {
2207
+ const timer = setTimeout(
2208
+ () => reject(new Error(`${label} timed out after ${ms}ms`)),
2209
+ ms
2210
+ );
2211
+ promise.then(resolve, reject).finally(() => clearTimeout(timer));
2212
+ });
2213
+ }
2171
2214
 
2172
2215
  // src/inference/blendshapeUtils.ts
2173
2216
  var LAM_BLENDSHAPES = [
@@ -2440,7 +2483,11 @@ var _Wav2Vec2Inference = class _Wav2Vec2Inference {
2440
2483
  )
2441
2484
  });
2442
2485
  try {
2443
- this.session = await this.ort.InferenceSession.create(modelUrl, sessionOptions);
2486
+ this.session = await withTimeout(
2487
+ this.ort.InferenceSession.create(modelUrl, sessionOptions),
2488
+ 18e4,
2489
+ "Wav2Vec2 InferenceSession.create (iOS URL pass-through)"
2490
+ );
2444
2491
  } catch (sessionErr) {
2445
2492
  logger3.error("iOS: InferenceSession.create() failed", {
2446
2493
  error: sessionErr instanceof Error ? sessionErr.message : String(sessionErr),
@@ -2841,6 +2888,7 @@ var FullFacePipeline = class extends EventEmitter {
2841
2888
  backend: options.lam,
2842
2889
  sampleRate,
2843
2890
  chunkSize,
2891
+ identityIndex: options.identityIndex,
2844
2892
  onError: (error) => {
2845
2893
  logger4.error("A2E inference error", { message: error.message, stack: error.stack });
2846
2894
  this.emit("error", error);
@@ -3527,9 +3575,10 @@ var _SenseVoiceInference = class _SenseVoiceInference {
3527
3575
  logger5.info("iOS: passing model URL directly to ORT (low-memory path)", {
3528
3576
  modelUrl: this.config.modelUrl
3529
3577
  });
3530
- this.session = await this.ort.InferenceSession.create(
3531
- this.config.modelUrl,
3532
- sessionOptions
3578
+ this.session = await withTimeout(
3579
+ this.ort.InferenceSession.create(this.config.modelUrl, sessionOptions),
3580
+ 18e4,
3581
+ "SenseVoice InferenceSession.create (iOS URL pass-through)"
3533
3582
  );
3534
3583
  } else {
3535
3584
  const cache = getModelCache();
@@ -3782,6 +3831,12 @@ var WORKER_SCRIPT = `
3782
3831
  var ort = null;
3783
3832
  var session = null;
3784
3833
  var tokenMap = null;
3834
+
3835
+ function fetchWithTimeout(url, timeoutMs) {
3836
+ var controller = new AbortController();
3837
+ var timer = setTimeout(function() { controller.abort(); }, timeoutMs);
3838
+ return fetch(url, { signal: controller.signal }).finally(function() { clearTimeout(timer); });
3839
+ }
3785
3840
  var negMean = null;
3786
3841
  var invStddev = null;
3787
3842
  var languageId = 0;
@@ -4225,7 +4280,7 @@ async function loadOrt(wasmPaths) {
4225
4280
  var ortUrl = wasmPaths + 'ort.wasm.min.js';
4226
4281
 
4227
4282
  // Load the script by fetching and executing it
4228
- var response = await fetch(ortUrl);
4283
+ var response = await fetchWithTimeout(ortUrl, 30000);
4229
4284
  var scriptText = await response.text();
4230
4285
 
4231
4286
  // Create a blob URL for the script
@@ -4251,7 +4306,7 @@ async function loadOrt(wasmPaths) {
4251
4306
  */
4252
4307
  async function loadModel(modelUrl, tokensUrl, isIOSDevice, lang, textNorm) {
4253
4308
  // 1. Fetch and parse tokens.txt
4254
- var tokensResponse = await fetch(tokensUrl);
4309
+ var tokensResponse = await fetchWithTimeout(tokensUrl, 30000);
4255
4310
  if (!tokensResponse.ok) {
4256
4311
  throw new Error('Failed to fetch tokens.txt: ' + tokensResponse.status + ' ' + tokensResponse.statusText);
4257
4312
  }
@@ -4274,7 +4329,7 @@ async function loadModel(modelUrl, tokensUrl, isIOSDevice, lang, textNorm) {
4274
4329
  session = await ort.InferenceSession.create(modelUrl, sessionOptions);
4275
4330
  } else {
4276
4331
  // Desktop: fetch ArrayBuffer for potential caching
4277
- var modelResponse = await fetch(modelUrl);
4332
+ var modelResponse = await fetchWithTimeout(modelUrl, 120000);
4278
4333
  if (!modelResponse.ok) {
4279
4334
  throw new Error('Failed to fetch model: ' + modelResponse.status + ' ' + modelResponse.statusText);
4280
4335
  }
@@ -4785,6 +4840,12 @@ var WORKER_SCRIPT2 = `
4785
4840
 
4786
4841
  var ort = null;
4787
4842
 
4843
+ function fetchWithTimeout(url, timeoutMs) {
4844
+ var controller = new AbortController();
4845
+ var timer = setTimeout(function() { controller.abort(); }, timeoutMs);
4846
+ return fetch(url, { signal: controller.signal }).finally(function() { clearTimeout(timer); });
4847
+ }
4848
+
4788
4849
  // SenseVoice state
4789
4850
  var svSession = null;
4790
4851
  var svTokenMap = null;
@@ -5099,7 +5160,7 @@ function symmetrizeBlendshapes(frame) {
5099
5160
  async function loadOrt(wasmPaths, isIOSDevice) {
5100
5161
  if (ort) return;
5101
5162
  var ortUrl = wasmPaths + 'ort.wasm.min.js';
5102
- var response = await fetch(ortUrl);
5163
+ var response = await fetchWithTimeout(ortUrl, 30000);
5103
5164
  var scriptText = await response.text();
5104
5165
  var blob = new Blob([scriptText], { type: 'application/javascript' });
5105
5166
  var blobUrl = URL.createObjectURL(blob);
@@ -5117,7 +5178,7 @@ async function loadOrt(wasmPaths, isIOSDevice) {
5117
5178
  // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
5118
5179
 
5119
5180
  async function svLoad(msg) {
5120
- var tokensResponse = await fetch(msg.tokensUrl);
5181
+ var tokensResponse = await fetchWithTimeout(msg.tokensUrl, 30000);
5121
5182
  if (!tokensResponse.ok) throw new Error('Failed to fetch tokens.txt: ' + tokensResponse.status);
5122
5183
  var tokensText = await tokensResponse.text();
5123
5184
  svTokenMap = parseTokensFile(tokensText);
@@ -5128,7 +5189,7 @@ async function svLoad(msg) {
5128
5189
  if (msg.isIOS) {
5129
5190
  svSession = await ort.InferenceSession.create(msg.modelUrl, sessionOptions);
5130
5191
  } else {
5131
- var modelResponse = await fetch(msg.modelUrl);
5192
+ var modelResponse = await fetchWithTimeout(msg.modelUrl, 120000);
5132
5193
  if (!modelResponse.ok) throw new Error('Failed to fetch model: ' + modelResponse.status);
5133
5194
  var modelBuffer = await modelResponse.arrayBuffer();
5134
5195
  svSession = await ort.InferenceSession.create(new Uint8Array(modelBuffer), sessionOptions);
@@ -5203,11 +5264,11 @@ async function cpuLoad(msg) {
5203
5264
  }
5204
5265
  cpuSession = await ort.InferenceSession.create(msg.modelUrl, sessionOptions);
5205
5266
  } else {
5206
- var graphResponse = await fetch(msg.modelUrl);
5267
+ var graphResponse = await fetchWithTimeout(msg.modelUrl, 120000);
5207
5268
  if (!graphResponse.ok) throw new Error('Failed to fetch model graph: ' + graphResponse.status);
5208
5269
  var graphBuffer = await graphResponse.arrayBuffer();
5209
5270
  if (msg.externalDataUrl && dataFilename) {
5210
- var dataResponse = await fetch(msg.externalDataUrl);
5271
+ var dataResponse = await fetchWithTimeout(msg.externalDataUrl, 120000);
5211
5272
  if (!dataResponse.ok) throw new Error('Failed to fetch external data: ' + dataResponse.status);
5212
5273
  var dataBuffer = await dataResponse.arrayBuffer();
5213
5274
  sessionOptions.externalData = [{ path: dataFilename, data: new Uint8Array(dataBuffer) }];
@@ -5259,7 +5320,7 @@ async function vadLoad(msg) {
5259
5320
  vadChunkSize = vadSampleRate === 16000 ? 512 : 256;
5260
5321
  vadContextSize = vadSampleRate === 16000 ? 64 : 32;
5261
5322
 
5262
- var response = await fetch(msg.modelUrl);
5323
+ var response = await fetchWithTimeout(msg.modelUrl, 60000);
5263
5324
  if (!response.ok) throw new Error('Failed to fetch VAD model: ' + response.status);
5264
5325
  var modelBuffer = await response.arrayBuffer();
5265
5326
  vadSession = await ort.InferenceSession.create(new Uint8Array(modelBuffer), {
@@ -6107,7 +6168,11 @@ var _Wav2ArkitCpuInference = class _Wav2ArkitCpuInference {
6107
6168
  // URL string — ORT fetches directly into WASM
6108
6169
  }];
6109
6170
  }
6110
- this.session = await this.ort.InferenceSession.create(modelUrl, sessionOptions);
6171
+ this.session = await withTimeout(
6172
+ this.ort.InferenceSession.create(modelUrl, sessionOptions),
6173
+ 18e4,
6174
+ "Wav2ArkitCpu InferenceSession.create (iOS URL pass-through)"
6175
+ );
6111
6176
  } else {
6112
6177
  const cache = getModelCache();
6113
6178
  const isCached = await cache.has(modelUrl);
@@ -6375,6 +6440,12 @@ var WORKER_SCRIPT3 = `
6375
6440
  var ort = null;
6376
6441
  var session = null;
6377
6442
 
6443
+ function fetchWithTimeout(url, timeoutMs) {
6444
+ var controller = new AbortController();
6445
+ var timer = setTimeout(function() { controller.abort(); }, timeoutMs);
6446
+ return fetch(url, { signal: controller.signal }).finally(function() { clearTimeout(timer); });
6447
+ }
6448
+
6378
6449
  // Precomputed symmetric index pairs from LAM_BLENDSHAPES alphabetical ordering
6379
6450
  // Used to average left/right blendshape pairs for symmetrized output
6380
6451
  const SYMMETRIC_INDEX_PAIRS = [
@@ -6424,7 +6495,7 @@ async function loadOrt(wasmPaths) {
6424
6495
  const ortUrl = wasmPaths + 'ort.wasm.min.js';
6425
6496
 
6426
6497
  // Load the script by fetching and executing it
6427
- const response = await fetch(ortUrl);
6498
+ const response = await fetchWithTimeout(ortUrl, 30000);
6428
6499
  const scriptText = await response.text();
6429
6500
 
6430
6501
  // Create a blob URL for the script
@@ -6466,7 +6537,7 @@ async function loadModel(modelUrl, externalDataUrl, isIOS) {
6466
6537
  session = await ort.InferenceSession.create(modelUrl, sessionOptions);
6467
6538
  } else {
6468
6539
  // Desktop: fetch model graph as ArrayBuffer
6469
- const graphResponse = await fetch(modelUrl);
6540
+ const graphResponse = await fetchWithTimeout(modelUrl, 120000);
6470
6541
  if (!graphResponse.ok) {
6471
6542
  throw new Error('Failed to fetch model graph: ' + graphResponse.status + ' ' + graphResponse.statusText);
6472
6543
  }
@@ -6474,7 +6545,7 @@ async function loadModel(modelUrl, externalDataUrl, isIOS) {
6474
6545
 
6475
6546
  // Fetch external data file if present
6476
6547
  if (externalDataUrl && dataFilename) {
6477
- const dataResponse = await fetch(externalDataUrl);
6548
+ const dataResponse = await fetchWithTimeout(externalDataUrl, 120000);
6478
6549
  if (!dataResponse.ok) {
6479
6550
  throw new Error('Failed to fetch external data: ' + dataResponse.status + ' ' + dataResponse.statusText);
6480
6551
  }
@@ -7609,6 +7680,13 @@ var WORKER_SCRIPT4 = `
7609
7680
 
7610
7681
  var ort = null;
7611
7682
  var session = null;
7683
+
7684
+ function fetchWithTimeout(url, timeoutMs) {
7685
+ var controller = new AbortController();
7686
+ var timer = setTimeout(function() { controller.abort(); }, timeoutMs);
7687
+ return fetch(url, { signal: controller.signal }).finally(function() { clearTimeout(timer); });
7688
+ }
7689
+
7612
7690
  var sampleRate = 16000;
7613
7691
  var chunkSize = 512;
7614
7692
  var contextSize = 64;
@@ -7624,7 +7702,7 @@ async function loadOrt(wasmPaths) {
7624
7702
  const ortUrl = wasmPaths + 'ort.wasm.min.js';
7625
7703
 
7626
7704
  // Load the script by fetching and executing it
7627
- const response = await fetch(ortUrl);
7705
+ const response = await fetchWithTimeout(ortUrl, 30000);
7628
7706
  const scriptText = await response.text();
7629
7707
 
7630
7708
  // Create a blob URL for the script
@@ -7654,7 +7732,7 @@ async function loadModel(modelUrl, sr) {
7654
7732
  contextSize = sr === 16000 ? 64 : 32;
7655
7733
 
7656
7734
  // Fetch model data
7657
- const response = await fetch(modelUrl);
7735
+ const response = await fetchWithTimeout(modelUrl, 60000);
7658
7736
  if (!response.ok) {
7659
7737
  throw new Error('Failed to fetch model: ' + response.status + ' ' + response.statusText);
7660
7738
  }