@degreesign/chat 0.3.7 → 0.4.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/README.md CHANGED
@@ -37,6 +37,8 @@ package loads one model, streams one reply and reports status.
37
37
  `startLLM` runtime contract.
38
38
  - Images: a user turn can carry image URLs, sent only to a vision model and
39
39
  capped by the prompt budgets.
40
+ - Audio: a user turn can carry an audio URL, sent only to a model with audio
41
+ support and capped by the prompt budgets.
40
42
  - Reusable: the loaded model stays in memory for the next call.
41
43
  - Typed: written in TypeScript with declarations for every export.
42
44
  - Portable: importing and calling works in browsers, workers, React and Node.
@@ -125,6 +127,13 @@ WebGPU engine asks LiteRT for the smallest Gemma 4 vision budget (70 tokens,
125
127
  630 patches), so it encodes the crop at 384 x 384 instead of upscaling it to
126
128
  768 x 768. The CPU engine uses the 288 crop directly.
127
129
 
130
+ Attach a recording with `audio: [dataUrl]`. Gemma 4 E2B accepts WAV, MP3 and
131
+ FLAC on the WebGPU engine and loads its audio encoder on demand. A short 16 kHz
132
+ mono WAV is the best input. `limits.maxAudio` caps the clips (default 1). A
133
+ model without audio drops the clip when the turn also has text; an audio-only
134
+ turn reports `Model does not accept audio`. The package does not transcode, so
135
+ the host supplies one of those formats.
136
+
128
137
  The package never writes storage, so the host appends the user message before
129
138
  the call and appends the assistant message after the last `onResponse`.
130
139
 
@@ -185,15 +194,17 @@ CPU, not the GPU. Remove it when measuring speed. Headless Chromium hides
185
194
  `--enable-features=Vulkan,WebGPU` and `--use-angle=vulkan`; check the adapter
186
195
  there too, because a missing adapter silently selects the CPU model.
187
196
 
188
- ### GPU memory and images
197
+ ### GPU memory, images and audio
189
198
 
190
- The WebGPU engine keeps the model, its KV cache and the vision encoder in GPU
191
- memory. A card that is already busy can lose the device (`VK_ERROR_DEVICE_LOST`)
192
- on the first image turn. The package requests the smallest Gemma 4 vision
193
- budget, 70 tokens and 630 patches, so the encoder runs one small signature and
194
- the crop is resized to 384 x 384 rather than the full 768 x 768. If the error
195
- still appears, close other GPU tabs, unload the model, or switch to the CPU
196
- model. A lost device needs a page or model reload; later calls cannot reuse it.
199
+ The WebGPU engine keeps the model, its KV cache, the vision encoder and the
200
+ audio encoder in GPU memory. The vision encoder loads on the first image turn
201
+ and the audio encoder loads on the first audio turn. A card that is already busy
202
+ can lose the device (`VK_ERROR_DEVICE_LOST`) on the first image turn. The
203
+ package requests the smallest Gemma 4 vision budget, 70 tokens and 630 patches,
204
+ so the encoder runs one small signature and the crop is resized to 384 x 384
205
+ rather than the full 768 x 768. If the error still appears, close other GPU
206
+ tabs, unload the model, or switch to the CPU model. A lost device needs a page
207
+ or model reload; later calls cannot reuse it.
197
208
 
198
209
  ## Public API
199
210
 
@@ -222,8 +233,8 @@ requested model is already loaded, no download or load happens.
222
233
  Without `model` the package picks the GPU build when the browser provides a
223
234
  WebGPU adapter and the CPU build when it does not. The budget defaults are 250
224
235
  reply tokens, 128 CPU thinking tokens, 1200 GPU reasoning tokens (320 on the
225
- CPU), 2 images, 1000 characters per message, 10000 prompt characters and 6 CPU
226
- threads.
236
+ CPU), 2 images, 1 audio clip, 1000 characters per message, 10000 prompt
237
+ characters and 6 CPU threads.
227
238
 
228
239
  `CancelChat` is `() => void`. Its meaning depends on the current state:
229
240
 
@@ -273,6 +284,7 @@ on failure. A preload owns no cancel function; a new load replaces it.
273
284
  | `status` | `LlmStatus`: `idle`, `downloading`, `ready`, `error` or `unsupported`. |
274
285
  | `backend` | `LlmBackend.cpu`, `.gpu` or `.none`. |
275
286
  | `supportsImages` | Whether the requested model accepts images. |
287
+ | `supportsAudio` | Whether the requested model accepts audio. |
276
288
  | `ready` | Whether a model is loaded and accepts a reply. |
277
289
  | `loading` | Whether a download or load is in flight. |
278
290
  | `requesting` | Whether a reply is streaming right now. |
@@ -296,10 +308,10 @@ without it goes quiet again.
296
308
 
297
309
  ## Models
298
310
 
299
- | `ChatModelId` | Name | Reasoning | Images | CPU | WebGPU |
300
- | ------------- | ---- | --------- | ------ | --- | ------ |
301
- | `qwen3_vl_2b` | Qwen3-VL 2B | Yes | Yes | Yes | No |
302
- | `gemma4_e2b` | Gemma 4 E2B | Yes | Yes | No | Yes |
311
+ | `ChatModelId` | Name | Reasoning | Images | Audio | CPU | WebGPU |
312
+ | ------------- | ---- | --------- | ------ | ----- | --- | ------ |
313
+ | `qwen3_vl_2b` | Qwen3-VL 2B | Yes | Yes | No | Yes | No |
314
+ | `gemma4_e2b` | Gemma 4 E2B | Yes | Yes | Yes | No | Yes |
303
315
 
304
316
  The first call downloads the model before its engine loads it. Later calls
305
317
  reuse the loaded runtime. Only one model stays in memory. An interrupted
@@ -310,13 +322,13 @@ Qwen3-VL 2B downloads a vision projector of about 445 MB with the model, so its
310
322
  total download is about 1.5 GB. It runs on the CPU engine.
311
323
 
312
324
  Gemma 4 E2B downloads one `gemma-4-E2B-it.litertlm` file of about 2.6 GB. It
313
- runs on the WebGPU engine with its vision encoder in the same file, so an
314
- image prompt does not download a projector.
325
+ runs on the WebGPU engine with its vision and audio encoders in the same file,
326
+ so an image or audio prompt does not download a projector.
315
327
 
316
328
  The WebGPU engine keeps one LiteRT conversation for consecutive turns. When the
317
329
  next prompt continues that history, only the new user turn is sent. A changed
318
- history opens a fresh conversation, and older images become `[image]` notes in
319
- its preface.
330
+ history opens a fresh conversation, and older images and audio become `[image]`
331
+ and `[audio]` notes in its preface.
320
332
 
321
333
  ## Prompt assembly
322
334
 
@@ -333,20 +345,24 @@ user: {next turn}
333
345
  - Leading assistant turns are dropped, because the transcript must start with a
334
346
  user turn.
335
347
  - Consecutive same-role turns are merged.
336
- - Messages with no text and no images are skipped.
348
+ - Messages with no text, images or audio are skipped.
337
349
  - `maxContentLength` clips each message and `promptCharBudget` bounds the whole
338
350
  prompt.
339
351
  - System messages are folded into one leading system turn.
340
- - User turns may carry `images`. They are sent only to a model with vision
341
- support.
352
+ - User turns may carry `images` or `audio`. They are sent only to a model with
353
+ vision or audio support.
342
354
  - Images are collected from the newest turns first and capped by `maxImages`
343
355
  (default 2). Older image parts are dropped and their text stays.
344
356
  - Every image part is center-cropped to a square, then resized to 288 x 288.
345
357
  Non-square edges are discarded and smaller images are upscaled. The WebGPU
346
358
  engine is told to use a 70-token vision budget, so it encodes the crop at
347
359
  384 x 384, the smallest Gemma 4 encoder signature.
348
- - Image payloads do not count against `promptCharBudget`.
349
- - An image-only turn with a text model reports `Model does not accept images`.
360
+ - Audio is collected from the newest turns first and capped by `maxAudio`
361
+ (default 1). Older audio parts are dropped and their text stays. WAV, MP3 and
362
+ FLAC are accepted, and a short 16 kHz mono WAV is the best input.
363
+ - Image and audio payloads do not count against `promptCharBudget`.
364
+ - An image-only turn with a text model reports `Model does not accept images`,
365
+ and an audio-only turn reports `Model does not accept audio`.
350
366
 
351
367
  ## Engines and assets
352
368
 
@@ -488,9 +504,9 @@ const useReply = () => {
488
504
  | `ChatInput` | One request: model, messages, budgets, reasoning and callbacks. |
489
505
  | `ChatPreloadInput` | Model, limits, logging and status, the subset `preloadModel` needs. |
490
506
  | `CancelChat` | The function returned by `createChat`. |
491
- | `ChatMessage` | One prompt turn, `role`, `content` and optional `images`. |
507
+ | `ChatMessage` | One prompt turn, `role`, `content` and optional `images` and `audio`. |
492
508
  | `ChatStatusSnapshot` | Availability, engine status, backend, progress and request state. |
493
- | `LlmLimits` | Token, prompt, image and thread budgets, all overridable. |
509
+ | `LlmLimits` | Token, prompt, image, audio and thread budgets, all overridable. |
494
510
  | `WllamaAssets` | Wasm, compat wasm and compat worker URLs. |
495
511
 
496
512
  The public enums are `ChatModelId`, `LlmRole`, `ChatStatus`, `LlmStatus`,
@@ -524,6 +540,15 @@ crop at 384 x 384 with the smallest Gemma 4 vision budget. A model without
524
540
  vision drops images when the turn also has text; an image-only turn reports
525
541
  `Model does not accept images`.
526
542
 
543
+ ### Does it support audio?
544
+
545
+ Yes, on models with audio support. Put an audio URL in a user turn's `audio`
546
+ array. Gemma 4 E2B accepts WAV, MP3 and FLAC on the WebGPU engine, and a short
547
+ 16 kHz mono WAV is the best input. `limits.maxAudio` caps the clips (default 1).
548
+ The package does not transcode, so the host supplies one of those formats. A
549
+ model without audio drops the clip when the turn also has text; an audio-only
550
+ turn reports `Model does not accept audio`.
551
+
527
552
  ### Does it need a server?
528
553
 
529
554
  No. Local chat, model download and inference work without a server. The package
@@ -546,9 +571,9 @@ when an older version stored the file. The package stores no model data itself.
546
571
  ### Which model should I pick?
547
572
 
548
573
  Qwen3-VL 2B supports reasoning and images on the CPU. Gemma 4 E2B supports
549
- reasoning and images on WebGPU, with much faster image prompts but a larger
550
- download and a slower first compile. Use Qwen for broad browser support and
551
- Gemma when the host has Chromium WebGPU.
574
+ reasoning, images and audio on WebGPU, with much faster media prompts but a
575
+ larger download and a slower first compile. Use Qwen for broad browser support
576
+ and Gemma when the host has Chromium WebGPU.
552
577
 
553
578
  ## Licensing
554
579
 
@@ -1,2 +1,2 @@
1
1
  /*! For license information please see chat.min.mjs.LICENSE.txt */
2
- var e={};const t={};function n(a){const o=t[a];if(void 0!==o)return o.exports;const r=t[a]={exports:{}};return e[a](r,r.exports,n),r.exports}var a,o,r,i,s,c;n.m=e,n.d=(e,t)=>{for(var a in t)n.o(t,a)&&!n.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:t[a]})},n.f={},n.e=e=>{const t=[];return n.f.j(e,t),Promise.all(t)},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.r=e=>{Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{const e={520:()=>import("./engine-litert.min.mjs"),924:()=>import("./engine-wllama.min.mjs")},t={792:0},a=e=>{let{__webpack_esm_ids__:a,__webpack_esm_modules__:o,__webpack_esm_runtime__:r}=e;var i,s,c=0;for(i in o)n.o(o,i)&&(n.m[i]=o[i]);for(r&&r(n);c<a.length;c++)s=a[c],n.o(t,s)&&t[s]&&t[s][0](),t[s]=0};n.f.j=(o,r)=>{let i=n.o(t,o)?t[o]:void 0;if(0!==i)if(i)r.push(i[1]);else{let n=e[o]().then(a,e=>{throw 0!==t[o]&&(t[o]=void 0),e});n=Promise.race([n,new Promise(e=>i=t[o]=[e])]),r.push(i[1]=n)}}})(),function(e){e.system="system",e.user="user",e.assistant="assistant"}(a||(a={})),function(e){e[e.online=0]="online",e[e.away=1]="away",e[e.offline=2]="offline"}(o||(o={})),function(e){e[e.idle=0]="idle",e[e.downloading=1]="downloading",e[e.ready=2]="ready",e[e.error=3]="error",e[e.unsupported=4]="unsupported"}(r||(r={})),function(e){e.cpu="cpu",e.gpu="gpu",e.none="none"}(i||(i={})),function(e){e.download="download",e.load="load"}(s||(s={})),function(e){e.qwen3_vl_2b="qwen3-vl-2b",e.gemma4_e2b="gemma4-e2b"}(c||(c={}));const l=[{id:c.qwen3_vl_2b,reasoning:!0,cpu:{repo:"Qwen/Qwen3-VL-2B-Thinking-GGUF",file:"Qwen3VL-2B-Thinking-Q4_K_M.gguf",mmproj:{file:"mmproj-Qwen3VL-2B-Thinking-Q8_0.gguf"}}},{id:c.gemma4_e2b,reasoning:!0,litert:{repo:"litert-community/gemma-4-E2B-it-litert-lm",file:"gemma-4-E2B-it.litertlm",vision:!0}}],d=e=>e?l.find(t=>t.id==e):void 0,g=async()=>(await(async()=>{const e="undefined"==typeof navigator?void 0:navigator.gpu;if(!e)return!1;try{return!!await e.requestAdapter({powerPreference:"high-performance"})}catch{return!1}})()?l.find(e=>!!e.litert):l.find(e=>!!e.cpu))?.id,u=async e=>d(e||await g()),m=e=>!!e.cpu?.mmproj||!!e.litert?.vision,f=new URL("./../assets/wllama/wllama.wasm",import.meta.url).href,p=new URL("./../assets/wllama/compat/wllama.wasm",import.meta.url).href,w=new URL("./../assets/wllama/compat/wllama.js",import.meta.url).href,h=new URL("./../assets/litert/litertlm_wasm_internal.js",import.meta.url).href,y=(new URL("./../assets/litert/litertlm_wasm_internal.wasm",import.meta.url).href,288),b=async e=>{if(!e.startsWith("data:"))return await(await fetch(e)).arrayBuffer();const t=e.indexOf(","),n=t>=0?e.slice(0,t):"";if(t<0)throw new Error("Invalid image data");if(!n.includes(";base64"))return await(await fetch(e)).arrayBuffer();const a=atob(e.slice(t+1)),o=new Uint8Array(a.length);for(let e=0;e<a.length;e++)o[e]=a.charCodeAt(e);return o.buffer},v=async e=>{if(!e||"function"!=typeof fetch||"function"!=typeof createImageBitmap)return e;try{const t=await fetch(e),n=await createImageBitmap(await t.blob()),a=Math.min(n.width,n.height),o=Math.floor((n.width-a)/2),r=Math.floor((n.height-a)/2),i=((e,t)=>{if("undefined"!=typeof OffscreenCanvas)return new OffscreenCanvas(e,t);if("undefined"==typeof document)return;const n=document.createElement("canvas");return n.width=e,n.height=t,n})(y,y),s=i?i.getContext("2d"):null;if(!i||!s||a<1)return n.close(),e;if(s.drawImage(n,o,r,a,a,0,0,y,y),n.close(),(e=>"undefined"!=typeof OffscreenCanvas&&e instanceof OffscreenCanvas)(i)){const t=await i.convertToBlob({type:"image/jpeg",quality:.85});return await(e=>new Promise(t=>{if("undefined"==typeof FileReader)return void t("");const n=new FileReader;n.onerror=()=>t(""),n.onload=()=>t(`${n.result}`),n.readAsDataURL(e)}))(t)||e}return i.toDataURL("image/jpeg",.85)}catch{return e}},x={maxReplyTokens:250,maxThinkingTokens:128,maxReasoningTokens:320,maxImages:2,maxContentLength:1e3,promptCharBudget:1e4,maxThreads:6},k=(e,t,n)=>"number"==typeof e&&Number.isFinite(e)&&e>=n?Math.floor(e):t,_=(e={},t=!1)=>{const n=x;return{maxReplyTokens:k(e.maxReplyTokens,n.maxReplyTokens,1),maxThinkingTokens:k(e.maxThinkingTokens,n.maxThinkingTokens,1),maxReasoningTokens:k(e.maxReasoningTokens,t?1200:n.maxReasoningTokens,1),maxImages:k(e.maxImages,n.maxImages,0),maxContentLength:k(e.maxContentLength,n.maxContentLength,1),promptCharBudget:k(e.promptCharBudget,n.promptCharBudget,1),maxThreads:k(e.maxThreads,n.maxThreads,1)}},M=(e,t)=>({stream:!0,temperature:.7,max_tokens:t?e.maxReasoningTokens:e.maxReplyTokens}),T=(e,t)=>`https://huggingface.co/${e}/resolve/main/${t}`,E=e=>Number(e.headers.get("content-length"))||0,C=e=>e.some(e=>Array.isArray(e.content)&&e.content.some(e=>"image"==e.type)),L=["<|channel>thought\n","<|channel>thought","..."],P=["<channel|>","..."],R=(e,t)=>t.length?[...e?[{type:"text",text:e}]:[],...t.map(e=>({type:"image",url:e}))]:e,$=e=>"string"==typeof e?e?[{type:"text",text:e}]:[]:e,S=(e,t)=>{if("string"==typeof e&&"string"==typeof t)return`${e}\n${t}`;const n=[];for(const a of[...$(e),{type:"text",text:"\n"},...$(t)]){const e=n[n.length-1];"text"==a.type&&"text"==e?.type?e.text+=a.text:n.push(a)}return n};class j extends Error{}const W=async(e,t,n)=>{let a;try{return await Promise.race([e,new Promise((e,o)=>{a=setTimeout(()=>o(new j(n)),t)})])}finally{a&&clearTimeout(a)}},q=e=>{try{Promise.resolve(e.return?.()).catch(()=>{})}catch{}},z=async({model:e,logging:t,onReason:n,onText:a,onSpeed:o,start:r,stopped:i,firstTimeoutMs:s=18e4,idleTimeoutMs:c=3e4,onStall:l})=>{let d="",g="",u="",m=!1,f=0,p=0;const w=((e,t)=>{let n="",a=!1;const o=(e,t)=>{let n=-1,a="";for(const o of t){const t=e.indexOf(o);t>=0&&(n<0||t<n)&&(n=t,a=o)}return{at:n,tag:a}},r=(e,t)=>{let n="";for(const a of t)for(let t=Math.min(e.length,a.length-1);t>n.length;t--)if(e.endsWith(a.slice(0,t))){n=e.slice(e.length-t);break}return n};return{feed:i=>{let s=n+i;for(n="";s;){if(a){const{at:t,tag:i}=o(s,P);if(t>=0){t&&e(s.slice(0,t)),s=s.slice(t+i.length),a=!1;continue}const c=r(s,P);e(s.slice(0,s.length-c.length)),n=c;break}{const{at:e,tag:i}=o(s,L);if(e>=0){e&&t(s.slice(0,e)),s=s.slice(e+i.length),s.startsWith("\n")&&(s=s.slice(1)),a=!0;continue}const c=r(s,L);t(s.slice(0,s.length-c.length)),n=c;break}}},flush:()=>{n&&(a?e(n):t(n),n="")}}})(e=>{g+=e,n(e)},e=>{d+=e,a(e)});try{const e=(await W(r(),s,"Model did not start"))[Symbol.asyncIterator]();for(;;){if(i()){q(e);break}let a;try{a=await W(e.next(),m?c:s,m?"Model stopped responding":"Model did not reply")}catch(n){if(n instanceof j&&(d||g)){l?.(),q(e),t&&console.log("Local chat stalled",n);break}throw n}if(a.done)break;m=!0;const r=a.value?.choices?.[0],h=r?.delta,y="string"==typeof h?.reasoning_content?h.reasoning_content:"string"==typeof h?.reasoning?h.reasoning:"";if(r?.finish_reason&&(u=r.finish_reason),y&&(g+=y,n(y)),h?.content&&w.feed(h.content),y||h?.content){const e=performance.now();f||(p=e),f+=1,f>1&&e>p&&o?.(1e3*f/(e-p))}}w.flush()}catch(e){if(t&&console.log("Local chat failed",e),e instanceof j&&!i())throw e}const h=i()?void 0:d||void 0;return t&&(console.log("Local chat reply",e.id,h?"****":""),console.log("Local chat finish",e.id,u||"none",d.length,g.length)),h},B=async({model:e,params:t,start:n,stopped:o,onStall:r})=>{const i=await(async e=>{const t=[];for(const n of e){if("string"==typeof n.content){t.push(n);continue}const e=[];for(const t of n.content)e.push("image"==t.type?{type:"image",url:await v(t.url)}:t);t.push({role:"user",content:e})}return t})(((e,t=x)=>{const n=t.maxContentLength,o=e.filter(e=>"string"==typeof e.content&&!!e.content.trim()||!(e.role!=a.user||!e.images?.length)),r=[],i=[];let s=0,c=t.maxImages;for(let e=o.length-1;e>=0;e--){const l=o[e],d=("string"==typeof l.content?l.content:"").slice(0,n),g=l.role==a.user?(l.images||[]).filter(e=>"string"==typeof e&&!!e).slice(0,Math.max(0,c)):[];if(d||g.length){if(d&&s+d.length>t.promptCharBudget)break;s+=d.length,c-=g.length,l.role==a.system?r.unshift(d):l.role==a.assistant?i.unshift({role:"assistant",content:d}):i.unshift({role:"user",content:R(d,g)})}}for(;i.length&&"user"!=i[0].role;)i.shift();const l=[];for(const e of i){const t=l[l.length-1];"assistant"==t?.role&&"assistant"==e.role?t.content=`${t.content}\n${e.content}`:"user"==t?.role&&"user"==e.role?t.content=S(t.content,e.content):"assistant"==e.role?l.push({role:"assistant",content:e.content}):l.push({role:"user",content:e.content})}const d=r.join("\n\n"),g=[];return d&&g.push({role:"system",content:d}),g.push(...l),g})(t.messages,t.limits));return t.logging&&((e,t)=>{console.log("Local chat call",e.id,t.map(({role:e,content:t})=>({role:e,content:"****",...Array.isArray(t)?{images:t.filter(e=>"image"==e.type).length}:{}})))})(e,i),await z({model:e,logging:t.logging,onReason:t.onReason,onText:t.onText,onSpeed:t.onSpeed,start:()=>n(i),stopped:o,onStall:r})},I=33554432,A=new Map;let O=!1;const N=e=>{const t=(e.split("/").pop()||"model").replace(/[^\w.-]+/g,"_");let n=0;for(let t=0;t<e.length;t++)n=31*n+e.charCodeAt(t)>>>0;return`${n.toString(16)}-${t}`},U=e=>{const t=/^part-(\d+)$/.exec(e);return t?Number(t[1]):-1},F=async()=>{if(!O)if("undefined"!=typeof navigator&&navigator.storage?.getDirectory)try{return await navigator.storage.getDirectory()}catch{return}else O=!0},D=async(e,t)=>{try{return await e.getDirectoryHandle(t)}catch{return}},H=async e=>{if(e)try{const t=await e.getFile(),n=JSON.parse(await t.text());return{total:Number(n.total)||0,etag:`${n.etag||""}`,complete:!0===n.complete}}catch{return}},Q=async(e,t)=>{try{const n=await e.getFileHandle("meta.json",{create:!0}),a=await n.createWritable();await a.write(JSON.stringify(t)),await a.close()}catch{}},V=async function*(e){for await(const t of e)yield t},G=async e=>{try{for await(const[t,n]of V(e))"file"==n.kind&&U(t)>=0&&await e.removeEntry(t).catch(()=>{})}catch{}},J=async(e,t)=>{try{const n=await(await e.getFileHandle(t)).getFile(),a=await H(await e.getFileHandle(`${t}.json`).catch(()=>{}));if(!a)return;return{file:n,size:n.size,total:a.total,etag:a.etag,complete:a.total?n.size>=a.total:!0===a.complete}}catch{return}},K=async(e,t)=>{const n=await D(e,t);if(n){const e=await(async e=>await H(await e.getFileHandle("meta.json").catch(()=>{})))(n),t=await(async e=>{const t=[];try{for await(const[n,a]of V(e)){const e=U(n);if("file"!=a.kind||e<0)continue;const o=await a.getFile();t.push({part:{start:e,size:o.size},file:o})}}catch{return}const n=(e=>{const t=[];let n=0;for(const a of[...e].sort((e,t)=>e.start-t.start))if(a.size){if(a.start!=n)break;t.push(a),n+=a.size}return t})(t.map(e=>e.part)),a=new Set(n.map(e=>e.start)),o=t.filter(e=>a.has(e.part.start)).sort((e,t)=>e.part.start-t.part.start).map(e=>e.file);if(o.length)return{file:1==o.length?o[0]:new Blob(o),size:n.reduce((e,t)=>e+t.size,0)}})(n),a=e?.total||0,o=t?.size??0,r=t?.file??new Blob;return{file:a&&o>a?r.slice(0,a):r,size:o,total:a,etag:e?.etag||"",complete:a?o>=a:!0===e?.complete}}return await J(e,t)},X=(e,t)=>{const n=e.headers.get("content-range"),a=/bytes (\d+)-/.exec(n||""),o=/\/(\d+)\s*$/.exec(n||""),r=e.headers.get("etag")||"";if(206==e.status){const e=a?Number(a[1]):t.size,n=o?Number(o[1]):t.total;if(e!=t.size)return;if(t.total&&n&&t.total!=n)return;if(t.etag&&r&&t.etag!=r)return;return{start:e,total:n||t.total,etag:r||t.etag}}if(!t.etag||!r||t.etag==r)return{start:0,total:E(e)||t.total,etag:r||t.etag}},Y=async({dir:e,start:t,skip:n,total:a,body:o,onProgress:r,onChunk:i})=>{let c,l=t,d=0,g=n;const u=async()=>{const t=await e.getFileHandle((e=>`part-${e}`)(l),{create:!0});c=await t.createWritable(),d=0},m=async()=>{const e=c;c=void 0,e&&await e.close().catch(()=>{})};try{const e=o.getReader();for(;;){const{done:n,value:o}=await e.read();if(n)break;i();let f=0;for(g&&(f=Math.min(g,o.byteLength),g-=f);f<o.byteLength;){c||await u();const e=Math.min(I-d,o.byteLength-f);await c.write({type:"write",position:d,data:o.subarray(f,f+e)}),f+=e,d+=e,l+=e,d==I&&await m()}l>t&&r(a?l/a:0,s.download)}await m()}catch(e){throw await m(),e}return l},Z=e=>["NotAllowedError","NotSupportedError"].includes(e?.name||""),ee=e=>{const t=async()=>{const t=await F();if(t)try{return await t.getDirectoryHandle(e,{create:!0})}catch{return}};return{stored:async e=>{const n=await t();return n?await K(n,N(e)):void 0},download:async({url:n,signal:a,onProgress:o})=>{const r=`${e}|${n}`,i=A.get(r);if(i)return await i;const c=await t();if(!c)return;const l=(async(e,t,n,a)=>{const o=N(t),r=await J(e,o);if(r?.complete)return r;let i=await D(e,o);i||(await e.removeEntry(o).catch(()=>{}),await e.removeEntry(`${o}.json`).catch(()=>{}),i=await e.getDirectoryHandle(o,{create:!0}));let c=0,l=0;for(;;){if(n?.aborted)throw new Error("Cancelled");const r=await K(e,o);if(r?.complete)return r;const d=r?.size??0,g=r?.total||0,u=r?.etag||"";if(d>c)c=d,l=0;else if(++l>4)throw new Error("Model download failed");d&&g&&a(d/g,s.download);const m=g?Math.min(d+I,g)-1:d+I-1,f=new AbortController,p=()=>f.abort(),w={size:d,total:g,etag:u};let h,y=setTimeout(p,3e4);n?.addEventListener("abort",p,{once:!0});try{if(h=await fetch(t,{signal:f.signal,headers:{Range:`bytes=${d}-${m}`}}),416==h.status){if(await(h.body?.cancel().catch(()=>{})),g&&d>=g){await Q(i,{total:g,etag:u,complete:!0});continue}await G(i),await Q(i,{total:0,etag:"",complete:!1});continue}if(!h.ok||!h.body)throw await(h.body?.cancel().catch(()=>{})),new Error("Model download failed");const n=X(h,w);if(!n){await h.body.cancel().catch(()=>{}),await G(i),await Q(i,{total:0,etag:"",complete:!1});continue}await Q(i,{total:n.total,etag:n.etag,complete:!1});const r=0==n.start?d:0,s=await Y({dir:i,start:n.start+r,skip:r,total:n.total,body:h.body,onProgress:a,onChunk:()=>{clearTimeout(y),y=setTimeout(p,3e4)}});if(!n.total){await Q(i,{total:s,etag:n.etag,complete:!0});const t=await K(e,o);if(t)return t}}catch(e){if(n?.aborted)throw new Error("Cancelled");if(e instanceof DOMException&&["QuotaExceededError","NotAllowedError","NotSupportedError","InvalidStateError","TypeMismatchError","SecurityError"].includes(e.name))throw e;await new Promise(e=>setTimeout(e,250+500*l))}finally{clearTimeout(y),n?.removeEventListener("abort",p)}}})(c,n,a,o).finally(()=>{A.delete(r)});return A.set(r,l),await l},remove:async e=>{const n=await t();n&&await(async(e,t)=>{await e.removeEntry(t,{recursive:!0}).catch(()=>{}),await e.removeEntry(`${t}.json`).catch(()=>{})})(n,N(e))},purge:async()=>{const t=await F();t&&await t.removeEntry(e,{recursive:!0}).catch(()=>{})}}},te="litertlm-models",ne=ee("litert-store"),ae={num_output_candidates:1,wait_for_weight_uploads:!0,num_decode_steps_per_sync:3,sequence_batch_size:0,supported_lora_ranks:[],max_top_k:40,enable_decode_logits:!1,enable_external_embeddings:!1,use_submodel:!1,use_autosized_ringbuffers:!0};let oe=!1;const re=(...e)=>{oe&&console.log(...e)},ie=(...e)=>{oe&&console.error(...e)},se=async()=>{try{if("undefined"==typeof caches)return;return await caches.open(te)}catch{return}},ce=new Map,le=(e,t,n)=>{let a=0;return e.pipeThrough(new TransformStream({transform:(e,o)=>{a+=e.byteLength,n(t?a/t:0,s.download),o.enqueue(e)}}))},de=async(e,t=!0)=>{const n=[];for(const a of e){if("string"==typeof a.content){n.push({role:a.role,content:a.content});continue}const e=[];for(const n of a.content)e.push("text"==n.type?{type:"text",text:n.text}:t?{type:"image",data:await b(n.url)}:{type:"text",text:"[image]"});n.push({role:a.role,content:e})}return n},ge=(e,t)=>{if(e.role!=t.role)return!1;if("string"==typeof e.content||"string"==typeof t.content)return e.content==t.content;if(e.content.length!=t.content.length)return!1;for(let n=0;n<e.content.length;n++){const a=e.content[n],o=t.content[n];if(a.type!=o.type)return!1;if("text"==a.type&&"text"==o.type){if(a.text!=o.text)return!1}else if("image"==a.type&&"image"==o.type&&a.url!=o.url)return!1}return!0},ue=async function*({engine:e,mod:t,params:n,messages:a,session:o}){const r=a[a.length-1],i=M(n.limits,n.reasoning);if("user"!=r?.role)throw new Error("No user turn");const s=a.slice(0,-1),c=(await de([r]))[0];if(!c)throw new Error("Empty prompt");const l=!!o.conversation&&o.reasoning==n.reasoning&&o.maxTokens==i.max_tokens&&((e,t)=>{const n="system"==e[0]?.role?e[0]:void 0,a="system"==t[0]?.role?t[0]:void 0;if(!n!=!a)return!1;if(n&&a&&!ge(n,a))return!1;const o=n?e.slice(1):e,r=a?t.slice(1):t;if(!r.length||r.length>o.length)return!1;const i=o.length-r.length;for(let e=0;e<r.length;e++)if(!ge(o[i+e],r[e]))return!1;return!0})(o.sent,s);let d;if(l&&o.conversation)d=o.conversation;else{const a=await de(s,!1);o.conversation=void 0,o.sent=[],d=await e.createConversation({sessionConfig:{maxOutputTokens:i.max_tokens,samplerParams:{type:t.SamplerType.TOP_K,temperature:i.temperature,k:40,p:.95}},preface:{...a.length?{messages:a}:{},extra_context:{enable_thinking:n.reasoning}},prefillPrefaceOnInit:a.length>0}),o.conversation=d,o.reasoning=n.reasoning,o.maxTokens=i.max_tokens,o.sent=[...s]}o.sent.push(r);let g,u=!1,m="";const f=()=>{g&&g.cancel().catch(()=>{});try{d.cancel()}catch{}};n.signal?.addEventListener("abort",f,{once:!0});try{if(n.signal?.aborted)return;const e=d.sendMessageStreaming(c).getReader();for(g=e;;){const{done:t,value:n}=await e.read();if(t)break;const a={};let o="string"==typeof n.content?n.content:"";if(Array.isArray(n.content))for(const e of n.content)"text"==e.type&&(o+=e.text);o&&(a.content=o,m+=o);const r=n.channels?.thought;r&&(a.reasoning=r),(a.content||a.reasoning)&&(yield{choices:[{delta:a}]})}u=!0}finally{n.signal?.removeEventListener("abort",f),u&&m?o.sent.push({role:"assistant",content:m}):(o.conversation=void 0,o.sent=[])}},me={backend:i.gpu,startLLM:async({model:e,limits:t,onProgress:a,signal:o,logging:r})=>{oe=!0===r;const c=e.litert;if(!c)throw new Error("Model has no LiteRT artifact");const l=await n.e(520).then(()=>n(507)),d=globalThis,g=d.Module;d.Module={print:re,printErr:ie};try{await l.getOrLoadGlobalLiteRtLm(h)}finally{d.Module=g}if(o?.aborted)throw new Error("Cancelled");const u=T(c.repo,c.file),m=await se();let f;const p=m?await m.match(u):void 0;if(p?.body)f=p.body;else{const e=await ne.stored(u);e?.complete&&(f=e.file.stream())}if(!f){const e=await ne.download({url:u,signal:o,onProgress:a}).catch(e=>{if(o?.aborted)throw new Error("Cancelled");if(!Z(e))throw e;r&&console.log("Local model store failed",e)});e&&(f=e.file.stream())}if(!f&&m)try{await(async(e,t,n,a)=>{const o=ce.get(e);if(o)return await o;const r=(async()=>{const o=await fetch(e,{signal:n});if(!o.ok||!o.body)throw new Error("Model download failed");const r=E(o),i=le(o.body,r,a);await t.put(e,new Response(i,{headers:{"content-type":"application/octet-stream",...r?{"content-length":`${r}`}:{}}}))})().finally(()=>{ce.delete(e)});return ce.set(e,r),await r})(u,m,o,a);const e=await m.match(u);e?.body&&(f=e.body)}catch(e){if(o?.aborted)throw new Error("Cancelled");r&&console.log("Local model cache failed",e)}if(f||(f=await(async(e,t,n)=>{const a=await fetch(e,{signal:t});if(!a.ok||!a.body)throw new Error("Model download failed");return le(a.body,E(a),n)})(u,o,a)),o?.aborted)throw new Error("Cancelled");a(1,s.load);const w=await l.Engine.create((({model:e,limits:t,source:n,mod:a})=>({model:n,mainExecutorSettings:{maxNumTokens:4096,...e.litert?.vision?{maxNumImages:Math.max(1,t.maxImages)}:{},backendConfig:ae},...e.litert?.vision?{visionBackend:a.Backend.GPU,maxVisionTokensPerImage:70,minVisionTokensPerImage:70}:{}}))({model:e,limits:t,source:f,mod:l}));if(o?.aborted)throw await w.delete(),new Error("Cancelled");const y={sent:[],reasoning:!1,maxTokens:0};return{model:e,backend:i.gpu,contextSize:4096,chat:t=>(async({engine:e,model:t,mod:n,params:a,session:o})=>{oe=!0===a.logging;const r=()=>{try{o.conversation?.cancel()}catch{}o.conversation=void 0,o.sent=[]};try{return await B({model:t,params:a,stopped:()=>!!a.signal?.aborted,onStall:r,start:async r=>{if(a.signal?.aborted)throw new Error("Cancelled");if(C(r)&&!t.litert?.vision)throw new Error("Model does not accept images");return ue({engine:e,mod:n,params:a,messages:r,session:o})}})}catch(e){throw r(),e}})({engine:w,model:e,mod:l,params:t,session:y}),unload:async()=>{y.conversation=void 0,y.sent=[],await w.delete()}}},cached:async e=>{const t=e.litert;if(!t)return!1;const n=T(t.repo,t.file),[a,o]=await Promise.all([se(),ne.stored(n)]);if(o?.complete)return!0;try{return!(!a||!await a.match(n))}catch{return!1}},hasCache:async()=>{try{for(const e of l){const t=e.litert;if(!t)continue;if(await ne.stored(T(t.repo,t.file)))return!0}const e=await se();return!!e&&(await e.keys()).length>0}catch{return!1}},remove:async e=>{try{const t=e.litert;if(!t)return;const n=T(t.repo,t.file),a=await se();await ne.remove(n),a&&await a.delete(n)}catch{}},purge:async()=>{try{await ne.purge(),"undefined"!=typeof caches&&await caches.delete(te)}catch{}}},fe=ee("wllama-store");let pe,we,he="",ye=!1;const be=e=>`${e.wasm}|${e.compatWasm}|${e.compatWorker}`,ve=e=>{const t={wasm:f,compatWasm:p,compatWorker:w};return{wasm:e?.wasm||t.wasm,compatWasm:e?.compatWasm||t.compatWasm,compatWorker:e?.compatWorker||t.compatWorker}},xe=e=>{const t=e=>(...t)=>{ye&&("string"==typeof t[0]&&t[0].includes("ggml_webgpu: Failed to get an adapter")||e(...t))};return{...e,log:t(e.log),warn:t(e.warn),error:t(e.error)}},ke=async()=>{if(pe)return pe;const e=await n.e(924).then(()=>n(577)),t=we||ve(),a=new e.Wllama({default:t.wasm},{logger:xe(e.LoggerWithoutDebug)});return we||(we=t,he=be(t)),a.setCompat({wasm:t.compatWasm,worker:t.compatWorker}),pe=a,a},_e=async()=>{const e=pe;pe=void 0;try{await(e?.exit())}catch{}},Me=(e,t)=>e.name.includes(t)||!!e.metadata?.originalURL?.includes(t),Te=e=>{const t=e.cpu;return t?[{name:t.file,url:T(t.repo,t.file)},...t.mmproj?[{name:t.mmproj.file,url:T(t.repo,t.mmproj.file)}]:[]]:[]},Ee=async e=>{const t=Te(e);if(!t.length)return!1;for(const e of t){const t=await fe.stored(e.url);if(!t?.complete)return!1}return!0},Ce=async(e,t)=>{const n=await e.cacheManager.list();return Te(t).every(e=>n.some(t=>Me(t,e.name)))},Le=async({model:e,signal:t,onProgress:n})=>{const a=Te(e),o=[];for(let e=0;e<a.length;e++){if(t?.aborted)throw new Error("Cancelled");const r=await fe.download({url:a[e].url,signal:t,onProgress:(t,o)=>n((e+t)/a.length,o)});if(!r)return;o.push(r.file)}return o.length?o:void 0},Pe=async e=>{const t=[];for(const n of e){if("string"==typeof n.content){"system"==n.role?t.push({role:"system",content:n.content}):"assistant"==n.role?t.push({role:"assistant",content:n.content}):t.push({role:"user",content:n.content});continue}const e=[];for(const t of n.content)e.push("text"==t.type?{type:"text",text:t.text}:{type:"image",data:await b(t.url)});t.push({role:"user",content:e})}return t},Re={backend:i.cpu,startLLM:async({model:e,limits:t,onProgress:n,signal:a,logging:o,assets:r})=>{ye=!0===o;const c=e.cpu;if(!c)throw new Error("Model has no CPU artifact");const l=ve(r),d=be(l);pe&&d!=he&&await _e(),we=l,he=d;const g=(u="undefined"!=typeof navigator&&navigator.hardwareConcurrency||4,m=t.maxThreads,Math.max(1,Math.min(Math.max(2,Math.min(8,(u||4)-1)),m)));var u,m;const f=await ke(),p=await Ee(e),w=c.mmproj?.file,h=T(c.repo,c.file),y=w?T(c.repo,w):"";if(a?.aborted)throw await _e(),new Error("Cancelled");if(!p&&w){const e=await f.cacheManager.list(),t=e.some(e=>Me(e,c.file)),n=e.some(e=>Me(e,w));t&&!n&&await f.cacheManager.deleteMany(e=>Me(e,c.file))}const b={n_ctx:8192,n_threads:g,reasoning_budget_tokens:t.maxThinkingTokens,n_gpu_layers:0,jinja:!0};try{let t=p?await Le({model:e,signal:a,onProgress:n}):void 0;t||p||await Ce(f,e).catch(()=>!1)||(t=await Le({model:e,signal:a,onProgress:n}).catch(e=>{if(a?.aborted)throw new Error("Cancelled");if(!Z(e))throw e;o&&console.log("Local model store failed",e)})),t?(n(1,s.load),await f.loadModel(t,b)):await f.loadModelFromUrl(y?{url:h,mmprojUrl:y}:h,{...b,useCache:!0,signal:a,progressCallback:({loaded:e,total:t})=>{const a=t?e/t:0;n(a,a>=1?s.load:s.download)}})}catch(e){throw await _e(),e}if(a?.aborted)throw await _e(),new Error("Cancelled");if(!f.getLoadedContextInfo().n_ctx)throw await _e(),new Error("Failed to create model context");return{model:e,backend:i.cpu,contextSize:8192,chat:t=>(async(e,t,n)=>{if(ye=!0===n.logging,e.isModelLoaded())return await B({model:t,params:n,stopped:()=>!!n.signal?.aborted,start:async a=>{if(C(a)&&!e.supportInputModality("image"))throw new Error("Model does not accept images");return await e.createChatCompletion({messages:await Pe(a),...M(n.limits,n.reasoning),abortSignal:n.signal,...t.reasoning?{chat_template_kwargs:{enable_thinking:n.reasoning}}:{}})}})})(f,e,t),unload:async()=>{if(pe==f)await _e();else try{await f.exit()}catch{}}}},cached:async e=>{try{return!!e.cpu&&(!!await Ee(e)||await Ce(await ke(),e))}catch{return!1}},hasCache:async()=>{try{for(const e of l.flatMap(e=>Te(e)))if(await fe.stored(e.url))return!0;const e=await ke();return(await e.cacheManager.list()).length>0}catch{return!1}},remove:async e=>{const t=Te(e);try{for(const e of t)await fe.remove(e.url)}catch{}try{const e=await ke();await e.cacheManager.deleteMany(e=>t.some(t=>Me(e,t.name)))}catch{}},purge:async()=>{try{await fe.purge()}catch{}try{const e=await ke();await e.cacheManager.clear()}catch{}}},$e=((e={})=>{const t=e.engines?.cpu||Re,n=e.engines?.litert||me;let a,o,c,d,g,u,m,f=i.none,p=r.idle,w=[],h=0,y={percent:0,phase:s.download};const b=a=>{if(e.backend!=i.none)return e.backend==i.cpu?a.cpu?t:void 0:e.backend==i.gpu?a.litert?n:void 0:a.litert&&"undefined"!=typeof navigator&&navigator.gpu&&"undefined"!=typeof WebAssembly?n:!a.cpu||"undefined"==typeof window&&"undefined"==typeof self&&"undefined"==typeof Worker||"undefined"==typeof WebAssembly?void 0:t},v=(e,t)=>{y={percent:Math.max(0,Math.min(1,e)),phase:t||s.download};for(const e of[...w])e.report(y.percent,y.phase)},x=e=>{p=e},k=()=>{h++,u?.abort(),u=void 0,g=void 0,p==r.downloading&&x(r.idle)},_=(e,t)=>{const n=d;return n?new Promise(a=>{let o=!1;const r={report:e,settle:e=>{o||(o=!0,t?.removeEventListener("abort",i),a(e))}},i=()=>{(e=>{w=w.filter(t=>t!=e),queueMicrotask(()=>{!w.length&&d&&k()})})(r),r.settle(!1)};w.push(r),t?.aborted?i():t?.addEventListener("abort",i,{once:!0}),n.then(r.settle,()=>r.settle(!1))}):Promise.resolve(!1)},M=()=>p==r.ready&&!!a&&!!o;return{load:async({model:e,limits:t,assets:n,logging:i,signal:m,onProgress:p})=>{if(m?.aborted)return!1;if(M()&&o?.id==e.id)return!0;if(d&&g==e.id)return p(y.percent,y.phase),await _(p,m);d&&(k(),w=[],await d.catch(()=>!1));const T=++h,E=(async()=>{await(async()=>{try{if("undefined"==typeof navigator)return;await(navigator.storage?.persist?.())}catch{}})();const d=b(e);if(T!=h)return!1;if(!d)return x(r.unsupported),!1;if(await(async(e,t)=>{if(o&&o.id!=e.id){const e=o,t=c,n=a;a=void 0,o=void 0,c=void 0,await(n?.unload()),await(t?.remove(e))}if(await t.hasCache())for(const n of l)n.id!=e.id&&b(n)==t&&await t.remove(n)})(e,d),T!=h)return!1;const g=await d.cached(e);if(T!=h)return!1;x(r.downloading),v(0,s.download),g&&v(1,s.load);const m=new AbortController;u=m;try{const l=await d.startLLM({model:e,limits:t,logging:i,signal:m.signal,assets:n,onProgress:(e,t)=>{T==h&&v(e,t)}});return T!=h||m.signal.aborted?(await l.unload(),!1):(a=l,o=e,c=d,f=d.backend,x(r.ready),v(1,s.load),!0)}catch(e){return T!=h||(a=void 0,o=void 0,c=void 0,i&&console.log("Local model failed",e),x(r.error)),!1}finally{u==m&&(u=void 0)}})().finally(()=>{d==E&&(d=void 0,g=void 0,w=[])});return d=E,g=e.id,await _(p,m)},chat:async e=>{if(!M()||!a)return;for(;m;)await m.catch(()=>{});if(!M()||!a)return;const t=a.chat(e);m=t;try{return await t}finally{m==t&&(m=void 0)}},unload:async()=>{const e=a;k(),a=void 0,o=void 0,c=void 0,x(r.idle),await(e?.unload())},ready:M,loading:()=>!M()&&p==r.downloading,status:()=>p,backend:()=>f,model:()=>o}})(),Se=(e,t,n)=>{const a=e.status(),i=d(t);return{availability:a==r.ready?o.online:a==r.error||a==r.unsupported?o.offline:o.away,status:a,backend:e.backend(),supportsImages:!!i&&m(i),ready:e.ready(),loading:e.loading(),requesting:n.requesting,phase:n.phase,percent:n.percent,...n.error?{error:n.error}:{}}},je=(e=>{let t,n=0;const o=t=>t.input.onStatus?.(Se(e,t.model??t.input.model,t));return i=>{t?.controller.abort();const c={token:++n,input:i,controller:new AbortController,percent:e.ready()?100:0,phase:e.ready()?s.load:s.download,requesting:!1,cancelled:!1};return t=c,o(c),(async t=>{const{input:r}=t,i=!0===r.logging,c=await u(r.model);if(t.model=c?.id,t.token!=n)return;const l=r.messages.some(e=>e.role!=a.system&&"string"==typeof e.content&&!!e.content.trim()),d=r.messages.some(e=>e.role==a.user&&!!e.images?.some(e=>"string"==typeof e&&!!e)),g=l||d;if(!c)return t.error="Unknown model",void o(t);if(!g)return void o(t);if(d&&!l&&!m(c))return t.error="Model does not accept images",void o(t);const f=_(r.limits,!!c.litert),p=await e.load({model:c,limits:f,logging:i,signal:t.controller.signal,onProgress:(e,a)=>{t.token==n&&(t.percent=Math.round(100*e),t.phase=a,o(t))}}).catch(e=>(t.token==n&&(t.error=`${e?.message||`${e}`}`),!1));if(t.token!=n)return;if(t.cancelled)return;if(!p)return t.error||(t.error="Model not ready"),void o(t);const w=m(c),h=d&&!w?r.messages.map(({images:e,...t})=>t):r.messages;if(d&&!w&&!l)return t.error="Model does not accept images",void o(t);t.requesting=!0,t.error=void 0,t.percent=100,t.phase=s.load,o(t);let y,b="",v="";try{y=await e.chat({messages:h,limits:f,reasoning:!0===r.reasoning,logging:i,onReason:e=>{t.token!=n||t.cancelled||(b+=e,r.onThinking?.(b))},onText:e=>{t.token!=n||t.cancelled||(v+=e,r.onResponse?.(v))},onSpeed:e=>{t.token!=n||t.cancelled||r.onSpeed?.(e)},signal:t.controller.signal})}catch(e){if(t.token!=n)return;return t.requesting=!1,t.error=`${e?.message||`${e}`}`,void o(t)}t.token==n&&(t.requesting=!1,y||t.cancelled||(t.error="No reply"),o(t))})(c),()=>(t=>{t.token==n&&(t.cancelled=!0,t.controller.abort(),e.status()!=r.ready?queueMicrotask(()=>o(t)):e.unload().then(()=>o(t)))})(c)}})($e),We=(qe=$e,async(e={})=>{const t=await u(e.model),n=n=>e.onStatus?.(Se(qe,t?.id,n));if(!t)return n({requesting:!1,phase:s.download,percent:0,error:"Unknown model"}),!1;let a,o=0,r=s.download;const i=qe.ready()&&qe.model()?.id==t.id;return await qe.load({model:t,limits:_(e.limits,!!t.litert),logging:!0===e.logging,onProgress:(e,t)=>{o=Math.round(100*e),r=t,n({requesting:!1,phase:r,percent:o})}}).catch(e=>(a=`${e?.message||`${e}`}`,!1))?(i&&n({requesting:!1,phase:s.load,percent:100}),!0):(n({requesting:!1,phase:r,percent:o,error:a||"Model not ready"}),!1)});var qe;export{c as ChatModelId,o as ChatStatus,i as LlmBackend,s as LlmPhase,a as LlmRole,r as LlmStatus,je as createChat,We as preloadModel};
2
+ var e={};const t={};function n(a){const o=t[a];if(void 0!==o)return o.exports;const r=t[a]={exports:{}};return e[a](r,r.exports,n),r.exports}var a,o,r,i,s,c;n.m=e,n.d=(e,t)=>{for(var a in t)n.o(t,a)&&!n.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:t[a]})},n.f={},n.e=e=>{const t=[];return n.f.j(e,t),Promise.all(t)},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.r=e=>{Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{const e={520:()=>import("./engine-litert.min.mjs"),924:()=>import("./engine-wllama.min.mjs")},t={792:0},a=e=>{let{__webpack_esm_ids__:a,__webpack_esm_modules__:o,__webpack_esm_runtime__:r}=e;var i,s,c=0;for(i in o)n.o(o,i)&&(n.m[i]=o[i]);for(r&&r(n);c<a.length;c++)s=a[c],n.o(t,s)&&t[s]&&t[s][0](),t[s]=0};n.f.j=(o,r)=>{let i=n.o(t,o)?t[o]:void 0;if(0!==i)if(i)r.push(i[1]);else{let n=e[o]().then(a,e=>{throw 0!==t[o]&&(t[o]=void 0),e});n=Promise.race([n,new Promise(e=>i=t[o]=[e])]),r.push(i[1]=n)}}})(),function(e){e.system="system",e.user="user",e.assistant="assistant"}(a||(a={})),function(e){e[e.online=0]="online",e[e.away=1]="away",e[e.offline=2]="offline"}(o||(o={})),function(e){e[e.idle=0]="idle",e[e.downloading=1]="downloading",e[e.ready=2]="ready",e[e.error=3]="error",e[e.unsupported=4]="unsupported"}(r||(r={})),function(e){e.cpu="cpu",e.gpu="gpu",e.none="none"}(i||(i={})),function(e){e.download="download",e.load="load"}(s||(s={})),function(e){e.qwen3_vl_2b="qwen3-vl-2b",e.gemma4_e2b="gemma4-e2b"}(c||(c={}));const l=[{id:c.qwen3_vl_2b,reasoning:!0,cpu:{repo:"Qwen/Qwen3-VL-2B-Thinking-GGUF",file:"Qwen3VL-2B-Thinking-Q4_K_M.gguf",mmproj:{file:"mmproj-Qwen3VL-2B-Thinking-Q8_0.gguf"}}},{id:c.gemma4_e2b,reasoning:!0,litert:{repo:"litert-community/gemma-4-E2B-it-litert-lm",file:"gemma-4-E2B-it.litertlm",vision:!0,audio:!0}}],d=e=>e?l.find(t=>t.id==e):void 0,u=async()=>(await(async()=>{const e="undefined"==typeof navigator?void 0:navigator.gpu;if(!e)return!1;try{return!!await e.requestAdapter({powerPreference:"high-performance"})}catch{return!1}})()?l.find(e=>!!e.litert):l.find(e=>!!e.cpu))?.id,g=async e=>d(e||await u()),m=e=>!!e.cpu?.mmproj||!!e.litert?.vision,f=e=>!!e.cpu?.mmproj?.audio||!!e.litert?.audio,p=new URL("./../assets/wllama/wllama.wasm",import.meta.url).href,h=new URL("./../assets/wllama/compat/wllama.wasm",import.meta.url).href,w=new URL("./../assets/wllama/compat/wllama.js",import.meta.url).href,y=new URL("./../assets/litert/litertlm_wasm_internal.js",import.meta.url).href,b=(new URL("./../assets/litert/litertlm_wasm_internal.wasm",import.meta.url).href,288),v=async e=>{if(!e.startsWith("data:"))return await(await fetch(e)).arrayBuffer();const t=e.indexOf(","),n=t>=0?e.slice(0,t):"";if(t<0)throw new Error("Invalid image data");if(!n.includes(";base64"))return await(await fetch(e)).arrayBuffer();const a=atob(e.slice(t+1)),o=new Uint8Array(a.length);for(let e=0;e<a.length;e++)o[e]=a.charCodeAt(e);return o.buffer},x=async e=>{if(!e||"function"!=typeof fetch||"function"!=typeof createImageBitmap)return e;try{const t=await fetch(e),n=await createImageBitmap(await t.blob()),a=Math.min(n.width,n.height),o=Math.floor((n.width-a)/2),r=Math.floor((n.height-a)/2),i=((e,t)=>{if("undefined"!=typeof OffscreenCanvas)return new OffscreenCanvas(e,t);if("undefined"==typeof document)return;const n=document.createElement("canvas");return n.width=e,n.height=t,n})(b,b),s=i?i.getContext("2d"):null;if(!i||!s||a<1)return n.close(),e;if(s.drawImage(n,o,r,a,a,0,0,b,b),n.close(),(e=>"undefined"!=typeof OffscreenCanvas&&e instanceof OffscreenCanvas)(i)){const t=await i.convertToBlob({type:"image/jpeg",quality:.85});return await(e=>new Promise(t=>{if("undefined"==typeof FileReader)return void t("");const n=new FileReader;n.onerror=()=>t(""),n.onload=()=>t(`${n.result}`),n.readAsDataURL(e)}))(t)||e}return i.toDataURL("image/jpeg",.85)}catch{return e}},k={maxReplyTokens:250,maxThinkingTokens:128,maxReasoningTokens:320,maxImages:2,maxAudio:1,maxContentLength:1e3,promptCharBudget:1e4,maxThreads:6},_=(e,t,n)=>"number"==typeof e&&Number.isFinite(e)&&e>=n?Math.floor(e):t,M=(e={},t=!1)=>{const n=k;return{maxReplyTokens:_(e.maxReplyTokens,n.maxReplyTokens,1),maxThinkingTokens:_(e.maxThinkingTokens,n.maxThinkingTokens,1),maxReasoningTokens:_(e.maxReasoningTokens,t?1200:n.maxReasoningTokens,1),maxImages:_(e.maxImages,n.maxImages,0),maxAudio:_(e.maxAudio,n.maxAudio,0),maxContentLength:_(e.maxContentLength,n.maxContentLength,1),promptCharBudget:_(e.promptCharBudget,n.promptCharBudget,1),maxThreads:_(e.maxThreads,n.maxThreads,1)}},T=(e,t)=>({stream:!0,temperature:.7,max_tokens:t?e.maxReasoningTokens:e.maxReplyTokens}),E=(e,t)=>`https://huggingface.co/${e}/resolve/main/${t}`,C=e=>Number(e.headers.get("content-length"))||0,L=(e,t)=>e.some(e=>Array.isArray(e.content)&&e.content.some(e=>e.type==t)),P=e=>L(e,"image"),R=e=>L(e,"audio"),$=["<|channel>thought\n","<|channel>thought","..."],j=["<channel|>","..."],S=(e,t,n)=>t.length||n.length?[...e?[{type:"text",text:e}]:[],...t.map(e=>({type:"image",url:e})),...n.map(e=>({type:"audio",url:e}))]:e,A=e=>"string"==typeof e?e?[{type:"text",text:e}]:[]:e,W=(e,t)=>{if("string"==typeof e&&"string"==typeof t)return`${e}\n${t}`;const n=[];for(const a of[...A(e),{type:"text",text:"\n"},...A(t)]){const e=n[n.length-1];"text"==a.type&&"text"==e?.type?e.text+=a.text:n.push(a)}return n};class B extends Error{}const I=async(e,t,n)=>{let a;try{return await Promise.race([e,new Promise((e,o)=>{a=setTimeout(()=>o(new B(n)),t)})])}finally{a&&clearTimeout(a)}},q=e=>{try{Promise.resolve(e.return?.()).catch(()=>{})}catch{}},z=async({model:e,logging:t,onReason:n,onText:a,onSpeed:o,start:r,stopped:i,firstTimeoutMs:s=18e4,idleTimeoutMs:c=3e4,onStall:l})=>{let d="",u="",g="",m=!1,f=0,p=0;const h=((e,t)=>{let n="",a=!1;const o=(e,t)=>{let n=-1,a="";for(const o of t){const t=e.indexOf(o);t>=0&&(n<0||t<n)&&(n=t,a=o)}return{at:n,tag:a}},r=(e,t)=>{let n="";for(const a of t)for(let t=Math.min(e.length,a.length-1);t>n.length;t--)if(e.endsWith(a.slice(0,t))){n=e.slice(e.length-t);break}return n};return{feed:i=>{let s=n+i;for(n="";s;){if(a){const{at:t,tag:i}=o(s,j);if(t>=0){t&&e(s.slice(0,t)),s=s.slice(t+i.length),a=!1;continue}const c=r(s,j);e(s.slice(0,s.length-c.length)),n=c;break}{const{at:e,tag:i}=o(s,$);if(e>=0){e&&t(s.slice(0,e)),s=s.slice(e+i.length),s.startsWith("\n")&&(s=s.slice(1)),a=!0;continue}const c=r(s,$);t(s.slice(0,s.length-c.length)),n=c;break}}},flush:()=>{n&&(a?e(n):t(n),n="")}}})(e=>{u+=e,n(e)},e=>{d+=e,a(e)});try{const e=(await I(r(),s,"Model did not start"))[Symbol.asyncIterator]();for(;;){if(i()){q(e);break}let a;try{a=await I(e.next(),m?c:s,m?"Model stopped responding":"Model did not reply")}catch(n){if(n instanceof B&&(d||u)){l?.(),q(e),t&&console.log("Local chat stalled",n);break}throw n}if(a.done)break;m=!0;const r=a.value?.choices?.[0],w=r?.delta,y="string"==typeof w?.reasoning_content?w.reasoning_content:"string"==typeof w?.reasoning?w.reasoning:"";if(r?.finish_reason&&(g=r.finish_reason),y&&(u+=y,n(y)),w?.content&&h.feed(w.content),y||w?.content){const e=performance.now();f||(p=e),f+=1,f>1&&e>p&&o?.(1e3*f/(e-p))}}h.flush()}catch(e){if(t&&console.log("Local chat failed",e),e instanceof B&&!i())throw e}const w=i()?void 0:d||void 0;return t&&(console.log("Local chat reply",e.id,w?"****":""),console.log("Local chat finish",e.id,g||"none",d.length,u.length)),w},O=async({model:e,params:t,start:n,stopped:o,onStall:r})=>{const i=await(async e=>{const t=[];for(const n of e){if("string"==typeof n.content){t.push(n);continue}const e=[];for(const t of n.content)e.push("image"==t.type?{type:"image",url:await x(t.url)}:t);t.push({role:"user",content:e})}return t})(((e,t=k)=>{const n=t.maxContentLength,o=e.filter(e=>"string"==typeof e.content&&!!e.content.trim()||!(e.role!=a.user||!e.images?.length&&!e.audio?.length)),r=[],i=[];let s=0,c=t.maxImages,l=t.maxAudio;for(let e=o.length-1;e>=0;e--){const d=o[e],u=("string"==typeof d.content?d.content:"").slice(0,n),g=d.role==a.user?(d.images||[]).filter(e=>"string"==typeof e&&!!e).slice(0,Math.max(0,c)):[],m=d.role==a.user?(d.audio||[]).filter(e=>"string"==typeof e&&!!e).slice(0,Math.max(0,l)):[];if(u||g.length||m.length){if(u&&s+u.length>t.promptCharBudget)break;s+=u.length,c-=g.length,l-=m.length,d.role==a.system?r.unshift(u):d.role==a.assistant?i.unshift({role:"assistant",content:u}):i.unshift({role:"user",content:S(u,g,m)})}}for(;i.length&&"user"!=i[0].role;)i.shift();const d=[];for(const e of i){const t=d[d.length-1];"assistant"==t?.role&&"assistant"==e.role?t.content=`${t.content}\n${e.content}`:"user"==t?.role&&"user"==e.role?t.content=W(t.content,e.content):"assistant"==e.role?d.push({role:"assistant",content:e.content}):d.push({role:"user",content:e.content})}const u=r.join("\n\n"),g=[];return u&&g.push({role:"system",content:u}),g.push(...d),g})(t.messages,t.limits));return t.logging&&((e,t)=>{console.log("Local chat call",e.id,t.map(({role:e,content:t})=>({role:e,content:"****",...Array.isArray(t)?{images:t.filter(e=>"image"==e.type).length,audio:t.filter(e=>"audio"==e.type).length}:{}})))})(e,i),await z({model:e,logging:t.logging,onReason:t.onReason,onText:t.onText,onSpeed:t.onSpeed,start:()=>n(i),stopped:o,onStall:r})},U=33554432,N=new Map;let F=!1;const D=e=>{const t=(e.split("/").pop()||"model").replace(/[^\w.-]+/g,"_");let n=0;for(let t=0;t<e.length;t++)n=31*n+e.charCodeAt(t)>>>0;return`${n.toString(16)}-${t}`},H=e=>{const t=/^part-(\d+)$/.exec(e);return t?Number(t[1]):-1},Q=async()=>{if(!F)if("undefined"!=typeof navigator&&navigator.storage?.getDirectory)try{return await navigator.storage.getDirectory()}catch{return}else F=!0},G=async(e,t)=>{try{return await e.getDirectoryHandle(t)}catch{return}},V=async e=>{if(e)try{const t=await e.getFile(),n=JSON.parse(await t.text());return{total:Number(n.total)||0,etag:`${n.etag||""}`,complete:!0===n.complete}}catch{return}},J=async(e,t)=>{try{const n=await e.getFileHandle("meta.json",{create:!0}),a=await n.createWritable();await a.write(JSON.stringify(t)),await a.close()}catch{}},K=async function*(e){for await(const t of e)yield t},X=async e=>{try{for await(const[t,n]of K(e))"file"==n.kind&&H(t)>=0&&await e.removeEntry(t).catch(()=>{})}catch{}},Y=async(e,t)=>{try{const n=await(await e.getFileHandle(t)).getFile(),a=await V(await e.getFileHandle(`${t}.json`).catch(()=>{}));if(!a)return;return{file:n,size:n.size,total:a.total,etag:a.etag,complete:a.total?n.size>=a.total:!0===a.complete}}catch{return}},Z=async(e,t)=>{const n=await G(e,t);if(n){const e=await(async e=>await V(await e.getFileHandle("meta.json").catch(()=>{})))(n),t=await(async e=>{const t=[];try{for await(const[n,a]of K(e)){const e=H(n);if("file"!=a.kind||e<0)continue;const o=await a.getFile();t.push({part:{start:e,size:o.size},file:o})}}catch{return}const n=(e=>{const t=[];let n=0;for(const a of[...e].sort((e,t)=>e.start-t.start))if(a.size){if(a.start!=n)break;t.push(a),n+=a.size}return t})(t.map(e=>e.part)),a=new Set(n.map(e=>e.start)),o=t.filter(e=>a.has(e.part.start)).sort((e,t)=>e.part.start-t.part.start).map(e=>e.file);if(o.length)return{file:1==o.length?o[0]:new Blob(o),size:n.reduce((e,t)=>e+t.size,0)}})(n),a=e?.total||0,o=t?.size??0,r=t?.file??new Blob;return{file:a&&o>a?r.slice(0,a):r,size:o,total:a,etag:e?.etag||"",complete:a?o>=a:!0===e?.complete}}return await Y(e,t)},ee=(e,t)=>{const n=e.headers.get("content-range"),a=/bytes (\d+)-/.exec(n||""),o=/\/(\d+)\s*$/.exec(n||""),r=e.headers.get("etag")||"";if(206==e.status){const e=a?Number(a[1]):t.size,n=o?Number(o[1]):t.total;if(e!=t.size)return;if(t.total&&n&&t.total!=n)return;if(t.etag&&r&&t.etag!=r)return;return{start:e,total:n||t.total,etag:r||t.etag}}if(!t.etag||!r||t.etag==r)return{start:0,total:C(e)||t.total,etag:r||t.etag}},te=async({dir:e,start:t,skip:n,total:a,body:o,onProgress:r,onChunk:i})=>{let c,l=t,d=0,u=n;const g=async()=>{const t=await e.getFileHandle((e=>`part-${e}`)(l),{create:!0});c=await t.createWritable(),d=0},m=async()=>{const e=c;c=void 0,e&&await e.close().catch(()=>{})};try{const e=o.getReader();for(;;){const{done:n,value:o}=await e.read();if(n)break;i();let f=0;for(u&&(f=Math.min(u,o.byteLength),u-=f);f<o.byteLength;){c||await g();const e=Math.min(U-d,o.byteLength-f);await c.write({type:"write",position:d,data:o.subarray(f,f+e)}),f+=e,d+=e,l+=e,d==U&&await m()}l>t&&r(a?l/a:0,s.download)}await m()}catch(e){throw await m(),e}return l},ne=e=>["NotAllowedError","NotSupportedError"].includes(e?.name||""),ae=e=>{const t=async()=>{const t=await Q();if(t)try{return await t.getDirectoryHandle(e,{create:!0})}catch{return}};return{stored:async e=>{const n=await t();return n?await Z(n,D(e)):void 0},download:async({url:n,signal:a,onProgress:o})=>{const r=`${e}|${n}`,i=N.get(r);if(i)return await i;const c=await t();if(!c)return;const l=(async(e,t,n,a)=>{const o=D(t),r=await Y(e,o);if(r?.complete)return r;let i=await G(e,o);i||(await e.removeEntry(o).catch(()=>{}),await e.removeEntry(`${o}.json`).catch(()=>{}),i=await e.getDirectoryHandle(o,{create:!0}));let c=0,l=0;for(;;){if(n?.aborted)throw new Error("Cancelled");const r=await Z(e,o);if(r?.complete)return r;const d=r?.size??0,u=r?.total||0,g=r?.etag||"";if(d>c)c=d,l=0;else if(++l>4)throw new Error("Model download failed");d&&u&&a(d/u,s.download);const m=u?Math.min(d+U,u)-1:d+U-1,f=new AbortController,p=()=>f.abort(),h={size:d,total:u,etag:g};let w,y=setTimeout(p,3e4);n?.addEventListener("abort",p,{once:!0});try{if(w=await fetch(t,{signal:f.signal,headers:{Range:`bytes=${d}-${m}`}}),416==w.status){if(await(w.body?.cancel().catch(()=>{})),u&&d>=u){await J(i,{total:u,etag:g,complete:!0});continue}await X(i),await J(i,{total:0,etag:"",complete:!1});continue}if(!w.ok||!w.body)throw await(w.body?.cancel().catch(()=>{})),new Error("Model download failed");const n=ee(w,h);if(!n){await w.body.cancel().catch(()=>{}),await X(i),await J(i,{total:0,etag:"",complete:!1});continue}await J(i,{total:n.total,etag:n.etag,complete:!1});const r=0==n.start?d:0,s=await te({dir:i,start:n.start+r,skip:r,total:n.total,body:w.body,onProgress:a,onChunk:()=>{clearTimeout(y),y=setTimeout(p,3e4)}});if(!n.total){await J(i,{total:s,etag:n.etag,complete:!0});const t=await Z(e,o);if(t)return t}}catch(e){if(n?.aborted)throw new Error("Cancelled");if(e instanceof DOMException&&["QuotaExceededError","NotAllowedError","NotSupportedError","InvalidStateError","TypeMismatchError","SecurityError"].includes(e.name))throw e;await new Promise(e=>setTimeout(e,250+500*l))}finally{clearTimeout(y),n?.removeEventListener("abort",p)}}})(c,n,a,o).finally(()=>{N.delete(r)});return N.set(r,l),await l},remove:async e=>{const n=await t();n&&await(async(e,t)=>{await e.removeEntry(t,{recursive:!0}).catch(()=>{}),await e.removeEntry(`${t}.json`).catch(()=>{})})(n,D(e))},purge:async()=>{const t=await Q();t&&await t.removeEntry(e,{recursive:!0}).catch(()=>{})}}},oe="litertlm-models",re=ae("litert-store"),ie={num_output_candidates:1,wait_for_weight_uploads:!0,num_decode_steps_per_sync:3,sequence_batch_size:0,supported_lora_ranks:[],max_top_k:40,enable_decode_logits:!1,enable_external_embeddings:!1,use_submodel:!1,use_autosized_ringbuffers:!0};let se=!1;const ce=(...e)=>{se&&console.log(...e)},le=(...e)=>{se&&console.error(...e)},de=async()=>{try{if("undefined"==typeof caches)return;return await caches.open(oe)}catch{return}},ue=new Map,ge=(e,t,n)=>{let a=0;return e.pipeThrough(new TransformStream({transform:(e,o)=>{a+=e.byteLength,n(t?a/t:0,s.download),o.enqueue(e)}}))},me=async(e,t=!0)=>{const n=[];for(const a of e){if("string"==typeof a.content){n.push({role:a.role,content:a.content});continue}const e=[];for(const n of a.content)"text"!=n.type?t?e.push({type:n.type,data:await v(n.url)}):e.push({type:"text",text:`[${n.type}]`}):e.push({type:"text",text:n.text});n.push({role:a.role,content:e})}return n},fe=(e,t)=>{if(e.role!=t.role)return!1;if("string"==typeof e.content||"string"==typeof t.content)return e.content==t.content;if(e.content.length!=t.content.length)return!1;for(let n=0;n<e.content.length;n++){const a=e.content[n],o=t.content[n];if(a.type!=o.type)return!1;if("text"==a.type&&"text"==o.type){if(a.text!=o.text)return!1}else if(("image"==a.type&&"image"==o.type||"audio"==a.type&&"audio"==o.type)&&a.url!=o.url)return!1}return!0},pe=async function*({engine:e,mod:t,params:n,messages:a,session:o}){const r=a[a.length-1],i=T(n.limits,n.reasoning);if("user"!=r?.role)throw new Error("No user turn");const s=a.slice(0,-1),c=(await me([r]))[0];if(!c)throw new Error("Empty prompt");const l=!!o.conversation&&o.reasoning==n.reasoning&&o.maxTokens==i.max_tokens&&((e,t)=>{const n="system"==e[0]?.role?e[0]:void 0,a="system"==t[0]?.role?t[0]:void 0;if(!n!=!a)return!1;if(n&&a&&!fe(n,a))return!1;const o=n?e.slice(1):e,r=a?t.slice(1):t;if(!r.length||r.length>o.length)return!1;const i=o.length-r.length;for(let e=0;e<r.length;e++)if(!fe(o[i+e],r[e]))return!1;return!0})(o.sent,s);let d;if(l&&o.conversation)d=o.conversation;else{const a=await me(s,!1);o.conversation=void 0,o.sent=[],d=await e.createConversation({sessionConfig:{maxOutputTokens:i.max_tokens,samplerParams:{type:t.SamplerType.TOP_K,temperature:i.temperature,k:40,p:.95}},preface:{...a.length?{messages:a}:{},extra_context:{enable_thinking:n.reasoning}},prefillPrefaceOnInit:a.length>0}),o.conversation=d,o.reasoning=n.reasoning,o.maxTokens=i.max_tokens,o.sent=[...s]}o.sent.push(r);let u,g=!1,m="";const f=()=>{u&&u.cancel().catch(()=>{});try{d.cancel()}catch{}};n.signal?.addEventListener("abort",f,{once:!0});try{if(n.signal?.aborted)return;const e=d.sendMessageStreaming(c).getReader();for(u=e;;){const{done:t,value:n}=await e.read();if(t)break;const a={};let o="string"==typeof n.content?n.content:"";if(Array.isArray(n.content))for(const e of n.content)"text"==e.type&&(o+=e.text);o&&(a.content=o,m+=o);const r=n.channels?.thought;r&&(a.reasoning=r),(a.content||a.reasoning)&&(yield{choices:[{delta:a}]})}g=!0}finally{n.signal?.removeEventListener("abort",f),g&&m?o.sent.push({role:"assistant",content:m}):(o.conversation=void 0,o.sent=[])}},he={backend:i.gpu,startLLM:async({model:e,limits:t,onProgress:a,signal:o,logging:r})=>{se=!0===r;const c=e.litert;if(!c)throw new Error("Model has no LiteRT artifact");const l=await n.e(520).then(()=>n(507)),d=globalThis,u=d.Module;d.Module={print:ce,printErr:le};try{await l.getOrLoadGlobalLiteRtLm(y)}finally{d.Module=u}if(o?.aborted)throw new Error("Cancelled");const g=E(c.repo,c.file),m=await de();let f;const p=m?await m.match(g):void 0;if(p?.body)f=p.body;else{const e=await re.stored(g);e?.complete&&(f=e.file.stream())}if(!f){const e=await re.download({url:g,signal:o,onProgress:a}).catch(e=>{if(o?.aborted)throw new Error("Cancelled");if(!ne(e))throw e;r&&console.log("Local model store failed",e)});e&&(f=e.file.stream())}if(!f&&m)try{await(async(e,t,n,a)=>{const o=ue.get(e);if(o)return await o;const r=(async()=>{const o=await fetch(e,{signal:n});if(!o.ok||!o.body)throw new Error("Model download failed");const r=C(o),i=ge(o.body,r,a);await t.put(e,new Response(i,{headers:{"content-type":"application/octet-stream",...r?{"content-length":`${r}`}:{}}}))})().finally(()=>{ue.delete(e)});return ue.set(e,r),await r})(g,m,o,a);const e=await m.match(g);e?.body&&(f=e.body)}catch(e){if(o?.aborted)throw new Error("Cancelled");r&&console.log("Local model cache failed",e)}if(f||(f=await(async(e,t,n)=>{const a=await fetch(e,{signal:t});if(!a.ok||!a.body)throw new Error("Model download failed");return ge(a.body,C(a),n)})(g,o,a)),o?.aborted)throw new Error("Cancelled");a(1,s.load);const h=await l.Engine.create((({model:e,limits:t,source:n,mod:a})=>({model:n,mainExecutorSettings:{maxNumTokens:4096,...e.litert?.vision?{maxNumImages:Math.max(1,t.maxImages)}:{},backendConfig:ie},...e.litert?.vision?{visionBackend:a.Backend.GPU,maxVisionTokensPerImage:70,minVisionTokensPerImage:70}:{},...e.litert?.audio?{audioBackend:a.Backend.GPU}:{}}))({model:e,limits:t,source:f,mod:l}));if(o?.aborted)throw await h.delete(),new Error("Cancelled");const w={sent:[],reasoning:!1,maxTokens:0};return{model:e,backend:i.gpu,contextSize:4096,chat:t=>(async({engine:e,model:t,mod:n,params:a,session:o})=>{se=!0===a.logging;const r=()=>{try{o.conversation?.cancel()}catch{}o.conversation=void 0,o.sent=[]};try{return await O({model:t,params:a,stopped:()=>!!a.signal?.aborted,onStall:r,start:async r=>{if(a.signal?.aborted)throw new Error("Cancelled");if(P(r)&&!t.litert?.vision)throw new Error("Model does not accept images");if(R(r)&&!t.litert?.audio)throw new Error("Model does not accept audio");return pe({engine:e,mod:n,params:a,messages:r,session:o})}})}catch(e){throw r(),e}})({engine:h,model:e,mod:l,params:t,session:w}),unload:async()=>{w.conversation=void 0,w.sent=[],await h.delete()}}},cached:async e=>{const t=e.litert;if(!t)return!1;const n=E(t.repo,t.file),[a,o]=await Promise.all([de(),re.stored(n)]);if(o?.complete)return!0;try{return!(!a||!await a.match(n))}catch{return!1}},hasCache:async()=>{try{for(const e of l){const t=e.litert;if(!t)continue;if(await re.stored(E(t.repo,t.file)))return!0}const e=await de();return!!e&&(await e.keys()).length>0}catch{return!1}},remove:async e=>{try{const t=e.litert;if(!t)return;const n=E(t.repo,t.file),a=await de();await re.remove(n),a&&await a.delete(n)}catch{}},purge:async()=>{try{await re.purge(),"undefined"!=typeof caches&&await caches.delete(oe)}catch{}}},we=ae("wllama-store");let ye,be,ve="",xe=!1;const ke=e=>`${e.wasm}|${e.compatWasm}|${e.compatWorker}`,_e=e=>{const t={wasm:p,compatWasm:h,compatWorker:w};return{wasm:e?.wasm||t.wasm,compatWasm:e?.compatWasm||t.compatWasm,compatWorker:e?.compatWorker||t.compatWorker}},Me=e=>{const t=e=>(...t)=>{xe&&("string"==typeof t[0]&&t[0].includes("ggml_webgpu: Failed to get an adapter")||e(...t))};return{...e,log:t(e.log),warn:t(e.warn),error:t(e.error)}},Te=async()=>{if(ye)return ye;const e=await n.e(924).then(()=>n(577)),t=be||_e(),a=new e.Wllama({default:t.wasm},{logger:Me(e.LoggerWithoutDebug)});return be||(be=t,ve=ke(t)),a.setCompat({wasm:t.compatWasm,worker:t.compatWorker}),ye=a,a},Ee=async()=>{const e=ye;ye=void 0;try{await(e?.exit())}catch{}},Ce=(e,t)=>e.name.includes(t)||!!e.metadata?.originalURL?.includes(t),Le=e=>{const t=e.cpu;return t?[{name:t.file,url:E(t.repo,t.file)},...t.mmproj?[{name:t.mmproj.file,url:E(t.repo,t.mmproj.file)}]:[]]:[]},Pe=async e=>{const t=Le(e);if(!t.length)return!1;for(const e of t){const t=await we.stored(e.url);if(!t?.complete)return!1}return!0},Re=async(e,t)=>{const n=await e.cacheManager.list();return Le(t).every(e=>n.some(t=>Ce(t,e.name)))},$e=async({model:e,signal:t,onProgress:n})=>{const a=Le(e),o=[];for(let e=0;e<a.length;e++){if(t?.aborted)throw new Error("Cancelled");const r=await we.download({url:a[e].url,signal:t,onProgress:(t,o)=>n((e+t)/a.length,o)});if(!r)return;o.push(r.file)}return o.length?o:void 0},je=async e=>{const t=[];for(const n of e){if("string"==typeof n.content){"system"==n.role?t.push({role:"system",content:n.content}):"assistant"==n.role?t.push({role:"assistant",content:n.content}):t.push({role:"user",content:n.content});continue}const e=[];for(const t of n.content)"text"==t.type?e.push({type:"text",text:t.text}):e.push({type:t.type,data:await v(t.url)});t.push({role:"user",content:e})}return t},Se={backend:i.cpu,startLLM:async({model:e,limits:t,onProgress:n,signal:a,logging:o,assets:r})=>{xe=!0===o;const c=e.cpu;if(!c)throw new Error("Model has no CPU artifact");const l=_e(r),d=ke(l);ye&&d!=ve&&await Ee(),be=l,ve=d;const u=(g="undefined"!=typeof navigator&&navigator.hardwareConcurrency||4,m=t.maxThreads,Math.max(1,Math.min(Math.max(2,Math.min(8,(g||4)-1)),m)));var g,m;const f=await Te(),p=await Pe(e),h=c.mmproj?.file,w=E(c.repo,c.file),y=h?E(c.repo,h):"";if(a?.aborted)throw await Ee(),new Error("Cancelled");if(!p&&h){const e=await f.cacheManager.list(),t=e.some(e=>Ce(e,c.file)),n=e.some(e=>Ce(e,h));t&&!n&&await f.cacheManager.deleteMany(e=>Ce(e,c.file))}const b={n_ctx:8192,n_threads:u,reasoning_budget_tokens:t.maxThinkingTokens,n_gpu_layers:0,jinja:!0};try{let t=p?await $e({model:e,signal:a,onProgress:n}):void 0;t||p||await Re(f,e).catch(()=>!1)||(t=await $e({model:e,signal:a,onProgress:n}).catch(e=>{if(a?.aborted)throw new Error("Cancelled");if(!ne(e))throw e;o&&console.log("Local model store failed",e)})),t?(n(1,s.load),await f.loadModel(t,b)):await f.loadModelFromUrl(y?{url:w,mmprojUrl:y}:w,{...b,useCache:!0,signal:a,progressCallback:({loaded:e,total:t})=>{const a=t?e/t:0;n(a,a>=1?s.load:s.download)}})}catch(e){throw await Ee(),e}if(a?.aborted)throw await Ee(),new Error("Cancelled");if(!f.getLoadedContextInfo().n_ctx)throw await Ee(),new Error("Failed to create model context");return{model:e,backend:i.cpu,contextSize:8192,chat:t=>(async(e,t,n)=>{if(xe=!0===n.logging,e.isModelLoaded())return await O({model:t,params:n,stopped:()=>!!n.signal?.aborted,start:async a=>{if(P(a)&&!e.supportInputModality("image"))throw new Error("Model does not accept images");if(R(a)&&!e.supportInputModality("audio"))throw new Error("Model does not accept audio");return await e.createChatCompletion({messages:await je(a),...T(n.limits,n.reasoning),abortSignal:n.signal,...t.reasoning?{chat_template_kwargs:{enable_thinking:n.reasoning}}:{}})}})})(f,e,t),unload:async()=>{if(ye==f)await Ee();else try{await f.exit()}catch{}}}},cached:async e=>{try{return!!e.cpu&&(!!await Pe(e)||await Re(await Te(),e))}catch{return!1}},hasCache:async()=>{try{for(const e of l.flatMap(e=>Le(e)))if(await we.stored(e.url))return!0;const e=await Te();return(await e.cacheManager.list()).length>0}catch{return!1}},remove:async e=>{const t=Le(e);try{for(const e of t)await we.remove(e.url)}catch{}try{const e=await Te();await e.cacheManager.deleteMany(e=>t.some(t=>Ce(e,t.name)))}catch{}},purge:async()=>{try{await we.purge()}catch{}try{const e=await Te();await e.cacheManager.clear()}catch{}}},Ae=((e={})=>{const t=e.engines?.cpu||Se,n=e.engines?.litert||he;let a,o,c,d,u,g,m,f=i.none,p=r.idle,h=[],w=0,y={percent:0,phase:s.download};const b=a=>{if(e.backend!=i.none)return e.backend==i.cpu?a.cpu?t:void 0:e.backend==i.gpu?a.litert?n:void 0:a.litert&&"undefined"!=typeof navigator&&navigator.gpu&&"undefined"!=typeof WebAssembly?n:!a.cpu||"undefined"==typeof window&&"undefined"==typeof self&&"undefined"==typeof Worker||"undefined"==typeof WebAssembly?void 0:t},v=(e,t)=>{y={percent:Math.max(0,Math.min(1,e)),phase:t||s.download};for(const e of[...h])e.report(y.percent,y.phase)},x=e=>{p=e},k=()=>{w++,g?.abort(),g=void 0,u=void 0,p==r.downloading&&x(r.idle)},_=(e,t)=>{const n=d;return n?new Promise(a=>{let o=!1;const r={report:e,settle:e=>{o||(o=!0,t?.removeEventListener("abort",i),a(e))}},i=()=>{(e=>{h=h.filter(t=>t!=e),queueMicrotask(()=>{!h.length&&d&&k()})})(r),r.settle(!1)};h.push(r),t?.aborted?i():t?.addEventListener("abort",i,{once:!0}),n.then(r.settle,()=>r.settle(!1))}):Promise.resolve(!1)},M=()=>p==r.ready&&!!a&&!!o;return{load:async({model:e,limits:t,assets:n,logging:i,signal:m,onProgress:p})=>{if(m?.aborted)return!1;if(M()&&o?.id==e.id)return!0;if(d&&u==e.id)return p(y.percent,y.phase),await _(p,m);d&&(k(),h=[],await d.catch(()=>!1));const T=++w,E=(async()=>{await(async()=>{try{if("undefined"==typeof navigator)return;await(navigator.storage?.persist?.())}catch{}})();const d=b(e);if(T!=w)return!1;if(!d)return x(r.unsupported),!1;if(await(async(e,t)=>{if(o&&o.id!=e.id){const e=o,t=c,n=a;a=void 0,o=void 0,c=void 0,await(n?.unload()),await(t?.remove(e))}if(await t.hasCache())for(const n of l)n.id!=e.id&&b(n)==t&&await t.remove(n)})(e,d),T!=w)return!1;const u=await d.cached(e);if(T!=w)return!1;x(r.downloading),v(0,s.download),u&&v(1,s.load);const m=new AbortController;g=m;try{const l=await d.startLLM({model:e,limits:t,logging:i,signal:m.signal,assets:n,onProgress:(e,t)=>{T==w&&v(e,t)}});return T!=w||m.signal.aborted?(await l.unload(),!1):(a=l,o=e,c=d,f=d.backend,x(r.ready),v(1,s.load),!0)}catch(e){return T!=w||(a=void 0,o=void 0,c=void 0,i&&console.log("Local model failed",e),x(r.error)),!1}finally{g==m&&(g=void 0)}})().finally(()=>{d==E&&(d=void 0,u=void 0,h=[])});return d=E,u=e.id,await _(p,m)},chat:async e=>{if(!M()||!a)return;for(;m;)await m.catch(()=>{});if(!M()||!a)return;const t=a.chat(e);m=t;try{return await t}finally{m==t&&(m=void 0)}},unload:async()=>{const e=a;k(),a=void 0,o=void 0,c=void 0,x(r.idle),await(e?.unload())},ready:M,loading:()=>!M()&&p==r.downloading,status:()=>p,backend:()=>f,model:()=>o}})(),We=(e,t,n)=>{const a=e.status(),i=d(t);return{availability:a==r.ready?o.online:a==r.error||a==r.unsupported?o.offline:o.away,status:a,backend:e.backend(),supportsImages:!!i&&m(i),supportsAudio:!!i&&f(i),ready:e.ready(),loading:e.loading(),requesting:n.requesting,phase:n.phase,percent:n.percent,...n.error?{error:n.error}:{}}},Be=(e=>{let t,n=0;const o=t=>t.input.onStatus?.(We(e,t.model??t.input.model,t));return i=>{t?.controller.abort();const c={token:++n,input:i,controller:new AbortController,percent:e.ready()?100:0,phase:e.ready()?s.load:s.download,requesting:!1,cancelled:!1};return t=c,o(c),(async t=>{const{input:r}=t,i=!0===r.logging,c=await g(r.model);if(t.model=c?.id,t.token!=n)return;const l=r.messages.some(e=>e.role!=a.system&&"string"==typeof e.content&&!!e.content.trim()),d=r.messages.some(e=>e.role==a.user&&!!e.images?.some(e=>"string"==typeof e&&!!e)),u=r.messages.some(e=>e.role==a.user&&!!e.audio?.some(e=>"string"==typeof e&&!!e)),p=l||d||u;if(!c)return t.error="Unknown model",void o(t);if(!p)return void o(t);const h=m(c),w=f(c);if(!(l||d&&h||u&&w))return u&&!w?t.error="Model does not accept audio":d&&!h&&(t.error="Model does not accept images"),void o(t);const y=M(r.limits,!!c.litert),b=await e.load({model:c,limits:y,logging:i,signal:t.controller.signal,onProgress:(e,a)=>{t.token==n&&(t.percent=Math.round(100*e),t.phase=a,o(t))}}).catch(e=>(t.token==n&&(t.error=`${e?.message||`${e}`}`),!1));if(t.token!=n)return;if(t.cancelled)return;if(!b)return t.error||(t.error="Model not ready"),void o(t);const v=d&&!h||u&&!w?r.messages.map(({images:e,audio:t,...n})=>({...n,...e&&h?{images:e}:{},...t&&w?{audio:t}:{}})):r.messages;t.requesting=!0,t.error=void 0,t.percent=100,t.phase=s.load,o(t);let x,k="",_="";try{x=await e.chat({messages:v,limits:y,reasoning:!0===r.reasoning,logging:i,onReason:e=>{t.token!=n||t.cancelled||(k+=e,r.onThinking?.(k))},onText:e=>{t.token!=n||t.cancelled||(_+=e,r.onResponse?.(_))},onSpeed:e=>{t.token!=n||t.cancelled||r.onSpeed?.(e)},signal:t.controller.signal})}catch(e){if(t.token!=n)return;return t.requesting=!1,t.error=`${e?.message||`${e}`}`,void o(t)}t.token==n&&(t.requesting=!1,x||t.cancelled||(t.error="No reply"),o(t))})(c),()=>(t=>{t.token==n&&(t.cancelled=!0,t.controller.abort(),e.status()!=r.ready?queueMicrotask(()=>o(t)):e.unload().then(()=>o(t)))})(c)}})(Ae),Ie=(qe=Ae,async(e={})=>{const t=await g(e.model),n=n=>e.onStatus?.(We(qe,t?.id,n));if(!t)return n({requesting:!1,phase:s.download,percent:0,error:"Unknown model"}),!1;let a,o=0,r=s.download;const i=qe.ready()&&qe.model()?.id==t.id;return await qe.load({model:t,limits:M(e.limits,!!t.litert),logging:!0===e.logging,onProgress:(e,t)=>{o=Math.round(100*e),r=t,n({requesting:!1,phase:r,percent:o})}}).catch(e=>(a=`${e?.message||`${e}`}`,!1))?(i&&n({requesting:!1,phase:s.load,percent:100}),!0):(n({requesting:!1,phase:r,percent:o,error:a||"Model not ready"}),!1)});var qe;export{c as ChatModelId,o as ChatStatus,i as LlmBackend,s as LlmPhase,a as LlmRole,r as LlmStatus,Be as createChat,Ie as preloadModel};
@@ -15,7 +15,9 @@ modelUrl: (repo: string, file: string) => string,
15
15
  /** Declared size of one download, zero when the host hides it */
16
16
  contentLength: (response: Response) => number,
17
17
  /** Whether any turn carries an image */
18
- hasImages: (messages: EngineMessage[]) => boolean;
18
+ hasImages: (messages: EngineMessage[]) => boolean,
19
+ /** Whether any turn carries audio */
20
+ hasAudio: (messages: EngineMessage[]) => boolean;
19
21
  declare const
20
22
  /** Map the host messages to OpenAI-style turns, bounded for local context */
21
23
  buildMessages: (messages: ChatMessage[], limits?: LlmLimits) => EngineMessage[],
@@ -32,4 +34,4 @@ streamReply: ({ model, logging, onReason, onText, onSpeed, start, stopped, first
32
34
  declare const
33
35
  /** Build the prompt and stream one reply through an engine */
34
36
  runEngineChat: ({ model, params, start, stopped, onStall, }: EngineRunParams) => Promise<string | undefined>;
35
- export { buildMessages, createThinkSplitter, logChatCall, runEngineChat, streamReply, replyOptions, defaultLlmLimits, resolveLlmLimits, hasImages, modelUrl, contentLength, };
37
+ export { buildMessages, createThinkSplitter, logChatCall, runEngineChat, streamReply, replyOptions, defaultLlmLimits, resolveLlmLimits, hasImages, hasAudio, modelUrl, contentLength, };
@@ -1,10 +1,10 @@
1
1
  import type { EngineMessage } from '../types';
2
2
  /** Square image side sent to an engine, a multiple of 48 */
3
3
  declare const imageSide = 288;
4
- /** Decode an image URL into bytes for an engine */
5
- declare const imageBuffer: (url: string) => Promise<ArrayBuffer>;
4
+ /** Decode a data URL or fetch a URL into bytes for an engine */
5
+ declare const contentBuffer: (url: string) => Promise<ArrayBuffer>;
6
6
  /** Center-crop one image URL to a square and resize it for an engine */
7
7
  declare const resizeImageUrl: (url: string) => Promise<string>;
8
8
  /** Center-crop and resize every image part before an engine sees it */
9
9
  declare const resizeImages: (messages: EngineMessage[]) => Promise<EngineMessage[]>;
10
- export { imageSide, imageBuffer, resizeImageUrl, resizeImages, };
10
+ export { imageSide, contentBuffer, resizeImageUrl, resizeImages, };
@@ -5,7 +5,7 @@ visionTokens = 70;
5
5
  /** Fetch one model file into the cache, once per URL */
6
6
  declare const cacheModel: (url: string, cache: Cache, signal: AbortSignal | undefined, onProgress: StartLlmParams[`onProgress`]) => Promise<void>;
7
7
  /** Map built messages to LiteRT turns */
8
- declare const toLitertMessages: (messages: EngineMessage[], keepImages?: boolean) => Promise<LitertMessage[]>;
8
+ declare const toLitertMessages: (messages: EngineMessage[], keepMedia?: boolean) => Promise<LitertMessage[]>;
9
9
  /** Whether the live conversation already holds the incoming history */
10
10
  declare const continuesHistory: (sent: EngineMessage[], history: EngineMessage[]) => boolean;
11
11
  /** Stream one LiteRT reply as engine chunks */
package/dist/models.d.ts CHANGED
@@ -14,5 +14,7 @@ defaultLlmModel: () => Promise<ChatModelId | undefined>,
14
14
  /** Resolve the requested model or the default of this device */
15
15
  resolveLlmModel: (id?: ChatModelId) => Promise<ChatModel | undefined>,
16
16
  /** Whether a model accepts images */
17
- supportsImages: (model: ChatModel) => boolean;
18
- export { llmModels, cpuContextWindow, litertContextWindow, findLlmModel, gpuAdapter, defaultLlmModel, resolveLlmModel, supportsImages, };
17
+ supportsImages: (model: ChatModel) => boolean,
18
+ /** Whether a model accepts audio */
19
+ supportsAudio: (model: ChatModel) => boolean;
20
+ export { llmModels, cpuContextWindow, litertContextWindow, findLlmModel, gpuAdapter, defaultLlmModel, resolveLlmModel, supportsImages, supportsAudio, };
@@ -1,2 +1,2 @@
1
1
  /*! For license information please see chat.node.min.js.LICENSE.txt */
2
- (()=>{"use strict";var e={};const t={};function n(a){const o=t[a];if(void 0!==o)return o.exports;const r=t[a]={exports:{}};return e[a](r,r.exports,n),r.exports}n.m=e,n.d=(e,t)=>{for(var a in t)n.o(t,a)&&!n.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:t[a]})},n.f={},n.e=e=>{const t=[];return n.f.require(e,t),Promise.all(t)},n.u=e=>({520:"engine-litert",924:"engine-wllama"}[e]+".node.min.js"),n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.r=e=>{Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{const e={792:1};n.f.require=(t,a)=>{if(!e[t]){const a=require("./"+n.u(t));e[t]||(t=>{const a=t.modules,o=t.ids,r=t.runtime;for(var i in a)n.o(a,i)&&(n.m[i]=a[i]);r&&r(n);for(var s=0;s<o.length;s++)e[o[s]]=1})(a)}}})();let a={};var o,r,i,s,c,l;n.r(a),n.d(a,{ChatModelId:()=>l,ChatStatus:()=>r,LlmBackend:()=>s,LlmPhase:()=>c,LlmRole:()=>o,LlmStatus:()=>i,createChat:()=>Re,preloadModel:()=>qe}),function(e){e.system="system",e.user="user",e.assistant="assistant"}(o||(o={})),function(e){e[e.online=0]="online",e[e.away=1]="away",e[e.offline=2]="offline"}(r||(r={})),function(e){e[e.idle=0]="idle",e[e.downloading=1]="downloading",e[e.ready=2]="ready",e[e.error=3]="error",e[e.unsupported=4]="unsupported"}(i||(i={})),function(e){e.cpu="cpu",e.gpu="gpu",e.none="none"}(s||(s={})),function(e){e.download="download",e.load="load"}(c||(c={})),function(e){e.qwen3_vl_2b="qwen3-vl-2b",e.gemma4_e2b="gemma4-e2b"}(l||(l={}));const d=[{id:l.qwen3_vl_2b,reasoning:!0,cpu:{repo:"Qwen/Qwen3-VL-2B-Thinking-GGUF",file:"Qwen3VL-2B-Thinking-Q4_K_M.gguf",mmproj:{file:"mmproj-Qwen3VL-2B-Thinking-Q8_0.gguf"}}},{id:l.gemma4_e2b,reasoning:!0,litert:{repo:"litert-community/gemma-4-E2B-it-litert-lm",file:"gemma-4-E2B-it.litertlm",vision:!0}}],g=e=>e?d.find(t=>t.id==e):void 0,u=async()=>(await(async()=>{const e="undefined"==typeof navigator?void 0:navigator.gpu;if(!e)return!1;try{return!!await e.requestAdapter({powerPreference:"high-performance"})}catch{return!1}})()?d.find(e=>!!e.litert):d.find(e=>!!e.cpu))?.id,m=async e=>g(e||await u()),f=e=>!!e.cpu?.mmproj||!!e.litert?.vision,p=require("node:path"),h=()=>{return e=(0,p.join)(__dirname,"../assets/wllama"),{wasm:(0,p.join)(e,"wllama.wasm"),compatWasm:(0,p.join)(e,"compat/wllama.wasm"),compatWorker:(0,p.join)(e,"compat/wllama.js")};var e},w=()=>{return e=(0,p.join)(__dirname,"../assets/litert"),{script:(0,p.join)(e,"litertlm_wasm_internal.js"),wasm:(0,p.join)(e,"litertlm_wasm_internal.wasm")};var e},y=288,b=async e=>{if(!e.startsWith("data:"))return await(await fetch(e)).arrayBuffer();const t=e.indexOf(","),n=t>=0?e.slice(0,t):"";if(t<0)throw new Error("Invalid image data");if(!n.includes(";base64"))return await(await fetch(e)).arrayBuffer();const a=atob(e.slice(t+1)),o=new Uint8Array(a.length);for(let e=0;e<a.length;e++)o[e]=a.charCodeAt(e);return o.buffer},v=async e=>{if(!e||"function"!=typeof fetch||"function"!=typeof createImageBitmap)return e;try{const t=await fetch(e),n=await createImageBitmap(await t.blob()),a=Math.min(n.width,n.height),o=Math.floor((n.width-a)/2),r=Math.floor((n.height-a)/2),i=((e,t)=>{if("undefined"!=typeof OffscreenCanvas)return new OffscreenCanvas(e,t);if("undefined"==typeof document)return;const n=document.createElement("canvas");return n.width=e,n.height=t,n})(y,y),s=i?i.getContext("2d"):null;if(!i||!s||a<1)return n.close(),e;if(s.drawImage(n,o,r,a,a,0,0,y,y),n.close(),(e=>"undefined"!=typeof OffscreenCanvas&&e instanceof OffscreenCanvas)(i)){const t=await i.convertToBlob({type:"image/jpeg",quality:.85});return await(e=>new Promise(t=>{if("undefined"==typeof FileReader)return void t("");const n=new FileReader;n.onerror=()=>t(""),n.onload=()=>t(`${n.result}`),n.readAsDataURL(e)}))(t)||e}return i.toDataURL("image/jpeg",.85)}catch{return e}},x={maxReplyTokens:250,maxThinkingTokens:128,maxReasoningTokens:320,maxImages:2,maxContentLength:1e3,promptCharBudget:1e4,maxThreads:6},k=(e,t,n)=>"number"==typeof e&&Number.isFinite(e)&&e>=n?Math.floor(e):t,M=(e={},t=!1)=>{const n=x;return{maxReplyTokens:k(e.maxReplyTokens,n.maxReplyTokens,1),maxThinkingTokens:k(e.maxThinkingTokens,n.maxThinkingTokens,1),maxReasoningTokens:k(e.maxReasoningTokens,t?1200:n.maxReasoningTokens,1),maxImages:k(e.maxImages,n.maxImages,0),maxContentLength:k(e.maxContentLength,n.maxContentLength,1),promptCharBudget:k(e.promptCharBudget,n.promptCharBudget,1),maxThreads:k(e.maxThreads,n.maxThreads,1)}},_=(e,t)=>({stream:!0,temperature:.7,max_tokens:t?e.maxReasoningTokens:e.maxReplyTokens}),T=(e,t)=>`https://huggingface.co/${e}/resolve/main/${t}`,E=e=>Number(e.headers.get("content-length"))||0,C=e=>e.some(e=>Array.isArray(e.content)&&e.content.some(e=>"image"==e.type)),L=["<|channel>thought\n","<|channel>thought","..."],P=["<channel|>","..."],j=(e,t)=>t.length?[...e?[{type:"text",text:e}]:[],...t.map(e=>({type:"image",url:e}))]:e,S=e=>"string"==typeof e?e?[{type:"text",text:e}]:[]:e,$=(e,t)=>{if("string"==typeof e&&"string"==typeof t)return`${e}\n${t}`;const n=[];for(const a of[...S(e),{type:"text",text:"\n"},...S(t)]){const e=n[n.length-1];"text"==a.type&&"text"==e?.type?e.text+=a.text:n.push(a)}return n};class R extends Error{}const q=async(e,t,n)=>{let a;try{return await Promise.race([e,new Promise((e,o)=>{a=setTimeout(()=>o(new R(n)),t)})])}finally{a&&clearTimeout(a)}},W=e=>{try{Promise.resolve(e.return?.()).catch(()=>{})}catch{}},B=async({model:e,logging:t,onReason:n,onText:a,onSpeed:o,start:r,stopped:i,firstTimeoutMs:s=18e4,idleTimeoutMs:c=3e4,onStall:l})=>{let d="",g="",u="",m=!1,f=0,p=0;const h=((e,t)=>{let n="",a=!1;const o=(e,t)=>{let n=-1,a="";for(const o of t){const t=e.indexOf(o);t>=0&&(n<0||t<n)&&(n=t,a=o)}return{at:n,tag:a}},r=(e,t)=>{let n="";for(const a of t)for(let t=Math.min(e.length,a.length-1);t>n.length;t--)if(e.endsWith(a.slice(0,t))){n=e.slice(e.length-t);break}return n};return{feed:i=>{let s=n+i;for(n="";s;){if(a){const{at:t,tag:i}=o(s,P);if(t>=0){t&&e(s.slice(0,t)),s=s.slice(t+i.length),a=!1;continue}const c=r(s,P);e(s.slice(0,s.length-c.length)),n=c;break}{const{at:e,tag:i}=o(s,L);if(e>=0){e&&t(s.slice(0,e)),s=s.slice(e+i.length),s.startsWith("\n")&&(s=s.slice(1)),a=!0;continue}const c=r(s,L);t(s.slice(0,s.length-c.length)),n=c;break}}},flush:()=>{n&&(a?e(n):t(n),n="")}}})(e=>{g+=e,n(e)},e=>{d+=e,a(e)});try{const e=(await q(r(),s,"Model did not start"))[Symbol.asyncIterator]();for(;;){if(i()){W(e);break}let a;try{a=await q(e.next(),m?c:s,m?"Model stopped responding":"Model did not reply")}catch(n){if(n instanceof R&&(d||g)){l?.(),W(e),t&&console.log("Local chat stalled",n);break}throw n}if(a.done)break;m=!0;const r=a.value?.choices?.[0],w=r?.delta,y="string"==typeof w?.reasoning_content?w.reasoning_content:"string"==typeof w?.reasoning?w.reasoning:"";if(r?.finish_reason&&(u=r.finish_reason),y&&(g+=y,n(y)),w?.content&&h.feed(w.content),y||w?.content){const e=performance.now();f||(p=e),f+=1,f>1&&e>p&&o?.(1e3*f/(e-p))}}h.flush()}catch(e){if(t&&console.log("Local chat failed",e),e instanceof R&&!i())throw e}const w=i()?void 0:d||void 0;return t&&(console.log("Local chat reply",e.id,w?"****":""),console.log("Local chat finish",e.id,u||"none",d.length,g.length)),w},I=async({model:e,params:t,start:n,stopped:a,onStall:r})=>{const i=await(async e=>{const t=[];for(const n of e){if("string"==typeof n.content){t.push(n);continue}const e=[];for(const t of n.content)e.push("image"==t.type?{type:"image",url:await v(t.url)}:t);t.push({role:"user",content:e})}return t})(((e,t=x)=>{const n=t.maxContentLength,a=e.filter(e=>"string"==typeof e.content&&!!e.content.trim()||!(e.role!=o.user||!e.images?.length)),r=[],i=[];let s=0,c=t.maxImages;for(let e=a.length-1;e>=0;e--){const l=a[e],d=("string"==typeof l.content?l.content:"").slice(0,n),g=l.role==o.user?(l.images||[]).filter(e=>"string"==typeof e&&!!e).slice(0,Math.max(0,c)):[];if(d||g.length){if(d&&s+d.length>t.promptCharBudget)break;s+=d.length,c-=g.length,l.role==o.system?r.unshift(d):l.role==o.assistant?i.unshift({role:"assistant",content:d}):i.unshift({role:"user",content:j(d,g)})}}for(;i.length&&"user"!=i[0].role;)i.shift();const l=[];for(const e of i){const t=l[l.length-1];"assistant"==t?.role&&"assistant"==e.role?t.content=`${t.content}\n${e.content}`:"user"==t?.role&&"user"==e.role?t.content=$(t.content,e.content):"assistant"==e.role?l.push({role:"assistant",content:e.content}):l.push({role:"user",content:e.content})}const d=r.join("\n\n"),g=[];return d&&g.push({role:"system",content:d}),g.push(...l),g})(t.messages,t.limits));return t.logging&&((e,t)=>{console.log("Local chat call",e.id,t.map(({role:e,content:t})=>({role:e,content:"****",...Array.isArray(t)?{images:t.filter(e=>"image"==e.type).length}:{}})))})(e,i),await B({model:e,logging:t.logging,onReason:t.onReason,onText:t.onText,onSpeed:t.onSpeed,start:()=>n(i),stopped:a,onStall:r})},z=33554432,A=new Map;let O=!1;const N=e=>{const t=(e.split("/").pop()||"model").replace(/[^\w.-]+/g,"_");let n=0;for(let t=0;t<e.length;t++)n=31*n+e.charCodeAt(t)>>>0;return`${n.toString(16)}-${t}`},F=e=>{const t=/^part-(\d+)$/.exec(e);return t?Number(t[1]):-1},U=async()=>{if(!O)if("undefined"!=typeof navigator&&navigator.storage?.getDirectory)try{return await navigator.storage.getDirectory()}catch{return}else O=!0},D=async(e,t)=>{try{return await e.getDirectoryHandle(t)}catch{return}},H=async e=>{if(e)try{const t=await e.getFile(),n=JSON.parse(await t.text());return{total:Number(n.total)||0,etag:`${n.etag||""}`,complete:!0===n.complete}}catch{return}},Q=async(e,t)=>{try{const n=await e.getFileHandle("meta.json",{create:!0}),a=await n.createWritable();await a.write(JSON.stringify(t)),await a.close()}catch{}},V=async function*(e){for await(const t of e)yield t},G=async e=>{try{for await(const[t,n]of V(e))"file"==n.kind&&F(t)>=0&&await e.removeEntry(t).catch(()=>{})}catch{}},J=async(e,t)=>{try{const n=await(await e.getFileHandle(t)).getFile(),a=await H(await e.getFileHandle(`${t}.json`).catch(()=>{}));if(!a)return;return{file:n,size:n.size,total:a.total,etag:a.etag,complete:a.total?n.size>=a.total:!0===a.complete}}catch{return}},K=async(e,t)=>{const n=await D(e,t);if(n){const e=await(async e=>await H(await e.getFileHandle("meta.json").catch(()=>{})))(n),t=await(async e=>{const t=[];try{for await(const[n,a]of V(e)){const e=F(n);if("file"!=a.kind||e<0)continue;const o=await a.getFile();t.push({part:{start:e,size:o.size},file:o})}}catch{return}const n=(e=>{const t=[];let n=0;for(const a of[...e].sort((e,t)=>e.start-t.start))if(a.size){if(a.start!=n)break;t.push(a),n+=a.size}return t})(t.map(e=>e.part)),a=new Set(n.map(e=>e.start)),o=t.filter(e=>a.has(e.part.start)).sort((e,t)=>e.part.start-t.part.start).map(e=>e.file);if(o.length)return{file:1==o.length?o[0]:new Blob(o),size:n.reduce((e,t)=>e+t.size,0)}})(n),a=e?.total||0,o=t?.size??0,r=t?.file??new Blob;return{file:a&&o>a?r.slice(0,a):r,size:o,total:a,etag:e?.etag||"",complete:a?o>=a:!0===e?.complete}}return await J(e,t)},X=(e,t)=>{const n=e.headers.get("content-range"),a=/bytes (\d+)-/.exec(n||""),o=/\/(\d+)\s*$/.exec(n||""),r=e.headers.get("etag")||"";if(206==e.status){const e=a?Number(a[1]):t.size,n=o?Number(o[1]):t.total;if(e!=t.size)return;if(t.total&&n&&t.total!=n)return;if(t.etag&&r&&t.etag!=r)return;return{start:e,total:n||t.total,etag:r||t.etag}}if(!t.etag||!r||t.etag==r)return{start:0,total:E(e)||t.total,etag:r||t.etag}},Y=async({dir:e,start:t,skip:n,total:a,body:o,onProgress:r,onChunk:i})=>{let s,l=t,d=0,g=n;const u=async()=>{const t=await e.getFileHandle((e=>`part-${e}`)(l),{create:!0});s=await t.createWritable(),d=0},m=async()=>{const e=s;s=void 0,e&&await e.close().catch(()=>{})};try{const e=o.getReader();for(;;){const{done:n,value:o}=await e.read();if(n)break;i();let f=0;for(g&&(f=Math.min(g,o.byteLength),g-=f);f<o.byteLength;){s||await u();const e=Math.min(z-d,o.byteLength-f);await s.write({type:"write",position:d,data:o.subarray(f,f+e)}),f+=e,d+=e,l+=e,d==z&&await m()}l>t&&r(a?l/a:0,c.download)}await m()}catch(e){throw await m(),e}return l},Z=e=>["NotAllowedError","NotSupportedError"].includes(e?.name||""),ee=e=>{const t=async()=>{const t=await U();if(t)try{return await t.getDirectoryHandle(e,{create:!0})}catch{return}};return{stored:async e=>{const n=await t();return n?await K(n,N(e)):void 0},download:async({url:n,signal:a,onProgress:o})=>{const r=`${e}|${n}`,i=A.get(r);if(i)return await i;const s=await t();if(!s)return;const l=(async(e,t,n,a)=>{const o=N(t),r=await J(e,o);if(r?.complete)return r;let i=await D(e,o);i||(await e.removeEntry(o).catch(()=>{}),await e.removeEntry(`${o}.json`).catch(()=>{}),i=await e.getDirectoryHandle(o,{create:!0}));let s=0,l=0;for(;;){if(n?.aborted)throw new Error("Cancelled");const r=await K(e,o);if(r?.complete)return r;const d=r?.size??0,g=r?.total||0,u=r?.etag||"";if(d>s)s=d,l=0;else if(++l>4)throw new Error("Model download failed");d&&g&&a(d/g,c.download);const m=g?Math.min(d+z,g)-1:d+z-1,f=new AbortController,p=()=>f.abort(),h={size:d,total:g,etag:u};let w,y=setTimeout(p,3e4);n?.addEventListener("abort",p,{once:!0});try{if(w=await fetch(t,{signal:f.signal,headers:{Range:`bytes=${d}-${m}`}}),416==w.status){if(await(w.body?.cancel().catch(()=>{})),g&&d>=g){await Q(i,{total:g,etag:u,complete:!0});continue}await G(i),await Q(i,{total:0,etag:"",complete:!1});continue}if(!w.ok||!w.body)throw await(w.body?.cancel().catch(()=>{})),new Error("Model download failed");const n=X(w,h);if(!n){await w.body.cancel().catch(()=>{}),await G(i),await Q(i,{total:0,etag:"",complete:!1});continue}await Q(i,{total:n.total,etag:n.etag,complete:!1});const r=0==n.start?d:0,s=await Y({dir:i,start:n.start+r,skip:r,total:n.total,body:w.body,onProgress:a,onChunk:()=>{clearTimeout(y),y=setTimeout(p,3e4)}});if(!n.total){await Q(i,{total:s,etag:n.etag,complete:!0});const t=await K(e,o);if(t)return t}}catch(e){if(n?.aborted)throw new Error("Cancelled");if(e instanceof DOMException&&["QuotaExceededError","NotAllowedError","NotSupportedError","InvalidStateError","TypeMismatchError","SecurityError"].includes(e.name))throw e;await new Promise(e=>setTimeout(e,250+500*l))}finally{clearTimeout(y),n?.removeEventListener("abort",p)}}})(s,n,a,o).finally(()=>{A.delete(r)});return A.set(r,l),await l},remove:async e=>{const n=await t();n&&await(async(e,t)=>{await e.removeEntry(t,{recursive:!0}).catch(()=>{}),await e.removeEntry(`${t}.json`).catch(()=>{})})(n,N(e))},purge:async()=>{const t=await U();t&&await t.removeEntry(e,{recursive:!0}).catch(()=>{})}}},te="litertlm-models",ne=ee("litert-store"),ae={num_output_candidates:1,wait_for_weight_uploads:!0,num_decode_steps_per_sync:3,sequence_batch_size:0,supported_lora_ranks:[],max_top_k:40,enable_decode_logits:!1,enable_external_embeddings:!1,use_submodel:!1,use_autosized_ringbuffers:!0};let oe=!1;const re=(...e)=>{oe&&console.log(...e)},ie=(...e)=>{oe&&console.error(...e)},se=async()=>{try{if("undefined"==typeof caches)return;return await caches.open(te)}catch{return}},ce=new Map,le=(e,t,n)=>{let a=0;return e.pipeThrough(new TransformStream({transform:(e,o)=>{a+=e.byteLength,n(t?a/t:0,c.download),o.enqueue(e)}}))},de=async(e,t=!0)=>{const n=[];for(const a of e){if("string"==typeof a.content){n.push({role:a.role,content:a.content});continue}const e=[];for(const n of a.content)e.push("text"==n.type?{type:"text",text:n.text}:t?{type:"image",data:await b(n.url)}:{type:"text",text:"[image]"});n.push({role:a.role,content:e})}return n},ge=(e,t)=>{if(e.role!=t.role)return!1;if("string"==typeof e.content||"string"==typeof t.content)return e.content==t.content;if(e.content.length!=t.content.length)return!1;for(let n=0;n<e.content.length;n++){const a=e.content[n],o=t.content[n];if(a.type!=o.type)return!1;if("text"==a.type&&"text"==o.type){if(a.text!=o.text)return!1}else if("image"==a.type&&"image"==o.type&&a.url!=o.url)return!1}return!0},ue=async function*({engine:e,mod:t,params:n,messages:a,session:o}){const r=a[a.length-1],i=_(n.limits,n.reasoning);if("user"!=r?.role)throw new Error("No user turn");const s=a.slice(0,-1),c=(await de([r]))[0];if(!c)throw new Error("Empty prompt");const l=!!o.conversation&&o.reasoning==n.reasoning&&o.maxTokens==i.max_tokens&&((e,t)=>{const n="system"==e[0]?.role?e[0]:void 0,a="system"==t[0]?.role?t[0]:void 0;if(!n!=!a)return!1;if(n&&a&&!ge(n,a))return!1;const o=n?e.slice(1):e,r=a?t.slice(1):t;if(!r.length||r.length>o.length)return!1;const i=o.length-r.length;for(let e=0;e<r.length;e++)if(!ge(o[i+e],r[e]))return!1;return!0})(o.sent,s);let d;if(l&&o.conversation)d=o.conversation;else{const a=await de(s,!1);o.conversation=void 0,o.sent=[],d=await e.createConversation({sessionConfig:{maxOutputTokens:i.max_tokens,samplerParams:{type:t.SamplerType.TOP_K,temperature:i.temperature,k:40,p:.95}},preface:{...a.length?{messages:a}:{},extra_context:{enable_thinking:n.reasoning}},prefillPrefaceOnInit:a.length>0}),o.conversation=d,o.reasoning=n.reasoning,o.maxTokens=i.max_tokens,o.sent=[...s]}o.sent.push(r);let g,u=!1,m="";const f=()=>{g&&g.cancel().catch(()=>{});try{d.cancel()}catch{}};n.signal?.addEventListener("abort",f,{once:!0});try{if(n.signal?.aborted)return;const e=d.sendMessageStreaming(c).getReader();for(g=e;;){const{done:t,value:n}=await e.read();if(t)break;const a={};let o="string"==typeof n.content?n.content:"";if(Array.isArray(n.content))for(const e of n.content)"text"==e.type&&(o+=e.text);o&&(a.content=o,m+=o);const r=n.channels?.thought;r&&(a.reasoning=r),(a.content||a.reasoning)&&(yield{choices:[{delta:a}]})}u=!0}finally{n.signal?.removeEventListener("abort",f),u&&m?o.sent.push({role:"assistant",content:m}):(o.conversation=void 0,o.sent=[])}},me={backend:s.gpu,startLLM:async({model:e,limits:t,onProgress:a,signal:o,logging:r})=>{oe=!0===r;const i=e.litert;if(!i)throw new Error("Model has no LiteRT artifact");const l=await n.e(520).then(()=>n(507)),d=globalThis,g=d.Module;d.Module={print:re,printErr:ie};try{await l.getOrLoadGlobalLiteRtLm(w().script)}finally{d.Module=g}if(o?.aborted)throw new Error("Cancelled");const u=T(i.repo,i.file),m=await se();let f;const p=m?await m.match(u):void 0;if(p?.body)f=p.body;else{const e=await ne.stored(u);e?.complete&&(f=e.file.stream())}if(!f){const e=await ne.download({url:u,signal:o,onProgress:a}).catch(e=>{if(o?.aborted)throw new Error("Cancelled");if(!Z(e))throw e;r&&console.log("Local model store failed",e)});e&&(f=e.file.stream())}if(!f&&m)try{await(async(e,t,n,a)=>{const o=ce.get(e);if(o)return await o;const r=(async()=>{const o=await fetch(e,{signal:n});if(!o.ok||!o.body)throw new Error("Model download failed");const r=E(o),i=le(o.body,r,a);await t.put(e,new Response(i,{headers:{"content-type":"application/octet-stream",...r?{"content-length":`${r}`}:{}}}))})().finally(()=>{ce.delete(e)});return ce.set(e,r),await r})(u,m,o,a);const e=await m.match(u);e?.body&&(f=e.body)}catch(e){if(o?.aborted)throw new Error("Cancelled");r&&console.log("Local model cache failed",e)}if(f||(f=await(async(e,t,n)=>{const a=await fetch(e,{signal:t});if(!a.ok||!a.body)throw new Error("Model download failed");return le(a.body,E(a),n)})(u,o,a)),o?.aborted)throw new Error("Cancelled");a(1,c.load);const h=await l.Engine.create((({model:e,limits:t,source:n,mod:a})=>({model:n,mainExecutorSettings:{maxNumTokens:4096,...e.litert?.vision?{maxNumImages:Math.max(1,t.maxImages)}:{},backendConfig:ae},...e.litert?.vision?{visionBackend:a.Backend.GPU,maxVisionTokensPerImage:70,minVisionTokensPerImage:70}:{}}))({model:e,limits:t,source:f,mod:l}));if(o?.aborted)throw await h.delete(),new Error("Cancelled");const y={sent:[],reasoning:!1,maxTokens:0};return{model:e,backend:s.gpu,contextSize:4096,chat:t=>(async({engine:e,model:t,mod:n,params:a,session:o})=>{oe=!0===a.logging;const r=()=>{try{o.conversation?.cancel()}catch{}o.conversation=void 0,o.sent=[]};try{return await I({model:t,params:a,stopped:()=>!!a.signal?.aborted,onStall:r,start:async r=>{if(a.signal?.aborted)throw new Error("Cancelled");if(C(r)&&!t.litert?.vision)throw new Error("Model does not accept images");return ue({engine:e,mod:n,params:a,messages:r,session:o})}})}catch(e){throw r(),e}})({engine:h,model:e,mod:l,params:t,session:y}),unload:async()=>{y.conversation=void 0,y.sent=[],await h.delete()}}},cached:async e=>{const t=e.litert;if(!t)return!1;const n=T(t.repo,t.file),[a,o]=await Promise.all([se(),ne.stored(n)]);if(o?.complete)return!0;try{return!(!a||!await a.match(n))}catch{return!1}},hasCache:async()=>{try{for(const e of d){const t=e.litert;if(!t)continue;if(await ne.stored(T(t.repo,t.file)))return!0}const e=await se();return!!e&&(await e.keys()).length>0}catch{return!1}},remove:async e=>{try{const t=e.litert;if(!t)return;const n=T(t.repo,t.file),a=await se();await ne.remove(n),a&&await a.delete(n)}catch{}},purge:async()=>{try{await ne.purge(),"undefined"!=typeof caches&&await caches.delete(te)}catch{}}},fe=ee("wllama-store");let pe,he,we="",ye=!1;const be=e=>`${e.wasm}|${e.compatWasm}|${e.compatWorker}`,ve=e=>{const t=h();return{wasm:e?.wasm||t.wasm,compatWasm:e?.compatWasm||t.compatWasm,compatWorker:e?.compatWorker||t.compatWorker}},xe=e=>{const t=e=>(...t)=>{ye&&("string"==typeof t[0]&&t[0].includes("ggml_webgpu: Failed to get an adapter")||e(...t))};return{...e,log:t(e.log),warn:t(e.warn),error:t(e.error)}},ke=async()=>{if(pe)return pe;const e=await n.e(924).then(()=>n(577)),t=he||ve(),a=new e.Wllama({default:t.wasm},{logger:xe(e.LoggerWithoutDebug)});return he||(he=t,we=be(t)),a.setCompat({wasm:t.compatWasm,worker:t.compatWorker}),pe=a,a},Me=async()=>{const e=pe;pe=void 0;try{await(e?.exit())}catch{}},_e=(e,t)=>e.name.includes(t)||!!e.metadata?.originalURL?.includes(t),Te=e=>{const t=e.cpu;return t?[{name:t.file,url:T(t.repo,t.file)},...t.mmproj?[{name:t.mmproj.file,url:T(t.repo,t.mmproj.file)}]:[]]:[]},Ee=async e=>{const t=Te(e);if(!t.length)return!1;for(const e of t){const t=await fe.stored(e.url);if(!t?.complete)return!1}return!0},Ce=async(e,t)=>{const n=await e.cacheManager.list();return Te(t).every(e=>n.some(t=>_e(t,e.name)))},Le=async({model:e,signal:t,onProgress:n})=>{const a=Te(e),o=[];for(let e=0;e<a.length;e++){if(t?.aborted)throw new Error("Cancelled");const r=await fe.download({url:a[e].url,signal:t,onProgress:(t,o)=>n((e+t)/a.length,o)});if(!r)return;o.push(r.file)}return o.length?o:void 0},Pe=async e=>{const t=[];for(const n of e){if("string"==typeof n.content){"system"==n.role?t.push({role:"system",content:n.content}):"assistant"==n.role?t.push({role:"assistant",content:n.content}):t.push({role:"user",content:n.content});continue}const e=[];for(const t of n.content)e.push("text"==t.type?{type:"text",text:t.text}:{type:"image",data:await b(t.url)});t.push({role:"user",content:e})}return t},je={backend:s.cpu,startLLM:async({model:e,limits:t,onProgress:n,signal:a,logging:o,assets:r})=>{ye=!0===o;const i=e.cpu;if(!i)throw new Error("Model has no CPU artifact");const l=ve(r),d=be(l);pe&&d!=we&&await Me(),he=l,we=d;const g=(u="undefined"!=typeof navigator&&navigator.hardwareConcurrency||4,m=t.maxThreads,Math.max(1,Math.min(Math.max(2,Math.min(8,(u||4)-1)),m)));var u,m;const f=await ke(),p=await Ee(e),h=i.mmproj?.file,w=T(i.repo,i.file),y=h?T(i.repo,h):"";if(a?.aborted)throw await Me(),new Error("Cancelled");if(!p&&h){const e=await f.cacheManager.list(),t=e.some(e=>_e(e,i.file)),n=e.some(e=>_e(e,h));t&&!n&&await f.cacheManager.deleteMany(e=>_e(e,i.file))}const b={n_ctx:8192,n_threads:g,reasoning_budget_tokens:t.maxThinkingTokens,n_gpu_layers:0,jinja:!0};try{let t=p?await Le({model:e,signal:a,onProgress:n}):void 0;t||p||await Ce(f,e).catch(()=>!1)||(t=await Le({model:e,signal:a,onProgress:n}).catch(e=>{if(a?.aborted)throw new Error("Cancelled");if(!Z(e))throw e;o&&console.log("Local model store failed",e)})),t?(n(1,c.load),await f.loadModel(t,b)):await f.loadModelFromUrl(y?{url:w,mmprojUrl:y}:w,{...b,useCache:!0,signal:a,progressCallback:({loaded:e,total:t})=>{const a=t?e/t:0;n(a,a>=1?c.load:c.download)}})}catch(e){throw await Me(),e}if(a?.aborted)throw await Me(),new Error("Cancelled");if(!f.getLoadedContextInfo().n_ctx)throw await Me(),new Error("Failed to create model context");return{model:e,backend:s.cpu,contextSize:8192,chat:t=>(async(e,t,n)=>{if(ye=!0===n.logging,e.isModelLoaded())return await I({model:t,params:n,stopped:()=>!!n.signal?.aborted,start:async a=>{if(C(a)&&!e.supportInputModality("image"))throw new Error("Model does not accept images");return await e.createChatCompletion({messages:await Pe(a),..._(n.limits,n.reasoning),abortSignal:n.signal,...t.reasoning?{chat_template_kwargs:{enable_thinking:n.reasoning}}:{}})}})})(f,e,t),unload:async()=>{if(pe==f)await Me();else try{await f.exit()}catch{}}}},cached:async e=>{try{return!!e.cpu&&(!!await Ee(e)||await Ce(await ke(),e))}catch{return!1}},hasCache:async()=>{try{for(const e of d.flatMap(e=>Te(e)))if(await fe.stored(e.url))return!0;const e=await ke();return(await e.cacheManager.list()).length>0}catch{return!1}},remove:async e=>{const t=Te(e);try{for(const e of t)await fe.remove(e.url)}catch{}try{const e=await ke();await e.cacheManager.deleteMany(e=>t.some(t=>_e(e,t.name)))}catch{}},purge:async()=>{try{await fe.purge()}catch{}try{const e=await ke();await e.cacheManager.clear()}catch{}}},Se=((e={})=>{const t=e.engines?.cpu||je,n=e.engines?.litert||me;let a,o,r,l,g,u,m,f=s.none,p=i.idle,h=[],w=0,y={percent:0,phase:c.download};const b=a=>{if(e.backend!=s.none)return e.backend==s.cpu?a.cpu?t:void 0:e.backend==s.gpu?a.litert?n:void 0:a.litert&&"undefined"!=typeof navigator&&navigator.gpu&&"undefined"!=typeof WebAssembly?n:!a.cpu||"undefined"==typeof window&&"undefined"==typeof self&&"undefined"==typeof Worker||"undefined"==typeof WebAssembly?void 0:t},v=(e,t)=>{y={percent:Math.max(0,Math.min(1,e)),phase:t||c.download};for(const e of[...h])e.report(y.percent,y.phase)},x=e=>{p=e},k=()=>{w++,u?.abort(),u=void 0,g=void 0,p==i.downloading&&x(i.idle)},M=(e,t)=>{const n=l;return n?new Promise(a=>{let o=!1;const r={report:e,settle:e=>{o||(o=!0,t?.removeEventListener("abort",i),a(e))}},i=()=>{(e=>{h=h.filter(t=>t!=e),queueMicrotask(()=>{!h.length&&l&&k()})})(r),r.settle(!1)};h.push(r),t?.aborted?i():t?.addEventListener("abort",i,{once:!0}),n.then(r.settle,()=>r.settle(!1))}):Promise.resolve(!1)},_=()=>p==i.ready&&!!a&&!!o;return{load:async({model:e,limits:t,assets:n,logging:s,signal:m,onProgress:p})=>{if(m?.aborted)return!1;if(_()&&o?.id==e.id)return!0;if(l&&g==e.id)return p(y.percent,y.phase),await M(p,m);l&&(k(),h=[],await l.catch(()=>!1));const T=++w,E=(async()=>{await(async()=>{try{if("undefined"==typeof navigator)return;await(navigator.storage?.persist?.())}catch{}})();const l=b(e);if(T!=w)return!1;if(!l)return x(i.unsupported),!1;if(await(async(e,t)=>{if(o&&o.id!=e.id){const e=o,t=r,n=a;a=void 0,o=void 0,r=void 0,await(n?.unload()),await(t?.remove(e))}if(await t.hasCache())for(const n of d)n.id!=e.id&&b(n)==t&&await t.remove(n)})(e,l),T!=w)return!1;const g=await l.cached(e);if(T!=w)return!1;x(i.downloading),v(0,c.download),g&&v(1,c.load);const m=new AbortController;u=m;try{const d=await l.startLLM({model:e,limits:t,logging:s,signal:m.signal,assets:n,onProgress:(e,t)=>{T==w&&v(e,t)}});return T!=w||m.signal.aborted?(await d.unload(),!1):(a=d,o=e,r=l,f=l.backend,x(i.ready),v(1,c.load),!0)}catch(e){return T!=w||(a=void 0,o=void 0,r=void 0,s&&console.log("Local model failed",e),x(i.error)),!1}finally{u==m&&(u=void 0)}})().finally(()=>{l==E&&(l=void 0,g=void 0,h=[])});return l=E,g=e.id,await M(p,m)},chat:async e=>{if(!_()||!a)return;for(;m;)await m.catch(()=>{});if(!_()||!a)return;const t=a.chat(e);m=t;try{return await t}finally{m==t&&(m=void 0)}},unload:async()=>{const e=a;k(),a=void 0,o=void 0,r=void 0,x(i.idle),await(e?.unload())},ready:_,loading:()=>!_()&&p==i.downloading,status:()=>p,backend:()=>f,model:()=>o}})(),$e=(e,t,n)=>{const a=e.status(),o=g(t);return{availability:a==i.ready?r.online:a==i.error||a==i.unsupported?r.offline:r.away,status:a,backend:e.backend(),supportsImages:!!o&&f(o),ready:e.ready(),loading:e.loading(),requesting:n.requesting,phase:n.phase,percent:n.percent,...n.error?{error:n.error}:{}}},Re=(e=>{let t,n=0;const a=t=>t.input.onStatus?.($e(e,t.model??t.input.model,t));return r=>{t?.controller.abort();const s={token:++n,input:r,controller:new AbortController,percent:e.ready()?100:0,phase:e.ready()?c.load:c.download,requesting:!1,cancelled:!1};return t=s,a(s),(async t=>{const{input:r}=t,i=!0===r.logging,s=await m(r.model);if(t.model=s?.id,t.token!=n)return;const l=r.messages.some(e=>e.role!=o.system&&"string"==typeof e.content&&!!e.content.trim()),d=r.messages.some(e=>e.role==o.user&&!!e.images?.some(e=>"string"==typeof e&&!!e)),g=l||d;if(!s)return t.error="Unknown model",void a(t);if(!g)return void a(t);if(d&&!l&&!f(s))return t.error="Model does not accept images",void a(t);const u=M(r.limits,!!s.litert),p=await e.load({model:s,limits:u,logging:i,signal:t.controller.signal,onProgress:(e,o)=>{t.token==n&&(t.percent=Math.round(100*e),t.phase=o,a(t))}}).catch(e=>(t.token==n&&(t.error=`${e?.message||`${e}`}`),!1));if(t.token!=n)return;if(t.cancelled)return;if(!p)return t.error||(t.error="Model not ready"),void a(t);const h=f(s),w=d&&!h?r.messages.map(({images:e,...t})=>t):r.messages;if(d&&!h&&!l)return t.error="Model does not accept images",void a(t);t.requesting=!0,t.error=void 0,t.percent=100,t.phase=c.load,a(t);let y,b="",v="";try{y=await e.chat({messages:w,limits:u,reasoning:!0===r.reasoning,logging:i,onReason:e=>{t.token!=n||t.cancelled||(b+=e,r.onThinking?.(b))},onText:e=>{t.token!=n||t.cancelled||(v+=e,r.onResponse?.(v))},onSpeed:e=>{t.token!=n||t.cancelled||r.onSpeed?.(e)},signal:t.controller.signal})}catch(e){if(t.token!=n)return;return t.requesting=!1,t.error=`${e?.message||`${e}`}`,void a(t)}t.token==n&&(t.requesting=!1,y||t.cancelled||(t.error="No reply"),a(t))})(s),()=>(t=>{t.token==n&&(t.cancelled=!0,t.controller.abort(),e.status()!=i.ready?queueMicrotask(()=>a(t)):e.unload().then(()=>a(t)))})(s)}})(Se),qe=(We=Se,async(e={})=>{const t=await m(e.model),n=n=>e.onStatus?.($e(We,t?.id,n));if(!t)return n({requesting:!1,phase:c.download,percent:0,error:"Unknown model"}),!1;let a,o=0,r=c.download;const i=We.ready()&&We.model()?.id==t.id;return await We.load({model:t,limits:M(e.limits,!!t.litert),logging:!0===e.logging,onProgress:(e,t)=>{o=Math.round(100*e),r=t,n({requesting:!1,phase:r,percent:o})}}).catch(e=>(a=`${e?.message||`${e}`}`,!1))?(i&&n({requesting:!1,phase:c.load,percent:100}),!0):(n({requesting:!1,phase:r,percent:o,error:a||"Model not ready"}),!1)});var We;module.exports=a})();
2
+ (()=>{"use strict";var e={};const t={};function n(a){const o=t[a];if(void 0!==o)return o.exports;const r=t[a]={exports:{}};return e[a](r,r.exports,n),r.exports}n.m=e,n.d=(e,t)=>{for(var a in t)n.o(t,a)&&!n.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:t[a]})},n.f={},n.e=e=>{const t=[];return n.f.require(e,t),Promise.all(t)},n.u=e=>({520:"engine-litert",924:"engine-wllama"}[e]+".node.min.js"),n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.r=e=>{Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{const e={792:1};n.f.require=(t,a)=>{if(!e[t]){const a=require("./"+n.u(t));e[t]||(t=>{const a=t.modules,o=t.ids,r=t.runtime;for(var i in a)n.o(a,i)&&(n.m[i]=a[i]);r&&r(n);for(var s=0;s<o.length;s++)e[o[s]]=1})(a)}}})();let a={};var o,r,i,s,c,l;n.r(a),n.d(a,{ChatModelId:()=>l,ChatStatus:()=>r,LlmBackend:()=>s,LlmPhase:()=>c,LlmRole:()=>o,LlmStatus:()=>i,createChat:()=>Be,preloadModel:()=>We}),function(e){e.system="system",e.user="user",e.assistant="assistant"}(o||(o={})),function(e){e[e.online=0]="online",e[e.away=1]="away",e[e.offline=2]="offline"}(r||(r={})),function(e){e[e.idle=0]="idle",e[e.downloading=1]="downloading",e[e.ready=2]="ready",e[e.error=3]="error",e[e.unsupported=4]="unsupported"}(i||(i={})),function(e){e.cpu="cpu",e.gpu="gpu",e.none="none"}(s||(s={})),function(e){e.download="download",e.load="load"}(c||(c={})),function(e){e.qwen3_vl_2b="qwen3-vl-2b",e.gemma4_e2b="gemma4-e2b"}(l||(l={}));const d=[{id:l.qwen3_vl_2b,reasoning:!0,cpu:{repo:"Qwen/Qwen3-VL-2B-Thinking-GGUF",file:"Qwen3VL-2B-Thinking-Q4_K_M.gguf",mmproj:{file:"mmproj-Qwen3VL-2B-Thinking-Q8_0.gguf"}}},{id:l.gemma4_e2b,reasoning:!0,litert:{repo:"litert-community/gemma-4-E2B-it-litert-lm",file:"gemma-4-E2B-it.litertlm",vision:!0,audio:!0}}],u=e=>e?d.find(t=>t.id==e):void 0,g=async()=>(await(async()=>{const e="undefined"==typeof navigator?void 0:navigator.gpu;if(!e)return!1;try{return!!await e.requestAdapter({powerPreference:"high-performance"})}catch{return!1}})()?d.find(e=>!!e.litert):d.find(e=>!!e.cpu))?.id,m=async e=>u(e||await g()),f=e=>!!e.cpu?.mmproj||!!e.litert?.vision,p=e=>!!e.cpu?.mmproj?.audio||!!e.litert?.audio,h=require("node:path"),w=()=>{return e=(0,h.join)(__dirname,"../assets/wllama"),{wasm:(0,h.join)(e,"wllama.wasm"),compatWasm:(0,h.join)(e,"compat/wllama.wasm"),compatWorker:(0,h.join)(e,"compat/wllama.js")};var e},y=()=>{return e=(0,h.join)(__dirname,"../assets/litert"),{script:(0,h.join)(e,"litertlm_wasm_internal.js"),wasm:(0,h.join)(e,"litertlm_wasm_internal.wasm")};var e},b=288,v=async e=>{if(!e.startsWith("data:"))return await(await fetch(e)).arrayBuffer();const t=e.indexOf(","),n=t>=0?e.slice(0,t):"";if(t<0)throw new Error("Invalid image data");if(!n.includes(";base64"))return await(await fetch(e)).arrayBuffer();const a=atob(e.slice(t+1)),o=new Uint8Array(a.length);for(let e=0;e<a.length;e++)o[e]=a.charCodeAt(e);return o.buffer},x=async e=>{if(!e||"function"!=typeof fetch||"function"!=typeof createImageBitmap)return e;try{const t=await fetch(e),n=await createImageBitmap(await t.blob()),a=Math.min(n.width,n.height),o=Math.floor((n.width-a)/2),r=Math.floor((n.height-a)/2),i=((e,t)=>{if("undefined"!=typeof OffscreenCanvas)return new OffscreenCanvas(e,t);if("undefined"==typeof document)return;const n=document.createElement("canvas");return n.width=e,n.height=t,n})(b,b),s=i?i.getContext("2d"):null;if(!i||!s||a<1)return n.close(),e;if(s.drawImage(n,o,r,a,a,0,0,b,b),n.close(),(e=>"undefined"!=typeof OffscreenCanvas&&e instanceof OffscreenCanvas)(i)){const t=await i.convertToBlob({type:"image/jpeg",quality:.85});return await(e=>new Promise(t=>{if("undefined"==typeof FileReader)return void t("");const n=new FileReader;n.onerror=()=>t(""),n.onload=()=>t(`${n.result}`),n.readAsDataURL(e)}))(t)||e}return i.toDataURL("image/jpeg",.85)}catch{return e}},k={maxReplyTokens:250,maxThinkingTokens:128,maxReasoningTokens:320,maxImages:2,maxAudio:1,maxContentLength:1e3,promptCharBudget:1e4,maxThreads:6},M=(e,t,n)=>"number"==typeof e&&Number.isFinite(e)&&e>=n?Math.floor(e):t,_=(e={},t=!1)=>{const n=k;return{maxReplyTokens:M(e.maxReplyTokens,n.maxReplyTokens,1),maxThinkingTokens:M(e.maxThinkingTokens,n.maxThinkingTokens,1),maxReasoningTokens:M(e.maxReasoningTokens,t?1200:n.maxReasoningTokens,1),maxImages:M(e.maxImages,n.maxImages,0),maxAudio:M(e.maxAudio,n.maxAudio,0),maxContentLength:M(e.maxContentLength,n.maxContentLength,1),promptCharBudget:M(e.promptCharBudget,n.promptCharBudget,1),maxThreads:M(e.maxThreads,n.maxThreads,1)}},T=(e,t)=>({stream:!0,temperature:.7,max_tokens:t?e.maxReasoningTokens:e.maxReplyTokens}),E=(e,t)=>`https://huggingface.co/${e}/resolve/main/${t}`,C=e=>Number(e.headers.get("content-length"))||0,L=(e,t)=>e.some(e=>Array.isArray(e.content)&&e.content.some(e=>e.type==t)),P=e=>L(e,"image"),j=e=>L(e,"audio"),S=["<|channel>thought\n","<|channel>thought","..."],$=["<channel|>","..."],R=(e,t,n)=>t.length||n.length?[...e?[{type:"text",text:e}]:[],...t.map(e=>({type:"image",url:e})),...n.map(e=>({type:"audio",url:e}))]:e,A=e=>"string"==typeof e?e?[{type:"text",text:e}]:[]:e,q=(e,t)=>{if("string"==typeof e&&"string"==typeof t)return`${e}\n${t}`;const n=[];for(const a of[...A(e),{type:"text",text:"\n"},...A(t)]){const e=n[n.length-1];"text"==a.type&&"text"==e?.type?e.text+=a.text:n.push(a)}return n};class B extends Error{}const W=async(e,t,n)=>{let a;try{return await Promise.race([e,new Promise((e,o)=>{a=setTimeout(()=>o(new B(n)),t)})])}finally{a&&clearTimeout(a)}},I=e=>{try{Promise.resolve(e.return?.()).catch(()=>{})}catch{}},z=async({model:e,logging:t,onReason:n,onText:a,onSpeed:o,start:r,stopped:i,firstTimeoutMs:s=18e4,idleTimeoutMs:c=3e4,onStall:l})=>{let d="",u="",g="",m=!1,f=0,p=0;const h=((e,t)=>{let n="",a=!1;const o=(e,t)=>{let n=-1,a="";for(const o of t){const t=e.indexOf(o);t>=0&&(n<0||t<n)&&(n=t,a=o)}return{at:n,tag:a}},r=(e,t)=>{let n="";for(const a of t)for(let t=Math.min(e.length,a.length-1);t>n.length;t--)if(e.endsWith(a.slice(0,t))){n=e.slice(e.length-t);break}return n};return{feed:i=>{let s=n+i;for(n="";s;){if(a){const{at:t,tag:i}=o(s,$);if(t>=0){t&&e(s.slice(0,t)),s=s.slice(t+i.length),a=!1;continue}const c=r(s,$);e(s.slice(0,s.length-c.length)),n=c;break}{const{at:e,tag:i}=o(s,S);if(e>=0){e&&t(s.slice(0,e)),s=s.slice(e+i.length),s.startsWith("\n")&&(s=s.slice(1)),a=!0;continue}const c=r(s,S);t(s.slice(0,s.length-c.length)),n=c;break}}},flush:()=>{n&&(a?e(n):t(n),n="")}}})(e=>{u+=e,n(e)},e=>{d+=e,a(e)});try{const e=(await W(r(),s,"Model did not start"))[Symbol.asyncIterator]();for(;;){if(i()){I(e);break}let a;try{a=await W(e.next(),m?c:s,m?"Model stopped responding":"Model did not reply")}catch(n){if(n instanceof B&&(d||u)){l?.(),I(e),t&&console.log("Local chat stalled",n);break}throw n}if(a.done)break;m=!0;const r=a.value?.choices?.[0],w=r?.delta,y="string"==typeof w?.reasoning_content?w.reasoning_content:"string"==typeof w?.reasoning?w.reasoning:"";if(r?.finish_reason&&(g=r.finish_reason),y&&(u+=y,n(y)),w?.content&&h.feed(w.content),y||w?.content){const e=performance.now();f||(p=e),f+=1,f>1&&e>p&&o?.(1e3*f/(e-p))}}h.flush()}catch(e){if(t&&console.log("Local chat failed",e),e instanceof B&&!i())throw e}const w=i()?void 0:d||void 0;return t&&(console.log("Local chat reply",e.id,w?"****":""),console.log("Local chat finish",e.id,g||"none",d.length,u.length)),w},O=async({model:e,params:t,start:n,stopped:a,onStall:r})=>{const i=await(async e=>{const t=[];for(const n of e){if("string"==typeof n.content){t.push(n);continue}const e=[];for(const t of n.content)e.push("image"==t.type?{type:"image",url:await x(t.url)}:t);t.push({role:"user",content:e})}return t})(((e,t=k)=>{const n=t.maxContentLength,a=e.filter(e=>"string"==typeof e.content&&!!e.content.trim()||!(e.role!=o.user||!e.images?.length&&!e.audio?.length)),r=[],i=[];let s=0,c=t.maxImages,l=t.maxAudio;for(let e=a.length-1;e>=0;e--){const d=a[e],u=("string"==typeof d.content?d.content:"").slice(0,n),g=d.role==o.user?(d.images||[]).filter(e=>"string"==typeof e&&!!e).slice(0,Math.max(0,c)):[],m=d.role==o.user?(d.audio||[]).filter(e=>"string"==typeof e&&!!e).slice(0,Math.max(0,l)):[];if(u||g.length||m.length){if(u&&s+u.length>t.promptCharBudget)break;s+=u.length,c-=g.length,l-=m.length,d.role==o.system?r.unshift(u):d.role==o.assistant?i.unshift({role:"assistant",content:u}):i.unshift({role:"user",content:R(u,g,m)})}}for(;i.length&&"user"!=i[0].role;)i.shift();const d=[];for(const e of i){const t=d[d.length-1];"assistant"==t?.role&&"assistant"==e.role?t.content=`${t.content}\n${e.content}`:"user"==t?.role&&"user"==e.role?t.content=q(t.content,e.content):"assistant"==e.role?d.push({role:"assistant",content:e.content}):d.push({role:"user",content:e.content})}const u=r.join("\n\n"),g=[];return u&&g.push({role:"system",content:u}),g.push(...d),g})(t.messages,t.limits));return t.logging&&((e,t)=>{console.log("Local chat call",e.id,t.map(({role:e,content:t})=>({role:e,content:"****",...Array.isArray(t)?{images:t.filter(e=>"image"==e.type).length,audio:t.filter(e=>"audio"==e.type).length}:{}})))})(e,i),await z({model:e,logging:t.logging,onReason:t.onReason,onText:t.onText,onSpeed:t.onSpeed,start:()=>n(i),stopped:a,onStall:r})},N=33554432,F=new Map;let U=!1;const D=e=>{const t=(e.split("/").pop()||"model").replace(/[^\w.-]+/g,"_");let n=0;for(let t=0;t<e.length;t++)n=31*n+e.charCodeAt(t)>>>0;return`${n.toString(16)}-${t}`},H=e=>{const t=/^part-(\d+)$/.exec(e);return t?Number(t[1]):-1},Q=async()=>{if(!U)if("undefined"!=typeof navigator&&navigator.storage?.getDirectory)try{return await navigator.storage.getDirectory()}catch{return}else U=!0},G=async(e,t)=>{try{return await e.getDirectoryHandle(t)}catch{return}},V=async e=>{if(e)try{const t=await e.getFile(),n=JSON.parse(await t.text());return{total:Number(n.total)||0,etag:`${n.etag||""}`,complete:!0===n.complete}}catch{return}},J=async(e,t)=>{try{const n=await e.getFileHandle("meta.json",{create:!0}),a=await n.createWritable();await a.write(JSON.stringify(t)),await a.close()}catch{}},K=async function*(e){for await(const t of e)yield t},X=async e=>{try{for await(const[t,n]of K(e))"file"==n.kind&&H(t)>=0&&await e.removeEntry(t).catch(()=>{})}catch{}},Y=async(e,t)=>{try{const n=await(await e.getFileHandle(t)).getFile(),a=await V(await e.getFileHandle(`${t}.json`).catch(()=>{}));if(!a)return;return{file:n,size:n.size,total:a.total,etag:a.etag,complete:a.total?n.size>=a.total:!0===a.complete}}catch{return}},Z=async(e,t)=>{const n=await G(e,t);if(n){const e=await(async e=>await V(await e.getFileHandle("meta.json").catch(()=>{})))(n),t=await(async e=>{const t=[];try{for await(const[n,a]of K(e)){const e=H(n);if("file"!=a.kind||e<0)continue;const o=await a.getFile();t.push({part:{start:e,size:o.size},file:o})}}catch{return}const n=(e=>{const t=[];let n=0;for(const a of[...e].sort((e,t)=>e.start-t.start))if(a.size){if(a.start!=n)break;t.push(a),n+=a.size}return t})(t.map(e=>e.part)),a=new Set(n.map(e=>e.start)),o=t.filter(e=>a.has(e.part.start)).sort((e,t)=>e.part.start-t.part.start).map(e=>e.file);if(o.length)return{file:1==o.length?o[0]:new Blob(o),size:n.reduce((e,t)=>e+t.size,0)}})(n),a=e?.total||0,o=t?.size??0,r=t?.file??new Blob;return{file:a&&o>a?r.slice(0,a):r,size:o,total:a,etag:e?.etag||"",complete:a?o>=a:!0===e?.complete}}return await Y(e,t)},ee=(e,t)=>{const n=e.headers.get("content-range"),a=/bytes (\d+)-/.exec(n||""),o=/\/(\d+)\s*$/.exec(n||""),r=e.headers.get("etag")||"";if(206==e.status){const e=a?Number(a[1]):t.size,n=o?Number(o[1]):t.total;if(e!=t.size)return;if(t.total&&n&&t.total!=n)return;if(t.etag&&r&&t.etag!=r)return;return{start:e,total:n||t.total,etag:r||t.etag}}if(!t.etag||!r||t.etag==r)return{start:0,total:C(e)||t.total,etag:r||t.etag}},te=async({dir:e,start:t,skip:n,total:a,body:o,onProgress:r,onChunk:i})=>{let s,l=t,d=0,u=n;const g=async()=>{const t=await e.getFileHandle((e=>`part-${e}`)(l),{create:!0});s=await t.createWritable(),d=0},m=async()=>{const e=s;s=void 0,e&&await e.close().catch(()=>{})};try{const e=o.getReader();for(;;){const{done:n,value:o}=await e.read();if(n)break;i();let f=0;for(u&&(f=Math.min(u,o.byteLength),u-=f);f<o.byteLength;){s||await g();const e=Math.min(N-d,o.byteLength-f);await s.write({type:"write",position:d,data:o.subarray(f,f+e)}),f+=e,d+=e,l+=e,d==N&&await m()}l>t&&r(a?l/a:0,c.download)}await m()}catch(e){throw await m(),e}return l},ne=e=>["NotAllowedError","NotSupportedError"].includes(e?.name||""),ae=e=>{const t=async()=>{const t=await Q();if(t)try{return await t.getDirectoryHandle(e,{create:!0})}catch{return}};return{stored:async e=>{const n=await t();return n?await Z(n,D(e)):void 0},download:async({url:n,signal:a,onProgress:o})=>{const r=`${e}|${n}`,i=F.get(r);if(i)return await i;const s=await t();if(!s)return;const l=(async(e,t,n,a)=>{const o=D(t),r=await Y(e,o);if(r?.complete)return r;let i=await G(e,o);i||(await e.removeEntry(o).catch(()=>{}),await e.removeEntry(`${o}.json`).catch(()=>{}),i=await e.getDirectoryHandle(o,{create:!0}));let s=0,l=0;for(;;){if(n?.aborted)throw new Error("Cancelled");const r=await Z(e,o);if(r?.complete)return r;const d=r?.size??0,u=r?.total||0,g=r?.etag||"";if(d>s)s=d,l=0;else if(++l>4)throw new Error("Model download failed");d&&u&&a(d/u,c.download);const m=u?Math.min(d+N,u)-1:d+N-1,f=new AbortController,p=()=>f.abort(),h={size:d,total:u,etag:g};let w,y=setTimeout(p,3e4);n?.addEventListener("abort",p,{once:!0});try{if(w=await fetch(t,{signal:f.signal,headers:{Range:`bytes=${d}-${m}`}}),416==w.status){if(await(w.body?.cancel().catch(()=>{})),u&&d>=u){await J(i,{total:u,etag:g,complete:!0});continue}await X(i),await J(i,{total:0,etag:"",complete:!1});continue}if(!w.ok||!w.body)throw await(w.body?.cancel().catch(()=>{})),new Error("Model download failed");const n=ee(w,h);if(!n){await w.body.cancel().catch(()=>{}),await X(i),await J(i,{total:0,etag:"",complete:!1});continue}await J(i,{total:n.total,etag:n.etag,complete:!1});const r=0==n.start?d:0,s=await te({dir:i,start:n.start+r,skip:r,total:n.total,body:w.body,onProgress:a,onChunk:()=>{clearTimeout(y),y=setTimeout(p,3e4)}});if(!n.total){await J(i,{total:s,etag:n.etag,complete:!0});const t=await Z(e,o);if(t)return t}}catch(e){if(n?.aborted)throw new Error("Cancelled");if(e instanceof DOMException&&["QuotaExceededError","NotAllowedError","NotSupportedError","InvalidStateError","TypeMismatchError","SecurityError"].includes(e.name))throw e;await new Promise(e=>setTimeout(e,250+500*l))}finally{clearTimeout(y),n?.removeEventListener("abort",p)}}})(s,n,a,o).finally(()=>{F.delete(r)});return F.set(r,l),await l},remove:async e=>{const n=await t();n&&await(async(e,t)=>{await e.removeEntry(t,{recursive:!0}).catch(()=>{}),await e.removeEntry(`${t}.json`).catch(()=>{})})(n,D(e))},purge:async()=>{const t=await Q();t&&await t.removeEntry(e,{recursive:!0}).catch(()=>{})}}},oe="litertlm-models",re=ae("litert-store"),ie={num_output_candidates:1,wait_for_weight_uploads:!0,num_decode_steps_per_sync:3,sequence_batch_size:0,supported_lora_ranks:[],max_top_k:40,enable_decode_logits:!1,enable_external_embeddings:!1,use_submodel:!1,use_autosized_ringbuffers:!0};let se=!1;const ce=(...e)=>{se&&console.log(...e)},le=(...e)=>{se&&console.error(...e)},de=async()=>{try{if("undefined"==typeof caches)return;return await caches.open(oe)}catch{return}},ue=new Map,ge=(e,t,n)=>{let a=0;return e.pipeThrough(new TransformStream({transform:(e,o)=>{a+=e.byteLength,n(t?a/t:0,c.download),o.enqueue(e)}}))},me=async(e,t=!0)=>{const n=[];for(const a of e){if("string"==typeof a.content){n.push({role:a.role,content:a.content});continue}const e=[];for(const n of a.content)"text"!=n.type?t?e.push({type:n.type,data:await v(n.url)}):e.push({type:"text",text:`[${n.type}]`}):e.push({type:"text",text:n.text});n.push({role:a.role,content:e})}return n},fe=(e,t)=>{if(e.role!=t.role)return!1;if("string"==typeof e.content||"string"==typeof t.content)return e.content==t.content;if(e.content.length!=t.content.length)return!1;for(let n=0;n<e.content.length;n++){const a=e.content[n],o=t.content[n];if(a.type!=o.type)return!1;if("text"==a.type&&"text"==o.type){if(a.text!=o.text)return!1}else if(("image"==a.type&&"image"==o.type||"audio"==a.type&&"audio"==o.type)&&a.url!=o.url)return!1}return!0},pe=async function*({engine:e,mod:t,params:n,messages:a,session:o}){const r=a[a.length-1],i=T(n.limits,n.reasoning);if("user"!=r?.role)throw new Error("No user turn");const s=a.slice(0,-1),c=(await me([r]))[0];if(!c)throw new Error("Empty prompt");const l=!!o.conversation&&o.reasoning==n.reasoning&&o.maxTokens==i.max_tokens&&((e,t)=>{const n="system"==e[0]?.role?e[0]:void 0,a="system"==t[0]?.role?t[0]:void 0;if(!n!=!a)return!1;if(n&&a&&!fe(n,a))return!1;const o=n?e.slice(1):e,r=a?t.slice(1):t;if(!r.length||r.length>o.length)return!1;const i=o.length-r.length;for(let e=0;e<r.length;e++)if(!fe(o[i+e],r[e]))return!1;return!0})(o.sent,s);let d;if(l&&o.conversation)d=o.conversation;else{const a=await me(s,!1);o.conversation=void 0,o.sent=[],d=await e.createConversation({sessionConfig:{maxOutputTokens:i.max_tokens,samplerParams:{type:t.SamplerType.TOP_K,temperature:i.temperature,k:40,p:.95}},preface:{...a.length?{messages:a}:{},extra_context:{enable_thinking:n.reasoning}},prefillPrefaceOnInit:a.length>0}),o.conversation=d,o.reasoning=n.reasoning,o.maxTokens=i.max_tokens,o.sent=[...s]}o.sent.push(r);let u,g=!1,m="";const f=()=>{u&&u.cancel().catch(()=>{});try{d.cancel()}catch{}};n.signal?.addEventListener("abort",f,{once:!0});try{if(n.signal?.aborted)return;const e=d.sendMessageStreaming(c).getReader();for(u=e;;){const{done:t,value:n}=await e.read();if(t)break;const a={};let o="string"==typeof n.content?n.content:"";if(Array.isArray(n.content))for(const e of n.content)"text"==e.type&&(o+=e.text);o&&(a.content=o,m+=o);const r=n.channels?.thought;r&&(a.reasoning=r),(a.content||a.reasoning)&&(yield{choices:[{delta:a}]})}g=!0}finally{n.signal?.removeEventListener("abort",f),g&&m?o.sent.push({role:"assistant",content:m}):(o.conversation=void 0,o.sent=[])}},he={backend:s.gpu,startLLM:async({model:e,limits:t,onProgress:a,signal:o,logging:r})=>{se=!0===r;const i=e.litert;if(!i)throw new Error("Model has no LiteRT artifact");const l=await n.e(520).then(()=>n(507)),d=globalThis,u=d.Module;d.Module={print:ce,printErr:le};try{await l.getOrLoadGlobalLiteRtLm(y().script)}finally{d.Module=u}if(o?.aborted)throw new Error("Cancelled");const g=E(i.repo,i.file),m=await de();let f;const p=m?await m.match(g):void 0;if(p?.body)f=p.body;else{const e=await re.stored(g);e?.complete&&(f=e.file.stream())}if(!f){const e=await re.download({url:g,signal:o,onProgress:a}).catch(e=>{if(o?.aborted)throw new Error("Cancelled");if(!ne(e))throw e;r&&console.log("Local model store failed",e)});e&&(f=e.file.stream())}if(!f&&m)try{await(async(e,t,n,a)=>{const o=ue.get(e);if(o)return await o;const r=(async()=>{const o=await fetch(e,{signal:n});if(!o.ok||!o.body)throw new Error("Model download failed");const r=C(o),i=ge(o.body,r,a);await t.put(e,new Response(i,{headers:{"content-type":"application/octet-stream",...r?{"content-length":`${r}`}:{}}}))})().finally(()=>{ue.delete(e)});return ue.set(e,r),await r})(g,m,o,a);const e=await m.match(g);e?.body&&(f=e.body)}catch(e){if(o?.aborted)throw new Error("Cancelled");r&&console.log("Local model cache failed",e)}if(f||(f=await(async(e,t,n)=>{const a=await fetch(e,{signal:t});if(!a.ok||!a.body)throw new Error("Model download failed");return ge(a.body,C(a),n)})(g,o,a)),o?.aborted)throw new Error("Cancelled");a(1,c.load);const h=await l.Engine.create((({model:e,limits:t,source:n,mod:a})=>({model:n,mainExecutorSettings:{maxNumTokens:4096,...e.litert?.vision?{maxNumImages:Math.max(1,t.maxImages)}:{},backendConfig:ie},...e.litert?.vision?{visionBackend:a.Backend.GPU,maxVisionTokensPerImage:70,minVisionTokensPerImage:70}:{},...e.litert?.audio?{audioBackend:a.Backend.GPU}:{}}))({model:e,limits:t,source:f,mod:l}));if(o?.aborted)throw await h.delete(),new Error("Cancelled");const w={sent:[],reasoning:!1,maxTokens:0};return{model:e,backend:s.gpu,contextSize:4096,chat:t=>(async({engine:e,model:t,mod:n,params:a,session:o})=>{se=!0===a.logging;const r=()=>{try{o.conversation?.cancel()}catch{}o.conversation=void 0,o.sent=[]};try{return await O({model:t,params:a,stopped:()=>!!a.signal?.aborted,onStall:r,start:async r=>{if(a.signal?.aborted)throw new Error("Cancelled");if(P(r)&&!t.litert?.vision)throw new Error("Model does not accept images");if(j(r)&&!t.litert?.audio)throw new Error("Model does not accept audio");return pe({engine:e,mod:n,params:a,messages:r,session:o})}})}catch(e){throw r(),e}})({engine:h,model:e,mod:l,params:t,session:w}),unload:async()=>{w.conversation=void 0,w.sent=[],await h.delete()}}},cached:async e=>{const t=e.litert;if(!t)return!1;const n=E(t.repo,t.file),[a,o]=await Promise.all([de(),re.stored(n)]);if(o?.complete)return!0;try{return!(!a||!await a.match(n))}catch{return!1}},hasCache:async()=>{try{for(const e of d){const t=e.litert;if(!t)continue;if(await re.stored(E(t.repo,t.file)))return!0}const e=await de();return!!e&&(await e.keys()).length>0}catch{return!1}},remove:async e=>{try{const t=e.litert;if(!t)return;const n=E(t.repo,t.file),a=await de();await re.remove(n),a&&await a.delete(n)}catch{}},purge:async()=>{try{await re.purge(),"undefined"!=typeof caches&&await caches.delete(oe)}catch{}}},we=ae("wllama-store");let ye,be,ve="",xe=!1;const ke=e=>`${e.wasm}|${e.compatWasm}|${e.compatWorker}`,Me=e=>{const t=w();return{wasm:e?.wasm||t.wasm,compatWasm:e?.compatWasm||t.compatWasm,compatWorker:e?.compatWorker||t.compatWorker}},_e=e=>{const t=e=>(...t)=>{xe&&("string"==typeof t[0]&&t[0].includes("ggml_webgpu: Failed to get an adapter")||e(...t))};return{...e,log:t(e.log),warn:t(e.warn),error:t(e.error)}},Te=async()=>{if(ye)return ye;const e=await n.e(924).then(()=>n(577)),t=be||Me(),a=new e.Wllama({default:t.wasm},{logger:_e(e.LoggerWithoutDebug)});return be||(be=t,ve=ke(t)),a.setCompat({wasm:t.compatWasm,worker:t.compatWorker}),ye=a,a},Ee=async()=>{const e=ye;ye=void 0;try{await(e?.exit())}catch{}},Ce=(e,t)=>e.name.includes(t)||!!e.metadata?.originalURL?.includes(t),Le=e=>{const t=e.cpu;return t?[{name:t.file,url:E(t.repo,t.file)},...t.mmproj?[{name:t.mmproj.file,url:E(t.repo,t.mmproj.file)}]:[]]:[]},Pe=async e=>{const t=Le(e);if(!t.length)return!1;for(const e of t){const t=await we.stored(e.url);if(!t?.complete)return!1}return!0},je=async(e,t)=>{const n=await e.cacheManager.list();return Le(t).every(e=>n.some(t=>Ce(t,e.name)))},Se=async({model:e,signal:t,onProgress:n})=>{const a=Le(e),o=[];for(let e=0;e<a.length;e++){if(t?.aborted)throw new Error("Cancelled");const r=await we.download({url:a[e].url,signal:t,onProgress:(t,o)=>n((e+t)/a.length,o)});if(!r)return;o.push(r.file)}return o.length?o:void 0},$e=async e=>{const t=[];for(const n of e){if("string"==typeof n.content){"system"==n.role?t.push({role:"system",content:n.content}):"assistant"==n.role?t.push({role:"assistant",content:n.content}):t.push({role:"user",content:n.content});continue}const e=[];for(const t of n.content)"text"==t.type?e.push({type:"text",text:t.text}):e.push({type:t.type,data:await v(t.url)});t.push({role:"user",content:e})}return t},Re={backend:s.cpu,startLLM:async({model:e,limits:t,onProgress:n,signal:a,logging:o,assets:r})=>{xe=!0===o;const i=e.cpu;if(!i)throw new Error("Model has no CPU artifact");const l=Me(r),d=ke(l);ye&&d!=ve&&await Ee(),be=l,ve=d;const u=(g="undefined"!=typeof navigator&&navigator.hardwareConcurrency||4,m=t.maxThreads,Math.max(1,Math.min(Math.max(2,Math.min(8,(g||4)-1)),m)));var g,m;const f=await Te(),p=await Pe(e),h=i.mmproj?.file,w=E(i.repo,i.file),y=h?E(i.repo,h):"";if(a?.aborted)throw await Ee(),new Error("Cancelled");if(!p&&h){const e=await f.cacheManager.list(),t=e.some(e=>Ce(e,i.file)),n=e.some(e=>Ce(e,h));t&&!n&&await f.cacheManager.deleteMany(e=>Ce(e,i.file))}const b={n_ctx:8192,n_threads:u,reasoning_budget_tokens:t.maxThinkingTokens,n_gpu_layers:0,jinja:!0};try{let t=p?await Se({model:e,signal:a,onProgress:n}):void 0;t||p||await je(f,e).catch(()=>!1)||(t=await Se({model:e,signal:a,onProgress:n}).catch(e=>{if(a?.aborted)throw new Error("Cancelled");if(!ne(e))throw e;o&&console.log("Local model store failed",e)})),t?(n(1,c.load),await f.loadModel(t,b)):await f.loadModelFromUrl(y?{url:w,mmprojUrl:y}:w,{...b,useCache:!0,signal:a,progressCallback:({loaded:e,total:t})=>{const a=t?e/t:0;n(a,a>=1?c.load:c.download)}})}catch(e){throw await Ee(),e}if(a?.aborted)throw await Ee(),new Error("Cancelled");if(!f.getLoadedContextInfo().n_ctx)throw await Ee(),new Error("Failed to create model context");return{model:e,backend:s.cpu,contextSize:8192,chat:t=>(async(e,t,n)=>{if(xe=!0===n.logging,e.isModelLoaded())return await O({model:t,params:n,stopped:()=>!!n.signal?.aborted,start:async a=>{if(P(a)&&!e.supportInputModality("image"))throw new Error("Model does not accept images");if(j(a)&&!e.supportInputModality("audio"))throw new Error("Model does not accept audio");return await e.createChatCompletion({messages:await $e(a),...T(n.limits,n.reasoning),abortSignal:n.signal,...t.reasoning?{chat_template_kwargs:{enable_thinking:n.reasoning}}:{}})}})})(f,e,t),unload:async()=>{if(ye==f)await Ee();else try{await f.exit()}catch{}}}},cached:async e=>{try{return!!e.cpu&&(!!await Pe(e)||await je(await Te(),e))}catch{return!1}},hasCache:async()=>{try{for(const e of d.flatMap(e=>Le(e)))if(await we.stored(e.url))return!0;const e=await Te();return(await e.cacheManager.list()).length>0}catch{return!1}},remove:async e=>{const t=Le(e);try{for(const e of t)await we.remove(e.url)}catch{}try{const e=await Te();await e.cacheManager.deleteMany(e=>t.some(t=>Ce(e,t.name)))}catch{}},purge:async()=>{try{await we.purge()}catch{}try{const e=await Te();await e.cacheManager.clear()}catch{}}},Ae=((e={})=>{const t=e.engines?.cpu||Re,n=e.engines?.litert||he;let a,o,r,l,u,g,m,f=s.none,p=i.idle,h=[],w=0,y={percent:0,phase:c.download};const b=a=>{if(e.backend!=s.none)return e.backend==s.cpu?a.cpu?t:void 0:e.backend==s.gpu?a.litert?n:void 0:a.litert&&"undefined"!=typeof navigator&&navigator.gpu&&"undefined"!=typeof WebAssembly?n:!a.cpu||"undefined"==typeof window&&"undefined"==typeof self&&"undefined"==typeof Worker||"undefined"==typeof WebAssembly?void 0:t},v=(e,t)=>{y={percent:Math.max(0,Math.min(1,e)),phase:t||c.download};for(const e of[...h])e.report(y.percent,y.phase)},x=e=>{p=e},k=()=>{w++,g?.abort(),g=void 0,u=void 0,p==i.downloading&&x(i.idle)},M=(e,t)=>{const n=l;return n?new Promise(a=>{let o=!1;const r={report:e,settle:e=>{o||(o=!0,t?.removeEventListener("abort",i),a(e))}},i=()=>{(e=>{h=h.filter(t=>t!=e),queueMicrotask(()=>{!h.length&&l&&k()})})(r),r.settle(!1)};h.push(r),t?.aborted?i():t?.addEventListener("abort",i,{once:!0}),n.then(r.settle,()=>r.settle(!1))}):Promise.resolve(!1)},_=()=>p==i.ready&&!!a&&!!o;return{load:async({model:e,limits:t,assets:n,logging:s,signal:m,onProgress:p})=>{if(m?.aborted)return!1;if(_()&&o?.id==e.id)return!0;if(l&&u==e.id)return p(y.percent,y.phase),await M(p,m);l&&(k(),h=[],await l.catch(()=>!1));const T=++w,E=(async()=>{await(async()=>{try{if("undefined"==typeof navigator)return;await(navigator.storage?.persist?.())}catch{}})();const l=b(e);if(T!=w)return!1;if(!l)return x(i.unsupported),!1;if(await(async(e,t)=>{if(o&&o.id!=e.id){const e=o,t=r,n=a;a=void 0,o=void 0,r=void 0,await(n?.unload()),await(t?.remove(e))}if(await t.hasCache())for(const n of d)n.id!=e.id&&b(n)==t&&await t.remove(n)})(e,l),T!=w)return!1;const u=await l.cached(e);if(T!=w)return!1;x(i.downloading),v(0,c.download),u&&v(1,c.load);const m=new AbortController;g=m;try{const d=await l.startLLM({model:e,limits:t,logging:s,signal:m.signal,assets:n,onProgress:(e,t)=>{T==w&&v(e,t)}});return T!=w||m.signal.aborted?(await d.unload(),!1):(a=d,o=e,r=l,f=l.backend,x(i.ready),v(1,c.load),!0)}catch(e){return T!=w||(a=void 0,o=void 0,r=void 0,s&&console.log("Local model failed",e),x(i.error)),!1}finally{g==m&&(g=void 0)}})().finally(()=>{l==E&&(l=void 0,u=void 0,h=[])});return l=E,u=e.id,await M(p,m)},chat:async e=>{if(!_()||!a)return;for(;m;)await m.catch(()=>{});if(!_()||!a)return;const t=a.chat(e);m=t;try{return await t}finally{m==t&&(m=void 0)}},unload:async()=>{const e=a;k(),a=void 0,o=void 0,r=void 0,x(i.idle),await(e?.unload())},ready:_,loading:()=>!_()&&p==i.downloading,status:()=>p,backend:()=>f,model:()=>o}})(),qe=(e,t,n)=>{const a=e.status(),o=u(t);return{availability:a==i.ready?r.online:a==i.error||a==i.unsupported?r.offline:r.away,status:a,backend:e.backend(),supportsImages:!!o&&f(o),supportsAudio:!!o&&p(o),ready:e.ready(),loading:e.loading(),requesting:n.requesting,phase:n.phase,percent:n.percent,...n.error?{error:n.error}:{}}},Be=(e=>{let t,n=0;const a=t=>t.input.onStatus?.(qe(e,t.model??t.input.model,t));return r=>{t?.controller.abort();const s={token:++n,input:r,controller:new AbortController,percent:e.ready()?100:0,phase:e.ready()?c.load:c.download,requesting:!1,cancelled:!1};return t=s,a(s),(async t=>{const{input:r}=t,i=!0===r.logging,s=await m(r.model);if(t.model=s?.id,t.token!=n)return;const l=r.messages.some(e=>e.role!=o.system&&"string"==typeof e.content&&!!e.content.trim()),d=r.messages.some(e=>e.role==o.user&&!!e.images?.some(e=>"string"==typeof e&&!!e)),u=r.messages.some(e=>e.role==o.user&&!!e.audio?.some(e=>"string"==typeof e&&!!e)),g=l||d||u;if(!s)return t.error="Unknown model",void a(t);if(!g)return void a(t);const h=f(s),w=p(s);if(!(l||d&&h||u&&w))return u&&!w?t.error="Model does not accept audio":d&&!h&&(t.error="Model does not accept images"),void a(t);const y=_(r.limits,!!s.litert),b=await e.load({model:s,limits:y,logging:i,signal:t.controller.signal,onProgress:(e,o)=>{t.token==n&&(t.percent=Math.round(100*e),t.phase=o,a(t))}}).catch(e=>(t.token==n&&(t.error=`${e?.message||`${e}`}`),!1));if(t.token!=n)return;if(t.cancelled)return;if(!b)return t.error||(t.error="Model not ready"),void a(t);const v=d&&!h||u&&!w?r.messages.map(({images:e,audio:t,...n})=>({...n,...e&&h?{images:e}:{},...t&&w?{audio:t}:{}})):r.messages;t.requesting=!0,t.error=void 0,t.percent=100,t.phase=c.load,a(t);let x,k="",M="";try{x=await e.chat({messages:v,limits:y,reasoning:!0===r.reasoning,logging:i,onReason:e=>{t.token!=n||t.cancelled||(k+=e,r.onThinking?.(k))},onText:e=>{t.token!=n||t.cancelled||(M+=e,r.onResponse?.(M))},onSpeed:e=>{t.token!=n||t.cancelled||r.onSpeed?.(e)},signal:t.controller.signal})}catch(e){if(t.token!=n)return;return t.requesting=!1,t.error=`${e?.message||`${e}`}`,void a(t)}t.token==n&&(t.requesting=!1,x||t.cancelled||(t.error="No reply"),a(t))})(s),()=>(t=>{t.token==n&&(t.cancelled=!0,t.controller.abort(),e.status()!=i.ready?queueMicrotask(()=>a(t)):e.unload().then(()=>a(t)))})(s)}})(Ae),We=(Ie=Ae,async(e={})=>{const t=await m(e.model),n=n=>e.onStatus?.(qe(Ie,t?.id,n));if(!t)return n({requesting:!1,phase:c.download,percent:0,error:"Unknown model"}),!1;let a,o=0,r=c.download;const i=Ie.ready()&&Ie.model()?.id==t.id;return await Ie.load({model:t,limits:_(e.limits,!!t.litert),logging:!0===e.logging,onProgress:(e,t)=>{o=Math.round(100*e),r=t,n({requesting:!1,phase:r,percent:o})}}).catch(e=>(a=`${e?.message||`${e}`}`,!1))?(i&&n({requesting:!1,phase:c.load,percent:100}),!0):(n({requesting:!1,phase:r,percent:o,error:a||"Model not ready"}),!1)});var Ie;module.exports=a})();
@@ -1,2 +1,2 @@
1
1
  /*! For license information please see chat.node.min.mjs.LICENSE.txt */
2
- import{dirname as e,join as t}from"node:path";import{fileURLToPath as n}from"node:url";var a={};const o={};function r(e){const t=o[e];if(void 0!==t)return t.exports;const n=o[e]={exports:{}};return a[e](n,n.exports,r),n.exports}var s,i,c,l,d,g;r.m=a,r.d=(e,t)=>{for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.f={},r.e=e=>{const t=[];return r.f.j(e,t),Promise.all(t)},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r.r=e=>{Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{const e={520:()=>import("./engine-litert.node.min.mjs"),924:()=>import("./engine-wllama.node.min.mjs")},t={792:0},n=e=>{let{__webpack_esm_ids__:n,__webpack_esm_modules__:a,__webpack_esm_runtime__:o}=e;var s,i,c=0;for(s in a)r.o(a,s)&&(r.m[s]=a[s]);for(o&&o(r);c<n.length;c++)i=n[c],r.o(t,i)&&t[i]&&t[i][0](),t[i]=0};r.f.j=(a,o)=>{let s=r.o(t,a)?t[a]:void 0;if(0!==s)if(s)o.push(s[1]);else{let r=e[a]().then(n,e=>{throw 0!==t[a]&&(t[a]=void 0),e});r=Promise.race([r,new Promise(e=>s=t[a]=[e])]),o.push(s[1]=r)}}})(),function(e){e.system="system",e.user="user",e.assistant="assistant"}(s||(s={})),function(e){e[e.online=0]="online",e[e.away=1]="away",e[e.offline=2]="offline"}(i||(i={})),function(e){e[e.idle=0]="idle",e[e.downloading=1]="downloading",e[e.ready=2]="ready",e[e.error=3]="error",e[e.unsupported=4]="unsupported"}(c||(c={})),function(e){e.cpu="cpu",e.gpu="gpu",e.none="none"}(l||(l={})),function(e){e.download="download",e.load="load"}(d||(d={})),function(e){e.qwen3_vl_2b="qwen3-vl-2b",e.gemma4_e2b="gemma4-e2b"}(g||(g={}));const u=[{id:g.qwen3_vl_2b,reasoning:!0,cpu:{repo:"Qwen/Qwen3-VL-2B-Thinking-GGUF",file:"Qwen3VL-2B-Thinking-Q4_K_M.gguf",mmproj:{file:"mmproj-Qwen3VL-2B-Thinking-Q8_0.gguf"}}},{id:g.gemma4_e2b,reasoning:!0,litert:{repo:"litert-community/gemma-4-E2B-it-litert-lm",file:"gemma-4-E2B-it.litertlm",vision:!0}}],m=e=>e?u.find(t=>t.id==e):void 0,f=async()=>(await(async()=>{const e="undefined"==typeof navigator?void 0:navigator.gpu;if(!e)return!1;try{return!!await e.requestAdapter({powerPreference:"high-performance"})}catch{return!1}})()?u.find(e=>!!e.litert):u.find(e=>!!e.cpu))?.id,p=async e=>m(e||await f()),h=e=>!!e.cpu?.mmproj||!!e.litert?.vision,w=()=>{return a=t(e(n("file:///media/x/store/code/DegreeSign/ds_chat/src/assets/nodeEsm.js")),"../assets/wllama"),{wasm:t(a,"wllama.wasm"),compatWasm:t(a,"compat/wllama.wasm"),compatWorker:t(a,"compat/wllama.js")};var a},y=()=>{return a=t(e(n("file:///media/x/store/code/DegreeSign/ds_chat/src/assets/nodeEsm.js")),"../assets/litert"),{script:t(a,"litertlm_wasm_internal.js"),wasm:t(a,"litertlm_wasm_internal.wasm")};var a},b=288,v=async e=>{if(!e.startsWith("data:"))return await(await fetch(e)).arrayBuffer();const t=e.indexOf(","),n=t>=0?e.slice(0,t):"";if(t<0)throw new Error("Invalid image data");if(!n.includes(";base64"))return await(await fetch(e)).arrayBuffer();const a=atob(e.slice(t+1)),o=new Uint8Array(a.length);for(let e=0;e<a.length;e++)o[e]=a.charCodeAt(e);return o.buffer},x=async e=>{if(!e||"function"!=typeof fetch||"function"!=typeof createImageBitmap)return e;try{const t=await fetch(e),n=await createImageBitmap(await t.blob()),a=Math.min(n.width,n.height),o=Math.floor((n.width-a)/2),r=Math.floor((n.height-a)/2),s=((e,t)=>{if("undefined"!=typeof OffscreenCanvas)return new OffscreenCanvas(e,t);if("undefined"==typeof document)return;const n=document.createElement("canvas");return n.width=e,n.height=t,n})(b,b),i=s?s.getContext("2d"):null;if(!s||!i||a<1)return n.close(),e;if(i.drawImage(n,o,r,a,a,0,0,b,b),n.close(),(e=>"undefined"!=typeof OffscreenCanvas&&e instanceof OffscreenCanvas)(s)){const t=await s.convertToBlob({type:"image/jpeg",quality:.85});return await(e=>new Promise(t=>{if("undefined"==typeof FileReader)return void t("");const n=new FileReader;n.onerror=()=>t(""),n.onload=()=>t(`${n.result}`),n.readAsDataURL(e)}))(t)||e}return s.toDataURL("image/jpeg",.85)}catch{return e}},k={maxReplyTokens:250,maxThinkingTokens:128,maxReasoningTokens:320,maxImages:2,maxContentLength:1e3,promptCharBudget:1e4,maxThreads:6},_=(e,t,n)=>"number"==typeof e&&Number.isFinite(e)&&e>=n?Math.floor(e):t,M=(e={},t=!1)=>{const n=k;return{maxReplyTokens:_(e.maxReplyTokens,n.maxReplyTokens,1),maxThinkingTokens:_(e.maxThinkingTokens,n.maxThinkingTokens,1),maxReasoningTokens:_(e.maxReasoningTokens,t?1200:n.maxReasoningTokens,1),maxImages:_(e.maxImages,n.maxImages,0),maxContentLength:_(e.maxContentLength,n.maxContentLength,1),promptCharBudget:_(e.promptCharBudget,n.promptCharBudget,1),maxThreads:_(e.maxThreads,n.maxThreads,1)}},T=(e,t)=>({stream:!0,temperature:.7,max_tokens:t?e.maxReasoningTokens:e.maxReplyTokens}),E=(e,t)=>`https://huggingface.co/${e}/resolve/main/${t}`,C=e=>Number(e.headers.get("content-length"))||0,L=e=>e.some(e=>Array.isArray(e.content)&&e.content.some(e=>"image"==e.type)),P=["<|channel>thought\n","<|channel>thought","..."],j=["<channel|>","..."],S=(e,t)=>t.length?[...e?[{type:"text",text:e}]:[],...t.map(e=>({type:"image",url:e}))]:e,$=e=>"string"==typeof e?e?[{type:"text",text:e}]:[]:e,R=(e,t)=>{if("string"==typeof e&&"string"==typeof t)return`${e}\n${t}`;const n=[];for(const a of[...$(e),{type:"text",text:"\n"},...$(t)]){const e=n[n.length-1];"text"==a.type&&"text"==e?.type?e.text+=a.text:n.push(a)}return n};class W extends Error{}const q=async(e,t,n)=>{let a;try{return await Promise.race([e,new Promise((e,o)=>{a=setTimeout(()=>o(new W(n)),t)})])}finally{a&&clearTimeout(a)}},z=e=>{try{Promise.resolve(e.return?.()).catch(()=>{})}catch{}},B=async({model:e,logging:t,onReason:n,onText:a,onSpeed:o,start:r,stopped:s,firstTimeoutMs:i=18e4,idleTimeoutMs:c=3e4,onStall:l})=>{let d="",g="",u="",m=!1,f=0,p=0;const h=((e,t)=>{let n="",a=!1;const o=(e,t)=>{let n=-1,a="";for(const o of t){const t=e.indexOf(o);t>=0&&(n<0||t<n)&&(n=t,a=o)}return{at:n,tag:a}},r=(e,t)=>{let n="";for(const a of t)for(let t=Math.min(e.length,a.length-1);t>n.length;t--)if(e.endsWith(a.slice(0,t))){n=e.slice(e.length-t);break}return n};return{feed:s=>{let i=n+s;for(n="";i;){if(a){const{at:t,tag:s}=o(i,j);if(t>=0){t&&e(i.slice(0,t)),i=i.slice(t+s.length),a=!1;continue}const c=r(i,j);e(i.slice(0,i.length-c.length)),n=c;break}{const{at:e,tag:s}=o(i,P);if(e>=0){e&&t(i.slice(0,e)),i=i.slice(e+s.length),i.startsWith("\n")&&(i=i.slice(1)),a=!0;continue}const c=r(i,P);t(i.slice(0,i.length-c.length)),n=c;break}}},flush:()=>{n&&(a?e(n):t(n),n="")}}})(e=>{g+=e,n(e)},e=>{d+=e,a(e)});try{const e=(await q(r(),i,"Model did not start"))[Symbol.asyncIterator]();for(;;){if(s()){z(e);break}let a;try{a=await q(e.next(),m?c:i,m?"Model stopped responding":"Model did not reply")}catch(n){if(n instanceof W&&(d||g)){l?.(),z(e),t&&console.log("Local chat stalled",n);break}throw n}if(a.done)break;m=!0;const r=a.value?.choices?.[0],w=r?.delta,y="string"==typeof w?.reasoning_content?w.reasoning_content:"string"==typeof w?.reasoning?w.reasoning:"";if(r?.finish_reason&&(u=r.finish_reason),y&&(g+=y,n(y)),w?.content&&h.feed(w.content),y||w?.content){const e=performance.now();f||(p=e),f+=1,f>1&&e>p&&o?.(1e3*f/(e-p))}}h.flush()}catch(e){if(t&&console.log("Local chat failed",e),e instanceof W&&!s())throw e}const w=s()?void 0:d||void 0;return t&&(console.log("Local chat reply",e.id,w?"****":""),console.log("Local chat finish",e.id,u||"none",d.length,g.length)),w},I=async({model:e,params:t,start:n,stopped:a,onStall:o})=>{const r=await(async e=>{const t=[];for(const n of e){if("string"==typeof n.content){t.push(n);continue}const e=[];for(const t of n.content)e.push("image"==t.type?{type:"image",url:await x(t.url)}:t);t.push({role:"user",content:e})}return t})(((e,t=k)=>{const n=t.maxContentLength,a=e.filter(e=>"string"==typeof e.content&&!!e.content.trim()||!(e.role!=s.user||!e.images?.length)),o=[],r=[];let i=0,c=t.maxImages;for(let e=a.length-1;e>=0;e--){const l=a[e],d=("string"==typeof l.content?l.content:"").slice(0,n),g=l.role==s.user?(l.images||[]).filter(e=>"string"==typeof e&&!!e).slice(0,Math.max(0,c)):[];if(d||g.length){if(d&&i+d.length>t.promptCharBudget)break;i+=d.length,c-=g.length,l.role==s.system?o.unshift(d):l.role==s.assistant?r.unshift({role:"assistant",content:d}):r.unshift({role:"user",content:S(d,g)})}}for(;r.length&&"user"!=r[0].role;)r.shift();const l=[];for(const e of r){const t=l[l.length-1];"assistant"==t?.role&&"assistant"==e.role?t.content=`${t.content}\n${e.content}`:"user"==t?.role&&"user"==e.role?t.content=R(t.content,e.content):"assistant"==e.role?l.push({role:"assistant",content:e.content}):l.push({role:"user",content:e.content})}const d=o.join("\n\n"),g=[];return d&&g.push({role:"system",content:d}),g.push(...l),g})(t.messages,t.limits));return t.logging&&((e,t)=>{console.log("Local chat call",e.id,t.map(({role:e,content:t})=>({role:e,content:"****",...Array.isArray(t)?{images:t.filter(e=>"image"==e.type).length}:{}})))})(e,r),await B({model:e,logging:t.logging,onReason:t.onReason,onText:t.onText,onSpeed:t.onSpeed,start:()=>n(r),stopped:a,onStall:o})},A=33554432,O=new Map;let N=!1;const F=e=>{const t=(e.split("/").pop()||"model").replace(/[^\w.-]+/g,"_");let n=0;for(let t=0;t<e.length;t++)n=31*n+e.charCodeAt(t)>>>0;return`${n.toString(16)}-${t}`},U=e=>{const t=/^part-(\d+)$/.exec(e);return t?Number(t[1]):-1},D=async()=>{if(!N)if("undefined"!=typeof navigator&&navigator.storage?.getDirectory)try{return await navigator.storage.getDirectory()}catch{return}else N=!0},H=async(e,t)=>{try{return await e.getDirectoryHandle(t)}catch{return}},Q=async e=>{if(e)try{const t=await e.getFile(),n=JSON.parse(await t.text());return{total:Number(n.total)||0,etag:`${n.etag||""}`,complete:!0===n.complete}}catch{return}},V=async(e,t)=>{try{const n=await e.getFileHandle("meta.json",{create:!0}),a=await n.createWritable();await a.write(JSON.stringify(t)),await a.close()}catch{}},G=async function*(e){for await(const t of e)yield t},J=async e=>{try{for await(const[t,n]of G(e))"file"==n.kind&&U(t)>=0&&await e.removeEntry(t).catch(()=>{})}catch{}},K=async(e,t)=>{try{const n=await(await e.getFileHandle(t)).getFile(),a=await Q(await e.getFileHandle(`${t}.json`).catch(()=>{}));if(!a)return;return{file:n,size:n.size,total:a.total,etag:a.etag,complete:a.total?n.size>=a.total:!0===a.complete}}catch{return}},X=async(e,t)=>{const n=await H(e,t);if(n){const e=await(async e=>await Q(await e.getFileHandle("meta.json").catch(()=>{})))(n),t=await(async e=>{const t=[];try{for await(const[n,a]of G(e)){const e=U(n);if("file"!=a.kind||e<0)continue;const o=await a.getFile();t.push({part:{start:e,size:o.size},file:o})}}catch{return}const n=(e=>{const t=[];let n=0;for(const a of[...e].sort((e,t)=>e.start-t.start))if(a.size){if(a.start!=n)break;t.push(a),n+=a.size}return t})(t.map(e=>e.part)),a=new Set(n.map(e=>e.start)),o=t.filter(e=>a.has(e.part.start)).sort((e,t)=>e.part.start-t.part.start).map(e=>e.file);if(o.length)return{file:1==o.length?o[0]:new Blob(o),size:n.reduce((e,t)=>e+t.size,0)}})(n),a=e?.total||0,o=t?.size??0,r=t?.file??new Blob;return{file:a&&o>a?r.slice(0,a):r,size:o,total:a,etag:e?.etag||"",complete:a?o>=a:!0===e?.complete}}return await K(e,t)},Y=(e,t)=>{const n=e.headers.get("content-range"),a=/bytes (\d+)-/.exec(n||""),o=/\/(\d+)\s*$/.exec(n||""),r=e.headers.get("etag")||"";if(206==e.status){const e=a?Number(a[1]):t.size,n=o?Number(o[1]):t.total;if(e!=t.size)return;if(t.total&&n&&t.total!=n)return;if(t.etag&&r&&t.etag!=r)return;return{start:e,total:n||t.total,etag:r||t.etag}}if(!t.etag||!r||t.etag==r)return{start:0,total:C(e)||t.total,etag:r||t.etag}},Z=async({dir:e,start:t,skip:n,total:a,body:o,onProgress:r,onChunk:s})=>{let i,c=t,l=0,g=n;const u=async()=>{const t=await e.getFileHandle((e=>`part-${e}`)(c),{create:!0});i=await t.createWritable(),l=0},m=async()=>{const e=i;i=void 0,e&&await e.close().catch(()=>{})};try{const e=o.getReader();for(;;){const{done:n,value:o}=await e.read();if(n)break;s();let f=0;for(g&&(f=Math.min(g,o.byteLength),g-=f);f<o.byteLength;){i||await u();const e=Math.min(A-l,o.byteLength-f);await i.write({type:"write",position:l,data:o.subarray(f,f+e)}),f+=e,l+=e,c+=e,l==A&&await m()}c>t&&r(a?c/a:0,d.download)}await m()}catch(e){throw await m(),e}return c},ee=e=>["NotAllowedError","NotSupportedError"].includes(e?.name||""),te=e=>{const t=async()=>{const t=await D();if(t)try{return await t.getDirectoryHandle(e,{create:!0})}catch{return}};return{stored:async e=>{const n=await t();return n?await X(n,F(e)):void 0},download:async({url:n,signal:a,onProgress:o})=>{const r=`${e}|${n}`,s=O.get(r);if(s)return await s;const i=await t();if(!i)return;const c=(async(e,t,n,a)=>{const o=F(t),r=await K(e,o);if(r?.complete)return r;let s=await H(e,o);s||(await e.removeEntry(o).catch(()=>{}),await e.removeEntry(`${o}.json`).catch(()=>{}),s=await e.getDirectoryHandle(o,{create:!0}));let i=0,c=0;for(;;){if(n?.aborted)throw new Error("Cancelled");const r=await X(e,o);if(r?.complete)return r;const l=r?.size??0,g=r?.total||0,u=r?.etag||"";if(l>i)i=l,c=0;else if(++c>4)throw new Error("Model download failed");l&&g&&a(l/g,d.download);const m=g?Math.min(l+A,g)-1:l+A-1,f=new AbortController,p=()=>f.abort(),h={size:l,total:g,etag:u};let w,y=setTimeout(p,3e4);n?.addEventListener("abort",p,{once:!0});try{if(w=await fetch(t,{signal:f.signal,headers:{Range:`bytes=${l}-${m}`}}),416==w.status){if(await(w.body?.cancel().catch(()=>{})),g&&l>=g){await V(s,{total:g,etag:u,complete:!0});continue}await J(s),await V(s,{total:0,etag:"",complete:!1});continue}if(!w.ok||!w.body)throw await(w.body?.cancel().catch(()=>{})),new Error("Model download failed");const n=Y(w,h);if(!n){await w.body.cancel().catch(()=>{}),await J(s),await V(s,{total:0,etag:"",complete:!1});continue}await V(s,{total:n.total,etag:n.etag,complete:!1});const r=0==n.start?l:0,i=await Z({dir:s,start:n.start+r,skip:r,total:n.total,body:w.body,onProgress:a,onChunk:()=>{clearTimeout(y),y=setTimeout(p,3e4)}});if(!n.total){await V(s,{total:i,etag:n.etag,complete:!0});const t=await X(e,o);if(t)return t}}catch(e){if(n?.aborted)throw new Error("Cancelled");if(e instanceof DOMException&&["QuotaExceededError","NotAllowedError","NotSupportedError","InvalidStateError","TypeMismatchError","SecurityError"].includes(e.name))throw e;await new Promise(e=>setTimeout(e,250+500*c))}finally{clearTimeout(y),n?.removeEventListener("abort",p)}}})(i,n,a,o).finally(()=>{O.delete(r)});return O.set(r,c),await c},remove:async e=>{const n=await t();n&&await(async(e,t)=>{await e.removeEntry(t,{recursive:!0}).catch(()=>{}),await e.removeEntry(`${t}.json`).catch(()=>{})})(n,F(e))},purge:async()=>{const t=await D();t&&await t.removeEntry(e,{recursive:!0}).catch(()=>{})}}},ne="litertlm-models",ae=te("litert-store"),oe={num_output_candidates:1,wait_for_weight_uploads:!0,num_decode_steps_per_sync:3,sequence_batch_size:0,supported_lora_ranks:[],max_top_k:40,enable_decode_logits:!1,enable_external_embeddings:!1,use_submodel:!1,use_autosized_ringbuffers:!0};let re=!1;const se=(...e)=>{re&&console.log(...e)},ie=(...e)=>{re&&console.error(...e)},ce=async()=>{try{if("undefined"==typeof caches)return;return await caches.open(ne)}catch{return}},le=new Map,de=(e,t,n)=>{let a=0;return e.pipeThrough(new TransformStream({transform:(e,o)=>{a+=e.byteLength,n(t?a/t:0,d.download),o.enqueue(e)}}))},ge=async(e,t=!0)=>{const n=[];for(const a of e){if("string"==typeof a.content){n.push({role:a.role,content:a.content});continue}const e=[];for(const n of a.content)e.push("text"==n.type?{type:"text",text:n.text}:t?{type:"image",data:await v(n.url)}:{type:"text",text:"[image]"});n.push({role:a.role,content:e})}return n},ue=(e,t)=>{if(e.role!=t.role)return!1;if("string"==typeof e.content||"string"==typeof t.content)return e.content==t.content;if(e.content.length!=t.content.length)return!1;for(let n=0;n<e.content.length;n++){const a=e.content[n],o=t.content[n];if(a.type!=o.type)return!1;if("text"==a.type&&"text"==o.type){if(a.text!=o.text)return!1}else if("image"==a.type&&"image"==o.type&&a.url!=o.url)return!1}return!0},me=async function*({engine:e,mod:t,params:n,messages:a,session:o}){const r=a[a.length-1],s=T(n.limits,n.reasoning);if("user"!=r?.role)throw new Error("No user turn");const i=a.slice(0,-1),c=(await ge([r]))[0];if(!c)throw new Error("Empty prompt");const l=!!o.conversation&&o.reasoning==n.reasoning&&o.maxTokens==s.max_tokens&&((e,t)=>{const n="system"==e[0]?.role?e[0]:void 0,a="system"==t[0]?.role?t[0]:void 0;if(!n!=!a)return!1;if(n&&a&&!ue(n,a))return!1;const o=n?e.slice(1):e,r=a?t.slice(1):t;if(!r.length||r.length>o.length)return!1;const s=o.length-r.length;for(let e=0;e<r.length;e++)if(!ue(o[s+e],r[e]))return!1;return!0})(o.sent,i);let d;if(l&&o.conversation)d=o.conversation;else{const a=await ge(i,!1);o.conversation=void 0,o.sent=[],d=await e.createConversation({sessionConfig:{maxOutputTokens:s.max_tokens,samplerParams:{type:t.SamplerType.TOP_K,temperature:s.temperature,k:40,p:.95}},preface:{...a.length?{messages:a}:{},extra_context:{enable_thinking:n.reasoning}},prefillPrefaceOnInit:a.length>0}),o.conversation=d,o.reasoning=n.reasoning,o.maxTokens=s.max_tokens,o.sent=[...i]}o.sent.push(r);let g,u=!1,m="";const f=()=>{g&&g.cancel().catch(()=>{});try{d.cancel()}catch{}};n.signal?.addEventListener("abort",f,{once:!0});try{if(n.signal?.aborted)return;const e=d.sendMessageStreaming(c).getReader();for(g=e;;){const{done:t,value:n}=await e.read();if(t)break;const a={};let o="string"==typeof n.content?n.content:"";if(Array.isArray(n.content))for(const e of n.content)"text"==e.type&&(o+=e.text);o&&(a.content=o,m+=o);const r=n.channels?.thought;r&&(a.reasoning=r),(a.content||a.reasoning)&&(yield{choices:[{delta:a}]})}u=!0}finally{n.signal?.removeEventListener("abort",f),u&&m?o.sent.push({role:"assistant",content:m}):(o.conversation=void 0,o.sent=[])}},fe={backend:l.gpu,startLLM:async({model:e,limits:t,onProgress:n,signal:a,logging:o})=>{re=!0===o;const s=e.litert;if(!s)throw new Error("Model has no LiteRT artifact");const i=await r.e(520).then(()=>r(507)),c=globalThis,g=c.Module;c.Module={print:se,printErr:ie};try{await i.getOrLoadGlobalLiteRtLm(y().script)}finally{c.Module=g}if(a?.aborted)throw new Error("Cancelled");const u=E(s.repo,s.file),m=await ce();let f;const p=m?await m.match(u):void 0;if(p?.body)f=p.body;else{const e=await ae.stored(u);e?.complete&&(f=e.file.stream())}if(!f){const e=await ae.download({url:u,signal:a,onProgress:n}).catch(e=>{if(a?.aborted)throw new Error("Cancelled");if(!ee(e))throw e;o&&console.log("Local model store failed",e)});e&&(f=e.file.stream())}if(!f&&m)try{await(async(e,t,n,a)=>{const o=le.get(e);if(o)return await o;const r=(async()=>{const o=await fetch(e,{signal:n});if(!o.ok||!o.body)throw new Error("Model download failed");const r=C(o),s=de(o.body,r,a);await t.put(e,new Response(s,{headers:{"content-type":"application/octet-stream",...r?{"content-length":`${r}`}:{}}}))})().finally(()=>{le.delete(e)});return le.set(e,r),await r})(u,m,a,n);const e=await m.match(u);e?.body&&(f=e.body)}catch(e){if(a?.aborted)throw new Error("Cancelled");o&&console.log("Local model cache failed",e)}if(f||(f=await(async(e,t,n)=>{const a=await fetch(e,{signal:t});if(!a.ok||!a.body)throw new Error("Model download failed");return de(a.body,C(a),n)})(u,a,n)),a?.aborted)throw new Error("Cancelled");n(1,d.load);const h=await i.Engine.create((({model:e,limits:t,source:n,mod:a})=>({model:n,mainExecutorSettings:{maxNumTokens:4096,...e.litert?.vision?{maxNumImages:Math.max(1,t.maxImages)}:{},backendConfig:oe},...e.litert?.vision?{visionBackend:a.Backend.GPU,maxVisionTokensPerImage:70,minVisionTokensPerImage:70}:{}}))({model:e,limits:t,source:f,mod:i}));if(a?.aborted)throw await h.delete(),new Error("Cancelled");const w={sent:[],reasoning:!1,maxTokens:0};return{model:e,backend:l.gpu,contextSize:4096,chat:t=>(async({engine:e,model:t,mod:n,params:a,session:o})=>{re=!0===a.logging;const r=()=>{try{o.conversation?.cancel()}catch{}o.conversation=void 0,o.sent=[]};try{return await I({model:t,params:a,stopped:()=>!!a.signal?.aborted,onStall:r,start:async r=>{if(a.signal?.aborted)throw new Error("Cancelled");if(L(r)&&!t.litert?.vision)throw new Error("Model does not accept images");return me({engine:e,mod:n,params:a,messages:r,session:o})}})}catch(e){throw r(),e}})({engine:h,model:e,mod:i,params:t,session:w}),unload:async()=>{w.conversation=void 0,w.sent=[],await h.delete()}}},cached:async e=>{const t=e.litert;if(!t)return!1;const n=E(t.repo,t.file),[a,o]=await Promise.all([ce(),ae.stored(n)]);if(o?.complete)return!0;try{return!(!a||!await a.match(n))}catch{return!1}},hasCache:async()=>{try{for(const e of u){const t=e.litert;if(!t)continue;if(await ae.stored(E(t.repo,t.file)))return!0}const e=await ce();return!!e&&(await e.keys()).length>0}catch{return!1}},remove:async e=>{try{const t=e.litert;if(!t)return;const n=E(t.repo,t.file),a=await ce();await ae.remove(n),a&&await a.delete(n)}catch{}},purge:async()=>{try{await ae.purge(),"undefined"!=typeof caches&&await caches.delete(ne)}catch{}}},pe=te("wllama-store");let he,we,ye="",be=!1;const ve=e=>`${e.wasm}|${e.compatWasm}|${e.compatWorker}`,xe=e=>{const t=w();return{wasm:e?.wasm||t.wasm,compatWasm:e?.compatWasm||t.compatWasm,compatWorker:e?.compatWorker||t.compatWorker}},ke=e=>{const t=e=>(...t)=>{be&&("string"==typeof t[0]&&t[0].includes("ggml_webgpu: Failed to get an adapter")||e(...t))};return{...e,log:t(e.log),warn:t(e.warn),error:t(e.error)}},_e=async()=>{if(he)return he;const e=await r.e(924).then(()=>r(577)),t=we||xe(),n=new e.Wllama({default:t.wasm},{logger:ke(e.LoggerWithoutDebug)});return we||(we=t,ye=ve(t)),n.setCompat({wasm:t.compatWasm,worker:t.compatWorker}),he=n,n},Me=async()=>{const e=he;he=void 0;try{await(e?.exit())}catch{}},Te=(e,t)=>e.name.includes(t)||!!e.metadata?.originalURL?.includes(t),Ee=e=>{const t=e.cpu;return t?[{name:t.file,url:E(t.repo,t.file)},...t.mmproj?[{name:t.mmproj.file,url:E(t.repo,t.mmproj.file)}]:[]]:[]},Ce=async e=>{const t=Ee(e);if(!t.length)return!1;for(const e of t){const t=await pe.stored(e.url);if(!t?.complete)return!1}return!0},Le=async(e,t)=>{const n=await e.cacheManager.list();return Ee(t).every(e=>n.some(t=>Te(t,e.name)))},Pe=async({model:e,signal:t,onProgress:n})=>{const a=Ee(e),o=[];for(let e=0;e<a.length;e++){if(t?.aborted)throw new Error("Cancelled");const r=await pe.download({url:a[e].url,signal:t,onProgress:(t,o)=>n((e+t)/a.length,o)});if(!r)return;o.push(r.file)}return o.length?o:void 0},je=async e=>{const t=[];for(const n of e){if("string"==typeof n.content){"system"==n.role?t.push({role:"system",content:n.content}):"assistant"==n.role?t.push({role:"assistant",content:n.content}):t.push({role:"user",content:n.content});continue}const e=[];for(const t of n.content)e.push("text"==t.type?{type:"text",text:t.text}:{type:"image",data:await v(t.url)});t.push({role:"user",content:e})}return t},Se={backend:l.cpu,startLLM:async({model:e,limits:t,onProgress:n,signal:a,logging:o,assets:r})=>{be=!0===o;const s=e.cpu;if(!s)throw new Error("Model has no CPU artifact");const i=xe(r),c=ve(i);he&&c!=ye&&await Me(),we=i,ye=c;const g=(u="undefined"!=typeof navigator&&navigator.hardwareConcurrency||4,m=t.maxThreads,Math.max(1,Math.min(Math.max(2,Math.min(8,(u||4)-1)),m)));var u,m;const f=await _e(),p=await Ce(e),h=s.mmproj?.file,w=E(s.repo,s.file),y=h?E(s.repo,h):"";if(a?.aborted)throw await Me(),new Error("Cancelled");if(!p&&h){const e=await f.cacheManager.list(),t=e.some(e=>Te(e,s.file)),n=e.some(e=>Te(e,h));t&&!n&&await f.cacheManager.deleteMany(e=>Te(e,s.file))}const b={n_ctx:8192,n_threads:g,reasoning_budget_tokens:t.maxThinkingTokens,n_gpu_layers:0,jinja:!0};try{let t=p?await Pe({model:e,signal:a,onProgress:n}):void 0;t||p||await Le(f,e).catch(()=>!1)||(t=await Pe({model:e,signal:a,onProgress:n}).catch(e=>{if(a?.aborted)throw new Error("Cancelled");if(!ee(e))throw e;o&&console.log("Local model store failed",e)})),t?(n(1,d.load),await f.loadModel(t,b)):await f.loadModelFromUrl(y?{url:w,mmprojUrl:y}:w,{...b,useCache:!0,signal:a,progressCallback:({loaded:e,total:t})=>{const a=t?e/t:0;n(a,a>=1?d.load:d.download)}})}catch(e){throw await Me(),e}if(a?.aborted)throw await Me(),new Error("Cancelled");if(!f.getLoadedContextInfo().n_ctx)throw await Me(),new Error("Failed to create model context");return{model:e,backend:l.cpu,contextSize:8192,chat:t=>(async(e,t,n)=>{if(be=!0===n.logging,e.isModelLoaded())return await I({model:t,params:n,stopped:()=>!!n.signal?.aborted,start:async a=>{if(L(a)&&!e.supportInputModality("image"))throw new Error("Model does not accept images");return await e.createChatCompletion({messages:await je(a),...T(n.limits,n.reasoning),abortSignal:n.signal,...t.reasoning?{chat_template_kwargs:{enable_thinking:n.reasoning}}:{}})}})})(f,e,t),unload:async()=>{if(he==f)await Me();else try{await f.exit()}catch{}}}},cached:async e=>{try{return!!e.cpu&&(!!await Ce(e)||await Le(await _e(),e))}catch{return!1}},hasCache:async()=>{try{for(const e of u.flatMap(e=>Ee(e)))if(await pe.stored(e.url))return!0;const e=await _e();return(await e.cacheManager.list()).length>0}catch{return!1}},remove:async e=>{const t=Ee(e);try{for(const e of t)await pe.remove(e.url)}catch{}try{const e=await _e();await e.cacheManager.deleteMany(e=>t.some(t=>Te(e,t.name)))}catch{}},purge:async()=>{try{await pe.purge()}catch{}try{const e=await _e();await e.cacheManager.clear()}catch{}}},$e=((e={})=>{const t=e.engines?.cpu||Se,n=e.engines?.litert||fe;let a,o,r,s,i,g,m,f=l.none,p=c.idle,h=[],w=0,y={percent:0,phase:d.download};const b=a=>{if(e.backend!=l.none)return e.backend==l.cpu?a.cpu?t:void 0:e.backend==l.gpu?a.litert?n:void 0:a.litert&&"undefined"!=typeof navigator&&navigator.gpu&&"undefined"!=typeof WebAssembly?n:!a.cpu||"undefined"==typeof window&&"undefined"==typeof self&&"undefined"==typeof Worker||"undefined"==typeof WebAssembly?void 0:t},v=(e,t)=>{y={percent:Math.max(0,Math.min(1,e)),phase:t||d.download};for(const e of[...h])e.report(y.percent,y.phase)},x=e=>{p=e},k=()=>{w++,g?.abort(),g=void 0,i=void 0,p==c.downloading&&x(c.idle)},_=(e,t)=>{const n=s;return n?new Promise(a=>{let o=!1;const r={report:e,settle:e=>{o||(o=!0,t?.removeEventListener("abort",i),a(e))}},i=()=>{(e=>{h=h.filter(t=>t!=e),queueMicrotask(()=>{!h.length&&s&&k()})})(r),r.settle(!1)};h.push(r),t?.aborted?i():t?.addEventListener("abort",i,{once:!0}),n.then(r.settle,()=>r.settle(!1))}):Promise.resolve(!1)},M=()=>p==c.ready&&!!a&&!!o;return{load:async({model:e,limits:t,assets:n,logging:l,signal:m,onProgress:p})=>{if(m?.aborted)return!1;if(M()&&o?.id==e.id)return!0;if(s&&i==e.id)return p(y.percent,y.phase),await _(p,m);s&&(k(),h=[],await s.catch(()=>!1));const T=++w,E=(async()=>{await(async()=>{try{if("undefined"==typeof navigator)return;await(navigator.storage?.persist?.())}catch{}})();const s=b(e);if(T!=w)return!1;if(!s)return x(c.unsupported),!1;if(await(async(e,t)=>{if(o&&o.id!=e.id){const e=o,t=r,n=a;a=void 0,o=void 0,r=void 0,await(n?.unload()),await(t?.remove(e))}if(await t.hasCache())for(const n of u)n.id!=e.id&&b(n)==t&&await t.remove(n)})(e,s),T!=w)return!1;const i=await s.cached(e);if(T!=w)return!1;x(c.downloading),v(0,d.download),i&&v(1,d.load);const m=new AbortController;g=m;try{const i=await s.startLLM({model:e,limits:t,logging:l,signal:m.signal,assets:n,onProgress:(e,t)=>{T==w&&v(e,t)}});return T!=w||m.signal.aborted?(await i.unload(),!1):(a=i,o=e,r=s,f=s.backend,x(c.ready),v(1,d.load),!0)}catch(e){return T!=w||(a=void 0,o=void 0,r=void 0,l&&console.log("Local model failed",e),x(c.error)),!1}finally{g==m&&(g=void 0)}})().finally(()=>{s==E&&(s=void 0,i=void 0,h=[])});return s=E,i=e.id,await _(p,m)},chat:async e=>{if(!M()||!a)return;for(;m;)await m.catch(()=>{});if(!M()||!a)return;const t=a.chat(e);m=t;try{return await t}finally{m==t&&(m=void 0)}},unload:async()=>{const e=a;k(),a=void 0,o=void 0,r=void 0,x(c.idle),await(e?.unload())},ready:M,loading:()=>!M()&&p==c.downloading,status:()=>p,backend:()=>f,model:()=>o}})(),Re=(e,t,n)=>{const a=e.status(),o=m(t);return{availability:a==c.ready?i.online:a==c.error||a==c.unsupported?i.offline:i.away,status:a,backend:e.backend(),supportsImages:!!o&&h(o),ready:e.ready(),loading:e.loading(),requesting:n.requesting,phase:n.phase,percent:n.percent,...n.error?{error:n.error}:{}}},We=(e=>{let t,n=0;const a=t=>t.input.onStatus?.(Re(e,t.model??t.input.model,t));return o=>{t?.controller.abort();const r={token:++n,input:o,controller:new AbortController,percent:e.ready()?100:0,phase:e.ready()?d.load:d.download,requesting:!1,cancelled:!1};return t=r,a(r),(async t=>{const{input:o}=t,r=!0===o.logging,i=await p(o.model);if(t.model=i?.id,t.token!=n)return;const c=o.messages.some(e=>e.role!=s.system&&"string"==typeof e.content&&!!e.content.trim()),l=o.messages.some(e=>e.role==s.user&&!!e.images?.some(e=>"string"==typeof e&&!!e)),g=c||l;if(!i)return t.error="Unknown model",void a(t);if(!g)return void a(t);if(l&&!c&&!h(i))return t.error="Model does not accept images",void a(t);const u=M(o.limits,!!i.litert),m=await e.load({model:i,limits:u,logging:r,signal:t.controller.signal,onProgress:(e,o)=>{t.token==n&&(t.percent=Math.round(100*e),t.phase=o,a(t))}}).catch(e=>(t.token==n&&(t.error=`${e?.message||`${e}`}`),!1));if(t.token!=n)return;if(t.cancelled)return;if(!m)return t.error||(t.error="Model not ready"),void a(t);const f=h(i),w=l&&!f?o.messages.map(({images:e,...t})=>t):o.messages;if(l&&!f&&!c)return t.error="Model does not accept images",void a(t);t.requesting=!0,t.error=void 0,t.percent=100,t.phase=d.load,a(t);let y,b="",v="";try{y=await e.chat({messages:w,limits:u,reasoning:!0===o.reasoning,logging:r,onReason:e=>{t.token!=n||t.cancelled||(b+=e,o.onThinking?.(b))},onText:e=>{t.token!=n||t.cancelled||(v+=e,o.onResponse?.(v))},onSpeed:e=>{t.token!=n||t.cancelled||o.onSpeed?.(e)},signal:t.controller.signal})}catch(e){if(t.token!=n)return;return t.requesting=!1,t.error=`${e?.message||`${e}`}`,void a(t)}t.token==n&&(t.requesting=!1,y||t.cancelled||(t.error="No reply"),a(t))})(r),()=>(t=>{t.token==n&&(t.cancelled=!0,t.controller.abort(),e.status()!=c.ready?queueMicrotask(()=>a(t)):e.unload().then(()=>a(t)))})(r)}})($e),qe=(ze=$e,async(e={})=>{const t=await p(e.model),n=n=>e.onStatus?.(Re(ze,t?.id,n));if(!t)return n({requesting:!1,phase:d.download,percent:0,error:"Unknown model"}),!1;let a,o=0,r=d.download;const s=ze.ready()&&ze.model()?.id==t.id;return await ze.load({model:t,limits:M(e.limits,!!t.litert),logging:!0===e.logging,onProgress:(e,t)=>{o=Math.round(100*e),r=t,n({requesting:!1,phase:r,percent:o})}}).catch(e=>(a=`${e?.message||`${e}`}`,!1))?(s&&n({requesting:!1,phase:d.load,percent:100}),!0):(n({requesting:!1,phase:r,percent:o,error:a||"Model not ready"}),!1)});var ze;export{g as ChatModelId,i as ChatStatus,l as LlmBackend,d as LlmPhase,s as LlmRole,c as LlmStatus,We as createChat,qe as preloadModel};
2
+ import{dirname as e,join as t}from"node:path";import{fileURLToPath as n}from"node:url";var a={};const o={};function r(e){const t=o[e];if(void 0!==t)return t.exports;const n=o[e]={exports:{}};return a[e](n,n.exports,r),n.exports}var i,s,c,l,d,u;r.m=a,r.d=(e,t)=>{for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.f={},r.e=e=>{const t=[];return r.f.j(e,t),Promise.all(t)},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r.r=e=>{Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{const e={520:()=>import("./engine-litert.node.min.mjs"),924:()=>import("./engine-wllama.node.min.mjs")},t={792:0},n=e=>{let{__webpack_esm_ids__:n,__webpack_esm_modules__:a,__webpack_esm_runtime__:o}=e;var i,s,c=0;for(i in a)r.o(a,i)&&(r.m[i]=a[i]);for(o&&o(r);c<n.length;c++)s=n[c],r.o(t,s)&&t[s]&&t[s][0](),t[s]=0};r.f.j=(a,o)=>{let i=r.o(t,a)?t[a]:void 0;if(0!==i)if(i)o.push(i[1]);else{let r=e[a]().then(n,e=>{throw 0!==t[a]&&(t[a]=void 0),e});r=Promise.race([r,new Promise(e=>i=t[a]=[e])]),o.push(i[1]=r)}}})(),function(e){e.system="system",e.user="user",e.assistant="assistant"}(i||(i={})),function(e){e[e.online=0]="online",e[e.away=1]="away",e[e.offline=2]="offline"}(s||(s={})),function(e){e[e.idle=0]="idle",e[e.downloading=1]="downloading",e[e.ready=2]="ready",e[e.error=3]="error",e[e.unsupported=4]="unsupported"}(c||(c={})),function(e){e.cpu="cpu",e.gpu="gpu",e.none="none"}(l||(l={})),function(e){e.download="download",e.load="load"}(d||(d={})),function(e){e.qwen3_vl_2b="qwen3-vl-2b",e.gemma4_e2b="gemma4-e2b"}(u||(u={}));const g=[{id:u.qwen3_vl_2b,reasoning:!0,cpu:{repo:"Qwen/Qwen3-VL-2B-Thinking-GGUF",file:"Qwen3VL-2B-Thinking-Q4_K_M.gguf",mmproj:{file:"mmproj-Qwen3VL-2B-Thinking-Q8_0.gguf"}}},{id:u.gemma4_e2b,reasoning:!0,litert:{repo:"litert-community/gemma-4-E2B-it-litert-lm",file:"gemma-4-E2B-it.litertlm",vision:!0,audio:!0}}],m=e=>e?g.find(t=>t.id==e):void 0,f=async()=>(await(async()=>{const e="undefined"==typeof navigator?void 0:navigator.gpu;if(!e)return!1;try{return!!await e.requestAdapter({powerPreference:"high-performance"})}catch{return!1}})()?g.find(e=>!!e.litert):g.find(e=>!!e.cpu))?.id,p=async e=>m(e||await f()),h=e=>!!e.cpu?.mmproj||!!e.litert?.vision,w=e=>!!e.cpu?.mmproj?.audio||!!e.litert?.audio,y=()=>{return a=t(e(n("file:///media/x/store/code/DegreeSign/ds_chat/src/assets/nodeEsm.js")),"../assets/wllama"),{wasm:t(a,"wllama.wasm"),compatWasm:t(a,"compat/wllama.wasm"),compatWorker:t(a,"compat/wllama.js")};var a},b=()=>{return a=t(e(n("file:///media/x/store/code/DegreeSign/ds_chat/src/assets/nodeEsm.js")),"../assets/litert"),{script:t(a,"litertlm_wasm_internal.js"),wasm:t(a,"litertlm_wasm_internal.wasm")};var a},v=288,x=async e=>{if(!e.startsWith("data:"))return await(await fetch(e)).arrayBuffer();const t=e.indexOf(","),n=t>=0?e.slice(0,t):"";if(t<0)throw new Error("Invalid image data");if(!n.includes(";base64"))return await(await fetch(e)).arrayBuffer();const a=atob(e.slice(t+1)),o=new Uint8Array(a.length);for(let e=0;e<a.length;e++)o[e]=a.charCodeAt(e);return o.buffer},k=async e=>{if(!e||"function"!=typeof fetch||"function"!=typeof createImageBitmap)return e;try{const t=await fetch(e),n=await createImageBitmap(await t.blob()),a=Math.min(n.width,n.height),o=Math.floor((n.width-a)/2),r=Math.floor((n.height-a)/2),i=((e,t)=>{if("undefined"!=typeof OffscreenCanvas)return new OffscreenCanvas(e,t);if("undefined"==typeof document)return;const n=document.createElement("canvas");return n.width=e,n.height=t,n})(v,v),s=i?i.getContext("2d"):null;if(!i||!s||a<1)return n.close(),e;if(s.drawImage(n,o,r,a,a,0,0,v,v),n.close(),(e=>"undefined"!=typeof OffscreenCanvas&&e instanceof OffscreenCanvas)(i)){const t=await i.convertToBlob({type:"image/jpeg",quality:.85});return await(e=>new Promise(t=>{if("undefined"==typeof FileReader)return void t("");const n=new FileReader;n.onerror=()=>t(""),n.onload=()=>t(`${n.result}`),n.readAsDataURL(e)}))(t)||e}return i.toDataURL("image/jpeg",.85)}catch{return e}},_={maxReplyTokens:250,maxThinkingTokens:128,maxReasoningTokens:320,maxImages:2,maxAudio:1,maxContentLength:1e3,promptCharBudget:1e4,maxThreads:6},M=(e,t,n)=>"number"==typeof e&&Number.isFinite(e)&&e>=n?Math.floor(e):t,T=(e={},t=!1)=>{const n=_;return{maxReplyTokens:M(e.maxReplyTokens,n.maxReplyTokens,1),maxThinkingTokens:M(e.maxThinkingTokens,n.maxThinkingTokens,1),maxReasoningTokens:M(e.maxReasoningTokens,t?1200:n.maxReasoningTokens,1),maxImages:M(e.maxImages,n.maxImages,0),maxAudio:M(e.maxAudio,n.maxAudio,0),maxContentLength:M(e.maxContentLength,n.maxContentLength,1),promptCharBudget:M(e.promptCharBudget,n.promptCharBudget,1),maxThreads:M(e.maxThreads,n.maxThreads,1)}},E=(e,t)=>({stream:!0,temperature:.7,max_tokens:t?e.maxReasoningTokens:e.maxReplyTokens}),C=(e,t)=>`https://huggingface.co/${e}/resolve/main/${t}`,L=e=>Number(e.headers.get("content-length"))||0,P=(e,t)=>e.some(e=>Array.isArray(e.content)&&e.content.some(e=>e.type==t)),j=e=>P(e,"image"),S=e=>P(e,"audio"),$=["<|channel>thought\n","<|channel>thought","..."],R=["<channel|>","..."],A=(e,t,n)=>t.length||n.length?[...e?[{type:"text",text:e}]:[],...t.map(e=>({type:"image",url:e})),...n.map(e=>({type:"audio",url:e}))]:e,W=e=>"string"==typeof e?e?[{type:"text",text:e}]:[]:e,B=(e,t)=>{if("string"==typeof e&&"string"==typeof t)return`${e}\n${t}`;const n=[];for(const a of[...W(e),{type:"text",text:"\n"},...W(t)]){const e=n[n.length-1];"text"==a.type&&"text"==e?.type?e.text+=a.text:n.push(a)}return n};class I extends Error{}const q=async(e,t,n)=>{let a;try{return await Promise.race([e,new Promise((e,o)=>{a=setTimeout(()=>o(new I(n)),t)})])}finally{a&&clearTimeout(a)}},z=e=>{try{Promise.resolve(e.return?.()).catch(()=>{})}catch{}},O=async({model:e,logging:t,onReason:n,onText:a,onSpeed:o,start:r,stopped:i,firstTimeoutMs:s=18e4,idleTimeoutMs:c=3e4,onStall:l})=>{let d="",u="",g="",m=!1,f=0,p=0;const h=((e,t)=>{let n="",a=!1;const o=(e,t)=>{let n=-1,a="";for(const o of t){const t=e.indexOf(o);t>=0&&(n<0||t<n)&&(n=t,a=o)}return{at:n,tag:a}},r=(e,t)=>{let n="";for(const a of t)for(let t=Math.min(e.length,a.length-1);t>n.length;t--)if(e.endsWith(a.slice(0,t))){n=e.slice(e.length-t);break}return n};return{feed:i=>{let s=n+i;for(n="";s;){if(a){const{at:t,tag:i}=o(s,R);if(t>=0){t&&e(s.slice(0,t)),s=s.slice(t+i.length),a=!1;continue}const c=r(s,R);e(s.slice(0,s.length-c.length)),n=c;break}{const{at:e,tag:i}=o(s,$);if(e>=0){e&&t(s.slice(0,e)),s=s.slice(e+i.length),s.startsWith("\n")&&(s=s.slice(1)),a=!0;continue}const c=r(s,$);t(s.slice(0,s.length-c.length)),n=c;break}}},flush:()=>{n&&(a?e(n):t(n),n="")}}})(e=>{u+=e,n(e)},e=>{d+=e,a(e)});try{const e=(await q(r(),s,"Model did not start"))[Symbol.asyncIterator]();for(;;){if(i()){z(e);break}let a;try{a=await q(e.next(),m?c:s,m?"Model stopped responding":"Model did not reply")}catch(n){if(n instanceof I&&(d||u)){l?.(),z(e),t&&console.log("Local chat stalled",n);break}throw n}if(a.done)break;m=!0;const r=a.value?.choices?.[0],w=r?.delta,y="string"==typeof w?.reasoning_content?w.reasoning_content:"string"==typeof w?.reasoning?w.reasoning:"";if(r?.finish_reason&&(g=r.finish_reason),y&&(u+=y,n(y)),w?.content&&h.feed(w.content),y||w?.content){const e=performance.now();f||(p=e),f+=1,f>1&&e>p&&o?.(1e3*f/(e-p))}}h.flush()}catch(e){if(t&&console.log("Local chat failed",e),e instanceof I&&!i())throw e}const w=i()?void 0:d||void 0;return t&&(console.log("Local chat reply",e.id,w?"****":""),console.log("Local chat finish",e.id,g||"none",d.length,u.length)),w},N=async({model:e,params:t,start:n,stopped:a,onStall:o})=>{const r=await(async e=>{const t=[];for(const n of e){if("string"==typeof n.content){t.push(n);continue}const e=[];for(const t of n.content)e.push("image"==t.type?{type:"image",url:await k(t.url)}:t);t.push({role:"user",content:e})}return t})(((e,t=_)=>{const n=t.maxContentLength,a=e.filter(e=>"string"==typeof e.content&&!!e.content.trim()||!(e.role!=i.user||!e.images?.length&&!e.audio?.length)),o=[],r=[];let s=0,c=t.maxImages,l=t.maxAudio;for(let e=a.length-1;e>=0;e--){const d=a[e],u=("string"==typeof d.content?d.content:"").slice(0,n),g=d.role==i.user?(d.images||[]).filter(e=>"string"==typeof e&&!!e).slice(0,Math.max(0,c)):[],m=d.role==i.user?(d.audio||[]).filter(e=>"string"==typeof e&&!!e).slice(0,Math.max(0,l)):[];if(u||g.length||m.length){if(u&&s+u.length>t.promptCharBudget)break;s+=u.length,c-=g.length,l-=m.length,d.role==i.system?o.unshift(u):d.role==i.assistant?r.unshift({role:"assistant",content:u}):r.unshift({role:"user",content:A(u,g,m)})}}for(;r.length&&"user"!=r[0].role;)r.shift();const d=[];for(const e of r){const t=d[d.length-1];"assistant"==t?.role&&"assistant"==e.role?t.content=`${t.content}\n${e.content}`:"user"==t?.role&&"user"==e.role?t.content=B(t.content,e.content):"assistant"==e.role?d.push({role:"assistant",content:e.content}):d.push({role:"user",content:e.content})}const u=o.join("\n\n"),g=[];return u&&g.push({role:"system",content:u}),g.push(...d),g})(t.messages,t.limits));return t.logging&&((e,t)=>{console.log("Local chat call",e.id,t.map(({role:e,content:t})=>({role:e,content:"****",...Array.isArray(t)?{images:t.filter(e=>"image"==e.type).length,audio:t.filter(e=>"audio"==e.type).length}:{}})))})(e,r),await O({model:e,logging:t.logging,onReason:t.onReason,onText:t.onText,onSpeed:t.onSpeed,start:()=>n(r),stopped:a,onStall:o})},F=33554432,U=new Map;let D=!1;const H=e=>{const t=(e.split("/").pop()||"model").replace(/[^\w.-]+/g,"_");let n=0;for(let t=0;t<e.length;t++)n=31*n+e.charCodeAt(t)>>>0;return`${n.toString(16)}-${t}`},Q=e=>{const t=/^part-(\d+)$/.exec(e);return t?Number(t[1]):-1},G=async()=>{if(!D)if("undefined"!=typeof navigator&&navigator.storage?.getDirectory)try{return await navigator.storage.getDirectory()}catch{return}else D=!0},V=async(e,t)=>{try{return await e.getDirectoryHandle(t)}catch{return}},J=async e=>{if(e)try{const t=await e.getFile(),n=JSON.parse(await t.text());return{total:Number(n.total)||0,etag:`${n.etag||""}`,complete:!0===n.complete}}catch{return}},K=async(e,t)=>{try{const n=await e.getFileHandle("meta.json",{create:!0}),a=await n.createWritable();await a.write(JSON.stringify(t)),await a.close()}catch{}},X=async function*(e){for await(const t of e)yield t},Y=async e=>{try{for await(const[t,n]of X(e))"file"==n.kind&&Q(t)>=0&&await e.removeEntry(t).catch(()=>{})}catch{}},Z=async(e,t)=>{try{const n=await(await e.getFileHandle(t)).getFile(),a=await J(await e.getFileHandle(`${t}.json`).catch(()=>{}));if(!a)return;return{file:n,size:n.size,total:a.total,etag:a.etag,complete:a.total?n.size>=a.total:!0===a.complete}}catch{return}},ee=async(e,t)=>{const n=await V(e,t);if(n){const e=await(async e=>await J(await e.getFileHandle("meta.json").catch(()=>{})))(n),t=await(async e=>{const t=[];try{for await(const[n,a]of X(e)){const e=Q(n);if("file"!=a.kind||e<0)continue;const o=await a.getFile();t.push({part:{start:e,size:o.size},file:o})}}catch{return}const n=(e=>{const t=[];let n=0;for(const a of[...e].sort((e,t)=>e.start-t.start))if(a.size){if(a.start!=n)break;t.push(a),n+=a.size}return t})(t.map(e=>e.part)),a=new Set(n.map(e=>e.start)),o=t.filter(e=>a.has(e.part.start)).sort((e,t)=>e.part.start-t.part.start).map(e=>e.file);if(o.length)return{file:1==o.length?o[0]:new Blob(o),size:n.reduce((e,t)=>e+t.size,0)}})(n),a=e?.total||0,o=t?.size??0,r=t?.file??new Blob;return{file:a&&o>a?r.slice(0,a):r,size:o,total:a,etag:e?.etag||"",complete:a?o>=a:!0===e?.complete}}return await Z(e,t)},te=(e,t)=>{const n=e.headers.get("content-range"),a=/bytes (\d+)-/.exec(n||""),o=/\/(\d+)\s*$/.exec(n||""),r=e.headers.get("etag")||"";if(206==e.status){const e=a?Number(a[1]):t.size,n=o?Number(o[1]):t.total;if(e!=t.size)return;if(t.total&&n&&t.total!=n)return;if(t.etag&&r&&t.etag!=r)return;return{start:e,total:n||t.total,etag:r||t.etag}}if(!t.etag||!r||t.etag==r)return{start:0,total:L(e)||t.total,etag:r||t.etag}},ne=async({dir:e,start:t,skip:n,total:a,body:o,onProgress:r,onChunk:i})=>{let s,c=t,l=0,u=n;const g=async()=>{const t=await e.getFileHandle((e=>`part-${e}`)(c),{create:!0});s=await t.createWritable(),l=0},m=async()=>{const e=s;s=void 0,e&&await e.close().catch(()=>{})};try{const e=o.getReader();for(;;){const{done:n,value:o}=await e.read();if(n)break;i();let f=0;for(u&&(f=Math.min(u,o.byteLength),u-=f);f<o.byteLength;){s||await g();const e=Math.min(F-l,o.byteLength-f);await s.write({type:"write",position:l,data:o.subarray(f,f+e)}),f+=e,l+=e,c+=e,l==F&&await m()}c>t&&r(a?c/a:0,d.download)}await m()}catch(e){throw await m(),e}return c},ae=e=>["NotAllowedError","NotSupportedError"].includes(e?.name||""),oe=e=>{const t=async()=>{const t=await G();if(t)try{return await t.getDirectoryHandle(e,{create:!0})}catch{return}};return{stored:async e=>{const n=await t();return n?await ee(n,H(e)):void 0},download:async({url:n,signal:a,onProgress:o})=>{const r=`${e}|${n}`,i=U.get(r);if(i)return await i;const s=await t();if(!s)return;const c=(async(e,t,n,a)=>{const o=H(t),r=await Z(e,o);if(r?.complete)return r;let i=await V(e,o);i||(await e.removeEntry(o).catch(()=>{}),await e.removeEntry(`${o}.json`).catch(()=>{}),i=await e.getDirectoryHandle(o,{create:!0}));let s=0,c=0;for(;;){if(n?.aborted)throw new Error("Cancelled");const r=await ee(e,o);if(r?.complete)return r;const l=r?.size??0,u=r?.total||0,g=r?.etag||"";if(l>s)s=l,c=0;else if(++c>4)throw new Error("Model download failed");l&&u&&a(l/u,d.download);const m=u?Math.min(l+F,u)-1:l+F-1,f=new AbortController,p=()=>f.abort(),h={size:l,total:u,etag:g};let w,y=setTimeout(p,3e4);n?.addEventListener("abort",p,{once:!0});try{if(w=await fetch(t,{signal:f.signal,headers:{Range:`bytes=${l}-${m}`}}),416==w.status){if(await(w.body?.cancel().catch(()=>{})),u&&l>=u){await K(i,{total:u,etag:g,complete:!0});continue}await Y(i),await K(i,{total:0,etag:"",complete:!1});continue}if(!w.ok||!w.body)throw await(w.body?.cancel().catch(()=>{})),new Error("Model download failed");const n=te(w,h);if(!n){await w.body.cancel().catch(()=>{}),await Y(i),await K(i,{total:0,etag:"",complete:!1});continue}await K(i,{total:n.total,etag:n.etag,complete:!1});const r=0==n.start?l:0,s=await ne({dir:i,start:n.start+r,skip:r,total:n.total,body:w.body,onProgress:a,onChunk:()=>{clearTimeout(y),y=setTimeout(p,3e4)}});if(!n.total){await K(i,{total:s,etag:n.etag,complete:!0});const t=await ee(e,o);if(t)return t}}catch(e){if(n?.aborted)throw new Error("Cancelled");if(e instanceof DOMException&&["QuotaExceededError","NotAllowedError","NotSupportedError","InvalidStateError","TypeMismatchError","SecurityError"].includes(e.name))throw e;await new Promise(e=>setTimeout(e,250+500*c))}finally{clearTimeout(y),n?.removeEventListener("abort",p)}}})(s,n,a,o).finally(()=>{U.delete(r)});return U.set(r,c),await c},remove:async e=>{const n=await t();n&&await(async(e,t)=>{await e.removeEntry(t,{recursive:!0}).catch(()=>{}),await e.removeEntry(`${t}.json`).catch(()=>{})})(n,H(e))},purge:async()=>{const t=await G();t&&await t.removeEntry(e,{recursive:!0}).catch(()=>{})}}},re="litertlm-models",ie=oe("litert-store"),se={num_output_candidates:1,wait_for_weight_uploads:!0,num_decode_steps_per_sync:3,sequence_batch_size:0,supported_lora_ranks:[],max_top_k:40,enable_decode_logits:!1,enable_external_embeddings:!1,use_submodel:!1,use_autosized_ringbuffers:!0};let ce=!1;const le=(...e)=>{ce&&console.log(...e)},de=(...e)=>{ce&&console.error(...e)},ue=async()=>{try{if("undefined"==typeof caches)return;return await caches.open(re)}catch{return}},ge=new Map,me=(e,t,n)=>{let a=0;return e.pipeThrough(new TransformStream({transform:(e,o)=>{a+=e.byteLength,n(t?a/t:0,d.download),o.enqueue(e)}}))},fe=async(e,t=!0)=>{const n=[];for(const a of e){if("string"==typeof a.content){n.push({role:a.role,content:a.content});continue}const e=[];for(const n of a.content)"text"!=n.type?t?e.push({type:n.type,data:await x(n.url)}):e.push({type:"text",text:`[${n.type}]`}):e.push({type:"text",text:n.text});n.push({role:a.role,content:e})}return n},pe=(e,t)=>{if(e.role!=t.role)return!1;if("string"==typeof e.content||"string"==typeof t.content)return e.content==t.content;if(e.content.length!=t.content.length)return!1;for(let n=0;n<e.content.length;n++){const a=e.content[n],o=t.content[n];if(a.type!=o.type)return!1;if("text"==a.type&&"text"==o.type){if(a.text!=o.text)return!1}else if(("image"==a.type&&"image"==o.type||"audio"==a.type&&"audio"==o.type)&&a.url!=o.url)return!1}return!0},he=async function*({engine:e,mod:t,params:n,messages:a,session:o}){const r=a[a.length-1],i=E(n.limits,n.reasoning);if("user"!=r?.role)throw new Error("No user turn");const s=a.slice(0,-1),c=(await fe([r]))[0];if(!c)throw new Error("Empty prompt");const l=!!o.conversation&&o.reasoning==n.reasoning&&o.maxTokens==i.max_tokens&&((e,t)=>{const n="system"==e[0]?.role?e[0]:void 0,a="system"==t[0]?.role?t[0]:void 0;if(!n!=!a)return!1;if(n&&a&&!pe(n,a))return!1;const o=n?e.slice(1):e,r=a?t.slice(1):t;if(!r.length||r.length>o.length)return!1;const i=o.length-r.length;for(let e=0;e<r.length;e++)if(!pe(o[i+e],r[e]))return!1;return!0})(o.sent,s);let d;if(l&&o.conversation)d=o.conversation;else{const a=await fe(s,!1);o.conversation=void 0,o.sent=[],d=await e.createConversation({sessionConfig:{maxOutputTokens:i.max_tokens,samplerParams:{type:t.SamplerType.TOP_K,temperature:i.temperature,k:40,p:.95}},preface:{...a.length?{messages:a}:{},extra_context:{enable_thinking:n.reasoning}},prefillPrefaceOnInit:a.length>0}),o.conversation=d,o.reasoning=n.reasoning,o.maxTokens=i.max_tokens,o.sent=[...s]}o.sent.push(r);let u,g=!1,m="";const f=()=>{u&&u.cancel().catch(()=>{});try{d.cancel()}catch{}};n.signal?.addEventListener("abort",f,{once:!0});try{if(n.signal?.aborted)return;const e=d.sendMessageStreaming(c).getReader();for(u=e;;){const{done:t,value:n}=await e.read();if(t)break;const a={};let o="string"==typeof n.content?n.content:"";if(Array.isArray(n.content))for(const e of n.content)"text"==e.type&&(o+=e.text);o&&(a.content=o,m+=o);const r=n.channels?.thought;r&&(a.reasoning=r),(a.content||a.reasoning)&&(yield{choices:[{delta:a}]})}g=!0}finally{n.signal?.removeEventListener("abort",f),g&&m?o.sent.push({role:"assistant",content:m}):(o.conversation=void 0,o.sent=[])}},we={backend:l.gpu,startLLM:async({model:e,limits:t,onProgress:n,signal:a,logging:o})=>{ce=!0===o;const i=e.litert;if(!i)throw new Error("Model has no LiteRT artifact");const s=await r.e(520).then(()=>r(507)),c=globalThis,u=c.Module;c.Module={print:le,printErr:de};try{await s.getOrLoadGlobalLiteRtLm(b().script)}finally{c.Module=u}if(a?.aborted)throw new Error("Cancelled");const g=C(i.repo,i.file),m=await ue();let f;const p=m?await m.match(g):void 0;if(p?.body)f=p.body;else{const e=await ie.stored(g);e?.complete&&(f=e.file.stream())}if(!f){const e=await ie.download({url:g,signal:a,onProgress:n}).catch(e=>{if(a?.aborted)throw new Error("Cancelled");if(!ae(e))throw e;o&&console.log("Local model store failed",e)});e&&(f=e.file.stream())}if(!f&&m)try{await(async(e,t,n,a)=>{const o=ge.get(e);if(o)return await o;const r=(async()=>{const o=await fetch(e,{signal:n});if(!o.ok||!o.body)throw new Error("Model download failed");const r=L(o),i=me(o.body,r,a);await t.put(e,new Response(i,{headers:{"content-type":"application/octet-stream",...r?{"content-length":`${r}`}:{}}}))})().finally(()=>{ge.delete(e)});return ge.set(e,r),await r})(g,m,a,n);const e=await m.match(g);e?.body&&(f=e.body)}catch(e){if(a?.aborted)throw new Error("Cancelled");o&&console.log("Local model cache failed",e)}if(f||(f=await(async(e,t,n)=>{const a=await fetch(e,{signal:t});if(!a.ok||!a.body)throw new Error("Model download failed");return me(a.body,L(a),n)})(g,a,n)),a?.aborted)throw new Error("Cancelled");n(1,d.load);const h=await s.Engine.create((({model:e,limits:t,source:n,mod:a})=>({model:n,mainExecutorSettings:{maxNumTokens:4096,...e.litert?.vision?{maxNumImages:Math.max(1,t.maxImages)}:{},backendConfig:se},...e.litert?.vision?{visionBackend:a.Backend.GPU,maxVisionTokensPerImage:70,minVisionTokensPerImage:70}:{},...e.litert?.audio?{audioBackend:a.Backend.GPU}:{}}))({model:e,limits:t,source:f,mod:s}));if(a?.aborted)throw await h.delete(),new Error("Cancelled");const w={sent:[],reasoning:!1,maxTokens:0};return{model:e,backend:l.gpu,contextSize:4096,chat:t=>(async({engine:e,model:t,mod:n,params:a,session:o})=>{ce=!0===a.logging;const r=()=>{try{o.conversation?.cancel()}catch{}o.conversation=void 0,o.sent=[]};try{return await N({model:t,params:a,stopped:()=>!!a.signal?.aborted,onStall:r,start:async r=>{if(a.signal?.aborted)throw new Error("Cancelled");if(j(r)&&!t.litert?.vision)throw new Error("Model does not accept images");if(S(r)&&!t.litert?.audio)throw new Error("Model does not accept audio");return he({engine:e,mod:n,params:a,messages:r,session:o})}})}catch(e){throw r(),e}})({engine:h,model:e,mod:s,params:t,session:w}),unload:async()=>{w.conversation=void 0,w.sent=[],await h.delete()}}},cached:async e=>{const t=e.litert;if(!t)return!1;const n=C(t.repo,t.file),[a,o]=await Promise.all([ue(),ie.stored(n)]);if(o?.complete)return!0;try{return!(!a||!await a.match(n))}catch{return!1}},hasCache:async()=>{try{for(const e of g){const t=e.litert;if(!t)continue;if(await ie.stored(C(t.repo,t.file)))return!0}const e=await ue();return!!e&&(await e.keys()).length>0}catch{return!1}},remove:async e=>{try{const t=e.litert;if(!t)return;const n=C(t.repo,t.file),a=await ue();await ie.remove(n),a&&await a.delete(n)}catch{}},purge:async()=>{try{await ie.purge(),"undefined"!=typeof caches&&await caches.delete(re)}catch{}}},ye=oe("wllama-store");let be,ve,xe="",ke=!1;const _e=e=>`${e.wasm}|${e.compatWasm}|${e.compatWorker}`,Me=e=>{const t=y();return{wasm:e?.wasm||t.wasm,compatWasm:e?.compatWasm||t.compatWasm,compatWorker:e?.compatWorker||t.compatWorker}},Te=e=>{const t=e=>(...t)=>{ke&&("string"==typeof t[0]&&t[0].includes("ggml_webgpu: Failed to get an adapter")||e(...t))};return{...e,log:t(e.log),warn:t(e.warn),error:t(e.error)}},Ee=async()=>{if(be)return be;const e=await r.e(924).then(()=>r(577)),t=ve||Me(),n=new e.Wllama({default:t.wasm},{logger:Te(e.LoggerWithoutDebug)});return ve||(ve=t,xe=_e(t)),n.setCompat({wasm:t.compatWasm,worker:t.compatWorker}),be=n,n},Ce=async()=>{const e=be;be=void 0;try{await(e?.exit())}catch{}},Le=(e,t)=>e.name.includes(t)||!!e.metadata?.originalURL?.includes(t),Pe=e=>{const t=e.cpu;return t?[{name:t.file,url:C(t.repo,t.file)},...t.mmproj?[{name:t.mmproj.file,url:C(t.repo,t.mmproj.file)}]:[]]:[]},je=async e=>{const t=Pe(e);if(!t.length)return!1;for(const e of t){const t=await ye.stored(e.url);if(!t?.complete)return!1}return!0},Se=async(e,t)=>{const n=await e.cacheManager.list();return Pe(t).every(e=>n.some(t=>Le(t,e.name)))},$e=async({model:e,signal:t,onProgress:n})=>{const a=Pe(e),o=[];for(let e=0;e<a.length;e++){if(t?.aborted)throw new Error("Cancelled");const r=await ye.download({url:a[e].url,signal:t,onProgress:(t,o)=>n((e+t)/a.length,o)});if(!r)return;o.push(r.file)}return o.length?o:void 0},Re=async e=>{const t=[];for(const n of e){if("string"==typeof n.content){"system"==n.role?t.push({role:"system",content:n.content}):"assistant"==n.role?t.push({role:"assistant",content:n.content}):t.push({role:"user",content:n.content});continue}const e=[];for(const t of n.content)"text"==t.type?e.push({type:"text",text:t.text}):e.push({type:t.type,data:await x(t.url)});t.push({role:"user",content:e})}return t},Ae={backend:l.cpu,startLLM:async({model:e,limits:t,onProgress:n,signal:a,logging:o,assets:r})=>{ke=!0===o;const i=e.cpu;if(!i)throw new Error("Model has no CPU artifact");const s=Me(r),c=_e(s);be&&c!=xe&&await Ce(),ve=s,xe=c;const u=(g="undefined"!=typeof navigator&&navigator.hardwareConcurrency||4,m=t.maxThreads,Math.max(1,Math.min(Math.max(2,Math.min(8,(g||4)-1)),m)));var g,m;const f=await Ee(),p=await je(e),h=i.mmproj?.file,w=C(i.repo,i.file),y=h?C(i.repo,h):"";if(a?.aborted)throw await Ce(),new Error("Cancelled");if(!p&&h){const e=await f.cacheManager.list(),t=e.some(e=>Le(e,i.file)),n=e.some(e=>Le(e,h));t&&!n&&await f.cacheManager.deleteMany(e=>Le(e,i.file))}const b={n_ctx:8192,n_threads:u,reasoning_budget_tokens:t.maxThinkingTokens,n_gpu_layers:0,jinja:!0};try{let t=p?await $e({model:e,signal:a,onProgress:n}):void 0;t||p||await Se(f,e).catch(()=>!1)||(t=await $e({model:e,signal:a,onProgress:n}).catch(e=>{if(a?.aborted)throw new Error("Cancelled");if(!ae(e))throw e;o&&console.log("Local model store failed",e)})),t?(n(1,d.load),await f.loadModel(t,b)):await f.loadModelFromUrl(y?{url:w,mmprojUrl:y}:w,{...b,useCache:!0,signal:a,progressCallback:({loaded:e,total:t})=>{const a=t?e/t:0;n(a,a>=1?d.load:d.download)}})}catch(e){throw await Ce(),e}if(a?.aborted)throw await Ce(),new Error("Cancelled");if(!f.getLoadedContextInfo().n_ctx)throw await Ce(),new Error("Failed to create model context");return{model:e,backend:l.cpu,contextSize:8192,chat:t=>(async(e,t,n)=>{if(ke=!0===n.logging,e.isModelLoaded())return await N({model:t,params:n,stopped:()=>!!n.signal?.aborted,start:async a=>{if(j(a)&&!e.supportInputModality("image"))throw new Error("Model does not accept images");if(S(a)&&!e.supportInputModality("audio"))throw new Error("Model does not accept audio");return await e.createChatCompletion({messages:await Re(a),...E(n.limits,n.reasoning),abortSignal:n.signal,...t.reasoning?{chat_template_kwargs:{enable_thinking:n.reasoning}}:{}})}})})(f,e,t),unload:async()=>{if(be==f)await Ce();else try{await f.exit()}catch{}}}},cached:async e=>{try{return!!e.cpu&&(!!await je(e)||await Se(await Ee(),e))}catch{return!1}},hasCache:async()=>{try{for(const e of g.flatMap(e=>Pe(e)))if(await ye.stored(e.url))return!0;const e=await Ee();return(await e.cacheManager.list()).length>0}catch{return!1}},remove:async e=>{const t=Pe(e);try{for(const e of t)await ye.remove(e.url)}catch{}try{const e=await Ee();await e.cacheManager.deleteMany(e=>t.some(t=>Le(e,t.name)))}catch{}},purge:async()=>{try{await ye.purge()}catch{}try{const e=await Ee();await e.cacheManager.clear()}catch{}}},We=((e={})=>{const t=e.engines?.cpu||Ae,n=e.engines?.litert||we;let a,o,r,i,s,u,m,f=l.none,p=c.idle,h=[],w=0,y={percent:0,phase:d.download};const b=a=>{if(e.backend!=l.none)return e.backend==l.cpu?a.cpu?t:void 0:e.backend==l.gpu?a.litert?n:void 0:a.litert&&"undefined"!=typeof navigator&&navigator.gpu&&"undefined"!=typeof WebAssembly?n:!a.cpu||"undefined"==typeof window&&"undefined"==typeof self&&"undefined"==typeof Worker||"undefined"==typeof WebAssembly?void 0:t},v=(e,t)=>{y={percent:Math.max(0,Math.min(1,e)),phase:t||d.download};for(const e of[...h])e.report(y.percent,y.phase)},x=e=>{p=e},k=()=>{w++,u?.abort(),u=void 0,s=void 0,p==c.downloading&&x(c.idle)},_=(e,t)=>{const n=i;return n?new Promise(a=>{let o=!1;const r={report:e,settle:e=>{o||(o=!0,t?.removeEventListener("abort",s),a(e))}},s=()=>{(e=>{h=h.filter(t=>t!=e),queueMicrotask(()=>{!h.length&&i&&k()})})(r),r.settle(!1)};h.push(r),t?.aborted?s():t?.addEventListener("abort",s,{once:!0}),n.then(r.settle,()=>r.settle(!1))}):Promise.resolve(!1)},M=()=>p==c.ready&&!!a&&!!o;return{load:async({model:e,limits:t,assets:n,logging:l,signal:m,onProgress:p})=>{if(m?.aborted)return!1;if(M()&&o?.id==e.id)return!0;if(i&&s==e.id)return p(y.percent,y.phase),await _(p,m);i&&(k(),h=[],await i.catch(()=>!1));const T=++w,E=(async()=>{await(async()=>{try{if("undefined"==typeof navigator)return;await(navigator.storage?.persist?.())}catch{}})();const i=b(e);if(T!=w)return!1;if(!i)return x(c.unsupported),!1;if(await(async(e,t)=>{if(o&&o.id!=e.id){const e=o,t=r,n=a;a=void 0,o=void 0,r=void 0,await(n?.unload()),await(t?.remove(e))}if(await t.hasCache())for(const n of g)n.id!=e.id&&b(n)==t&&await t.remove(n)})(e,i),T!=w)return!1;const s=await i.cached(e);if(T!=w)return!1;x(c.downloading),v(0,d.download),s&&v(1,d.load);const m=new AbortController;u=m;try{const s=await i.startLLM({model:e,limits:t,logging:l,signal:m.signal,assets:n,onProgress:(e,t)=>{T==w&&v(e,t)}});return T!=w||m.signal.aborted?(await s.unload(),!1):(a=s,o=e,r=i,f=i.backend,x(c.ready),v(1,d.load),!0)}catch(e){return T!=w||(a=void 0,o=void 0,r=void 0,l&&console.log("Local model failed",e),x(c.error)),!1}finally{u==m&&(u=void 0)}})().finally(()=>{i==E&&(i=void 0,s=void 0,h=[])});return i=E,s=e.id,await _(p,m)},chat:async e=>{if(!M()||!a)return;for(;m;)await m.catch(()=>{});if(!M()||!a)return;const t=a.chat(e);m=t;try{return await t}finally{m==t&&(m=void 0)}},unload:async()=>{const e=a;k(),a=void 0,o=void 0,r=void 0,x(c.idle),await(e?.unload())},ready:M,loading:()=>!M()&&p==c.downloading,status:()=>p,backend:()=>f,model:()=>o}})(),Be=(e,t,n)=>{const a=e.status(),o=m(t);return{availability:a==c.ready?s.online:a==c.error||a==c.unsupported?s.offline:s.away,status:a,backend:e.backend(),supportsImages:!!o&&h(o),supportsAudio:!!o&&w(o),ready:e.ready(),loading:e.loading(),requesting:n.requesting,phase:n.phase,percent:n.percent,...n.error?{error:n.error}:{}}},Ie=(e=>{let t,n=0;const a=t=>t.input.onStatus?.(Be(e,t.model??t.input.model,t));return o=>{t?.controller.abort();const r={token:++n,input:o,controller:new AbortController,percent:e.ready()?100:0,phase:e.ready()?d.load:d.download,requesting:!1,cancelled:!1};return t=r,a(r),(async t=>{const{input:o}=t,r=!0===o.logging,s=await p(o.model);if(t.model=s?.id,t.token!=n)return;const c=o.messages.some(e=>e.role!=i.system&&"string"==typeof e.content&&!!e.content.trim()),l=o.messages.some(e=>e.role==i.user&&!!e.images?.some(e=>"string"==typeof e&&!!e)),u=o.messages.some(e=>e.role==i.user&&!!e.audio?.some(e=>"string"==typeof e&&!!e)),g=c||l||u;if(!s)return t.error="Unknown model",void a(t);if(!g)return void a(t);const m=h(s),f=w(s);if(!(c||l&&m||u&&f))return u&&!f?t.error="Model does not accept audio":l&&!m&&(t.error="Model does not accept images"),void a(t);const y=T(o.limits,!!s.litert),b=await e.load({model:s,limits:y,logging:r,signal:t.controller.signal,onProgress:(e,o)=>{t.token==n&&(t.percent=Math.round(100*e),t.phase=o,a(t))}}).catch(e=>(t.token==n&&(t.error=`${e?.message||`${e}`}`),!1));if(t.token!=n)return;if(t.cancelled)return;if(!b)return t.error||(t.error="Model not ready"),void a(t);const v=l&&!m||u&&!f?o.messages.map(({images:e,audio:t,...n})=>({...n,...e&&m?{images:e}:{},...t&&f?{audio:t}:{}})):o.messages;t.requesting=!0,t.error=void 0,t.percent=100,t.phase=d.load,a(t);let x,k="",_="";try{x=await e.chat({messages:v,limits:y,reasoning:!0===o.reasoning,logging:r,onReason:e=>{t.token!=n||t.cancelled||(k+=e,o.onThinking?.(k))},onText:e=>{t.token!=n||t.cancelled||(_+=e,o.onResponse?.(_))},onSpeed:e=>{t.token!=n||t.cancelled||o.onSpeed?.(e)},signal:t.controller.signal})}catch(e){if(t.token!=n)return;return t.requesting=!1,t.error=`${e?.message||`${e}`}`,void a(t)}t.token==n&&(t.requesting=!1,x||t.cancelled||(t.error="No reply"),a(t))})(r),()=>(t=>{t.token==n&&(t.cancelled=!0,t.controller.abort(),e.status()!=c.ready?queueMicrotask(()=>a(t)):e.unload().then(()=>a(t)))})(r)}})(We),qe=(ze=We,async(e={})=>{const t=await p(e.model),n=n=>e.onStatus?.(Be(ze,t?.id,n));if(!t)return n({requesting:!1,phase:d.download,percent:0,error:"Unknown model"}),!1;let a,o=0,r=d.download;const i=ze.ready()&&ze.model()?.id==t.id;return await ze.load({model:t,limits:T(e.limits,!!t.litert),logging:!0===e.logging,onProgress:(e,t)=>{o=Math.round(100*e),r=t,n({requesting:!1,phase:r,percent:o})}}).catch(e=>(a=`${e?.message||`${e}`}`,!1))?(i&&n({requesting:!1,phase:d.load,percent:100}),!0):(n({requesting:!1,phase:r,percent:o,error:a||"Model not ready"}),!1)});var ze;export{u as ChatModelId,s as ChatStatus,l as LlmBackend,d as LlmPhase,i as LlmRole,c as LlmStatus,Ie as createChat,qe as preloadModel};
package/dist/types.d.ts CHANGED
@@ -5,6 +5,8 @@ interface ChatMessage {
5
5
  content: string;
6
6
  /** Image data URLs or URLs attached to the turn */
7
7
  images?: string[];
8
+ /** Audio data URLs or URLs attached to the turn */
9
+ audio?: string[];
8
10
  }
9
11
  /** One on-device model */
10
12
  interface ChatModel {
@@ -19,6 +21,8 @@ interface ChatModel {
19
21
  /** Vision projector, present when the CPU build accepts images */
20
22
  mmproj?: {
21
23
  file: string;
24
+ /** Whether the projector also accepts audio */
25
+ audio?: boolean;
22
26
  };
23
27
  };
24
28
  /** LiteRT-LM (WebGPU) artifact */
@@ -27,6 +31,8 @@ interface ChatModel {
27
31
  file: string;
28
32
  /** Whether the build accepts images */
29
33
  vision: boolean;
34
+ /** Whether the build accepts audio */
35
+ audio: boolean;
30
36
  };
31
37
  }
32
38
  /** Token, prompt and thread budgets of an on-device generation */
@@ -39,6 +45,8 @@ interface LlmLimits {
39
45
  maxReasoningTokens: number;
40
46
  /** Max images sent in one prompt */
41
47
  maxImages: number;
48
+ /** Max audio clips sent in one prompt */
49
+ maxAudio: number;
42
50
  /** Max characters kept per message */
43
51
  maxContentLength: number;
44
52
  /** Max characters of the whole prompt */
@@ -54,6 +62,8 @@ interface ChatStatusSnapshot {
54
62
  backend: LlmBackend;
55
63
  /** Whether the requested model accepts images on this backend */
56
64
  supportsImages: boolean;
65
+ /** Whether the requested model accepts audio on this backend */
66
+ supportsAudio: boolean;
57
67
  ready: boolean;
58
68
  loading: boolean;
59
69
  /** Whether a reply is streaming right now */
@@ -297,13 +307,16 @@ interface LlmEngine {
297
307
  /** Delete every on-device artifact this engine keeps, including orphans */
298
308
  purge: () => Promise<void>;
299
309
  }
300
- /** One text or image segment of a user turn */
310
+ /** One text, image or audio segment of a user turn */
301
311
  type EngineContentPart = {
302
312
  type: `text`;
303
313
  text: string;
304
314
  } | {
305
315
  type: `image`;
306
316
  url: string;
317
+ } | {
318
+ type: `audio`;
319
+ url: string;
307
320
  };
308
321
  /** One wllama content segment */
309
322
  type WllamaContentPart = {
@@ -312,6 +325,9 @@ type WllamaContentPart = {
312
325
  } | {
313
326
  type: `image`;
314
327
  data: ArrayBuffer;
328
+ } | {
329
+ type: `audio`;
330
+ data: ArrayBuffer;
315
331
  };
316
332
  /** One wllama chat turn */
317
333
  type WllamaMessage = {
@@ -324,7 +340,7 @@ type WllamaMessage = {
324
340
  role: `assistant`;
325
341
  content: string;
326
342
  };
327
- /** One OpenAI-style message */
343
+ /** One OpenAI-style message, a user turn may carry media parts */
328
344
  type EngineMessage = {
329
345
  role: `system`;
330
346
  content: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@degreesign/chat",
3
- "version": "0.3.7",
3
+ "version": "0.4.0",
4
4
  "description": "Headless on-device AI chat for browsers and Node with wllama CPU and LiteRT WebGPU engines",
5
5
  "main": "dist/node/chat.node.min.js",
6
6
  "module": "dist/browser/chat.min.mjs",