@degreesign/chat 0.3.7 → 0.5.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.
@@ -123,7 +125,17 @@ only to a model with vision support, center-crops each image to a square,
123
125
  resizes it to 288 x 288, and caps the number with `limits.maxImages`. The
124
126
  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
- 768 x 768. The CPU engine uses the 288 crop directly.
128
+ 768 x 768. The CPU engine uses the 288 crop directly. On a phone or tablet,
129
+ Gemma runs the text-only browser build, so images there go through
130
+ `qwen3_vl_2b` instead.
131
+
132
+ Attach a recording with `audio: [dataUrl]`. Gemma 4 E2B accepts WAV, MP3 and
133
+ FLAC on the desktop WebGPU engine and loads its audio encoder on demand. A short
134
+ 16 kHz mono WAV is the best input. `limits.maxAudio` caps the clips (default 1).
135
+ A model without audio drops the clip when the turn also has text; an audio-only
136
+ turn reports `Model does not accept audio`. The phone build is text only, so
137
+ audio follows the same rule as images there. The package does not transcode, so
138
+ the host supplies one of those formats.
127
139
 
128
140
  The package never writes storage, so the host appends the user message before
129
141
  the call and appends the assistant message after the last `onResponse`.
@@ -131,8 +143,12 @@ the call and appends the assistant message after the last `onResponse`.
131
143
  ## Enable WebGPU
132
144
 
133
145
  A request without `model` picks `gemma4_e2b` when the browser exposes a WebGPU
134
- adapter and `qwen3_vl_2b` when it does not. The GPU engine is far faster, so
135
- check the browser before blaming the package:
146
+ adapter and `qwen3_vl_2b` when it does not. A phone or tablet asking for Gemma
147
+ gets `gemma-4-E2B-it-gpu.litertlm`, the 2.0 GB browser build, which is text
148
+ only. A desktop gets the 2.6 GB multimodal build with vision and audio. Hosts
149
+ that need images on mobile pass `qwen3_vl_2b`; audio is desktop only. The GPU
150
+ engine is far faster on a desktop, so check the browser before blaming the
151
+ package:
136
152
 
137
153
  ```js
138
154
  // Run in the console of the page that loads the package
@@ -185,15 +201,18 @@ CPU, not the GPU. Remove it when measuring speed. Headless Chromium hides
185
201
  `--enable-features=Vulkan,WebGPU` and `--use-angle=vulkan`; check the adapter
186
202
  there too, because a missing adapter silently selects the CPU model.
187
203
 
188
- ### GPU memory and images
204
+ ### GPU memory, images and audio
189
205
 
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.
206
+ The WebGPU engine keeps the model, its KV cache, the vision encoder and the
207
+ audio encoder in GPU memory. The vision encoder loads on the first image turn
208
+ and the audio encoder loads on the first audio turn. A card that is already busy
209
+ can lose the device (`VK_ERROR_DEVICE_LOST`) on the first image turn. The
210
+ package requests the smallest Gemma 4 vision budget, 70 tokens and 630 patches,
211
+ so the encoder runs one small signature and the crop is resized to 384 x 384
212
+ rather than the full 768 x 768. If the error still appears, close other GPU
213
+ tabs, unload the model, or switch to the CPU model. A lost device needs a page
214
+ or model reload; later calls cannot reuse it. A phone or tablet uses the
215
+ text-only browser build, so it never starts the vision or audio encoder.
197
216
 
198
217
  ## Public API
199
218
 
@@ -222,8 +241,8 @@ requested model is already loaded, no download or load happens.
222
241
  Without `model` the package picks the GPU build when the browser provides a
223
242
  WebGPU adapter and the CPU build when it does not. The budget defaults are 250
224
243
  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.
244
+ CPU), 2 images, 1 audio clip, 1000 characters per message, 10000 prompt
245
+ characters and 6 CPU threads.
227
246
 
228
247
  `CancelChat` is `() => void`. Its meaning depends on the current state:
229
248
 
@@ -273,6 +292,7 @@ on failure. A preload owns no cancel function; a new load replaces it.
273
292
  | `status` | `LlmStatus`: `idle`, `downloading`, `ready`, `error` or `unsupported`. |
274
293
  | `backend` | `LlmBackend.cpu`, `.gpu` or `.none`. |
275
294
  | `supportsImages` | Whether the requested model accepts images. |
295
+ | `supportsAudio` | Whether the requested model accepts audio. |
276
296
  | `ready` | Whether a model is loaded and accepts a reply. |
277
297
  | `loading` | Whether a download or load is in flight. |
278
298
  | `requesting` | Whether a reply is streaming right now. |
@@ -296,10 +316,15 @@ without it goes quiet again.
296
316
 
297
317
  ## Models
298
318
 
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 |
319
+ | `ChatModelId` | Name | Reasoning | Images | Audio | CPU | WebGPU |
320
+ | ------------- | ---- | --------- | ------ | ----- | --- | ------ |
321
+ | `qwen3_vl_2b` | Qwen3-VL 2B | Yes | Yes | No | Yes | No |
322
+ | `gemma4_e2b` | Gemma 4 E2B | Yes | Desktop | Desktop | No | Yes |
323
+
324
+ A phone or tablet asking for Gemma gets the browser build,
325
+ `gemma-4-E2B-it-gpu.litertlm`, which is text only. It has no vision or audio
326
+ encoder, so `supportsImages` and `supportsAudio` report `false` there. Use
327
+ `qwen3_vl_2b` for images on mobile.
303
328
 
304
329
  The first call downloads the model before its engine loads it. Later calls
305
330
  reuse the loaded runtime. Only one model stays in memory. An interrupted
@@ -307,16 +332,20 @@ download keeps every part it committed and resumes from the last one on the
307
332
  next attempt, including after a reload.
308
333
 
309
334
  Qwen3-VL 2B downloads a vision projector of about 445 MB with the model, so its
310
- total download is about 1.5 GB. It runs on the CPU engine.
335
+ total download is about 1.5 GB. It runs on the CPU engine, with a 4096 token
336
+ context on a phone and 8192 on a desktop.
311
337
 
312
- 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.
338
+ Gemma 4 E2B downloads `gemma-4-E2B-it.litertlm`, about 2.6 GB, on a desktop. It
339
+ runs on the WebGPU engine with its vision and audio encoders in the same file,
340
+ so an image or audio prompt does not download a projector. A phone or tablet
341
+ instead downloads the 2.0 GB `gemma-4-E2B-it-gpu.litertlm`, the precompiled
342
+ GPU_ARTISAN build Google ships for browsers. The smaller file is what keeps a
343
+ mobile tab alive at the end of the model load.
315
344
 
316
345
  The WebGPU engine keeps one LiteRT conversation for consecutive turns. When the
317
346
  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.
347
+ history opens a fresh conversation, and older images and audio become `[image]`
348
+ and `[audio]` notes in its preface.
320
349
 
321
350
  ## Prompt assembly
322
351
 
@@ -333,20 +362,24 @@ user: {next turn}
333
362
  - Leading assistant turns are dropped, because the transcript must start with a
334
363
  user turn.
335
364
  - Consecutive same-role turns are merged.
336
- - Messages with no text and no images are skipped.
365
+ - Messages with no text, images or audio are skipped.
337
366
  - `maxContentLength` clips each message and `promptCharBudget` bounds the whole
338
367
  prompt.
339
368
  - 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.
369
+ - User turns may carry `images` or `audio`. They are sent only to a model with
370
+ vision or audio support.
342
371
  - Images are collected from the newest turns first and capped by `maxImages`
343
372
  (default 2). Older image parts are dropped and their text stays.
344
373
  - Every image part is center-cropped to a square, then resized to 288 x 288.
345
374
  Non-square edges are discarded and smaller images are upscaled. The WebGPU
346
375
  engine is told to use a 70-token vision budget, so it encodes the crop at
347
376
  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`.
377
+ - Audio is collected from the newest turns first and capped by `maxAudio`
378
+ (default 1). Older audio parts are dropped and their text stays. WAV, MP3 and
379
+ FLAC are accepted, and a short 16 kHz mono WAV is the best input.
380
+ - Image and audio payloads do not count against `promptCharBudget`.
381
+ - An image-only turn with a text model reports `Model does not accept images`,
382
+ and an audio-only turn reports `Model does not accept audio`.
350
383
 
351
384
  ## Engines and assets
352
385
 
@@ -488,9 +521,9 @@ const useReply = () => {
488
521
  | `ChatInput` | One request: model, messages, budgets, reasoning and callbacks. |
489
522
  | `ChatPreloadInput` | Model, limits, logging and status, the subset `preloadModel` needs. |
490
523
  | `CancelChat` | The function returned by `createChat`. |
491
- | `ChatMessage` | One prompt turn, `role`, `content` and optional `images`. |
524
+ | `ChatMessage` | One prompt turn, `role`, `content` and optional `images` and `audio`. |
492
525
  | `ChatStatusSnapshot` | Availability, engine status, backend, progress and request state. |
493
- | `LlmLimits` | Token, prompt, image and thread budgets, all overridable. |
526
+ | `LlmLimits` | Token, prompt, image, audio and thread budgets, all overridable. |
494
527
  | `WllamaAssets` | Wasm, compat wasm and compat worker URLs. |
495
528
 
496
529
  The public enums are `ChatModelId`, `LlmRole`, `ChatStatus`, `LlmStatus`,
@@ -518,11 +551,22 @@ No. The host passes the full prompt on every call and keeps the reply.
518
551
 
519
552
  Yes, on models with vision support. Put image URLs in a user turn's `images`
520
553
  array. Qwen3-VL 2B accepts images on the CPU engine and Gemma 4 E2B accepts
521
- images on the WebGPU engine. Each image is center-cropped to a square and
522
- resized to 288 x 288 before the engine sees it. The WebGPU engine encodes that
523
- crop at 384 x 384 with the smallest Gemma 4 vision budget. A model without
524
- vision drops images when the turn also has text; an image-only turn reports
525
- `Model does not accept images`.
554
+ images on the desktop WebGPU engine. On a phone or tablet Gemma runs the
555
+ text-only browser build, so use `qwen3_vl_2b` for mobile images. Each image is
556
+ center-cropped to a square and resized to 288 x 288 before the engine sees it.
557
+ The WebGPU engine encodes that crop at 384 x 384 with the smallest Gemma 4
558
+ vision budget. A model without vision drops images when the turn also has text;
559
+ an image-only turn reports `Model does not accept images`.
560
+
561
+ ### Does it support audio?
562
+
563
+ Yes, on models with audio support. Put an audio URL in a user turn's `audio`
564
+ array. Gemma 4 E2B accepts WAV, MP3 and FLAC on the desktop WebGPU engine, and a
565
+ short 16 kHz mono WAV is the best input. `limits.maxAudio` caps the clips
566
+ (default 1). Audio is desktop only; the mobile browser build has no audio
567
+ encoder. The package does not transcode, so the host supplies one of those
568
+ formats. A model without audio drops the clip when the turn also has text; an
569
+ audio-only turn reports `Model does not accept audio`.
526
570
 
527
571
  ### Does it need a server?
528
572
 
@@ -546,9 +590,10 @@ when an older version stored the file. The package stores no model data itself.
546
590
  ### Which model should I pick?
547
591
 
548
592
  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.
593
+ reasoning, images and audio on desktop WebGPU, with much faster media prompts
594
+ but a larger download and a slower first compile. Use Qwen for broad browser
595
+ support and for mobile images. Use Gemma on a desktop with Chromium WebGPU, or
596
+ on a phone for text with the smaller browser build.
552
597
 
553
598
  ## Licensing
554
599
 
@@ -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,web:{file:"gemma-4-E2B-it-gpu.litertlm",vision:!1,audio:!1}}}],d=e=>{const t=e??("undefined"==typeof navigator?void 0:navigator);if(!t)return!1;if("boolean"==typeof t.userAgentData?.mobile)return t.userAgentData.mobile;const n=t.userAgent||"";return/Android|iPhone|iPad|iPod|Mobile/i.test(n)||/Macintosh/.test(n)&&t.maxTouchPoints>1},u=(e,t=d())=>{const n=e.litert;if(n)return t&&n.web?n.web:{file:n.file,vision:n.vision,audio:n.audio}},g=e=>e?l.find(t=>t.id==e):void 0,m=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,f=async e=>g(e||await m()),p=(e,t=d())=>!!e.cpu?.mmproj||!0===u(e,t)?.vision,h=(e,t=d())=>!!e.cpu?.mmproj?.audio||!0===u(e,t)?.audio,w=new URL("./../assets/wllama/wllama.wasm",import.meta.url).href,y=new URL("./../assets/wllama/compat/wllama.wasm",import.meta.url).href,b=new URL("./../assets/wllama/compat/wllama.js",import.meta.url).href,v=new URL("./../assets/litert/litertlm_wasm_internal.js",import.meta.url).href,x=(new URL("./../assets/litert/litertlm_wasm_internal.wasm",import.meta.url).href,288),k=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},_=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})(x,x),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,x,x),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}},M={maxReplyTokens:250,maxThinkingTokens:128,maxReasoningTokens:320,maxImages:2,maxAudio:1,maxContentLength:1e3,promptCharBudget:1e4,maxThreads:6},T=(e,t,n)=>"number"==typeof e&&Number.isFinite(e)&&e>=n?Math.floor(e):t,E=(e={},t=!1)=>{const n=M;return{maxReplyTokens:T(e.maxReplyTokens,n.maxReplyTokens,1),maxThinkingTokens:T(e.maxThinkingTokens,n.maxThinkingTokens,1),maxReasoningTokens:T(e.maxReasoningTokens,t?1200:n.maxReasoningTokens,1),maxImages:T(e.maxImages,n.maxImages,0),maxAudio:T(e.maxAudio,n.maxAudio,0),maxContentLength:T(e.maxContentLength,n.maxContentLength,1),promptCharBudget:T(e.promptCharBudget,n.promptCharBudget,1),maxThreads:T(e.maxThreads,n.maxThreads,1)}},C=(e,t)=>({stream:!0,temperature:.7,max_tokens:t?e.maxReasoningTokens:e.maxReplyTokens}),L=(e,t)=>`https://huggingface.co/${e}/resolve/main/${t}`,P=e=>Number(e.headers.get("content-length"))||0,R=(e,t)=>e.some(e=>Array.isArray(e.content)&&e.content.some(e=>e.type==t)),$=e=>R(e,"image"),j=e=>R(e,"audio"),S=["<|channel>thought\n","<|channel>thought","..."],A=["<channel|>","..."],B=(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,I=(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 q extends Error{}const z=async(e,t,n)=>{let a;try{return await Promise.race([e,new Promise((e,o)=>{a=setTimeout(()=>o(new q(n)),t)})])}finally{a&&clearTimeout(a)}},O=e=>{try{Promise.resolve(e.return?.()).catch(()=>{})}catch{}},U=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,A);if(t>=0){t&&e(s.slice(0,t)),s=s.slice(t+i.length),a=!1;continue}const c=r(s,A);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 z(r(),s,"Model did not start"))[Symbol.asyncIterator]();for(;;){if(i()){O(e);break}let a;try{a=await z(e.next(),m?c:s,m?"Model stopped responding":"Model did not reply")}catch(n){if(n instanceof q&&(d||u)){l?.(),O(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 q&&!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: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 _(t.url)}:t);t.push({role:"user",content:e})}return t})(((e,t=M)=>{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:B(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=I(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 U({model:e,logging:t.logging,onReason:t.onReason,onText:t.onText,onSpeed:t.onSpeed,start:()=>n(i),stopped:o,onStall:r})},F=33554432,D=new Map;let H=!1;const Q=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}`},G=e=>{const t=/^part-(\d+)$/.exec(e);return t?Number(t[1]):-1},V=async()=>{if(!H)if("undefined"!=typeof navigator&&navigator.storage?.getDirectory)try{return await navigator.storage.getDirectory()}catch{return}else H=!0},J=async(e,t)=>{try{return await e.getDirectoryHandle(t)}catch{return}},K=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}},X=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{}},Y=async function*(e){for await(const t of e)yield t},Z=async e=>{try{for await(const[t,n]of Y(e))"file"==n.kind&&G(t)>=0&&await e.removeEntry(t).catch(()=>{})}catch{}},ee=async(e,t)=>{try{const n=await(await e.getFileHandle(t)).getFile(),a=await K(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}},te=async(e,t)=>{const n=await J(e,t);if(n){const e=await(async e=>await K(await e.getFileHandle("meta.json").catch(()=>{})))(n),t=await(async e=>{const t=[];try{for await(const[n,a]of Y(e)){const e=G(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 ee(e,t)},ne=(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:P(e)||t.total,etag:r||t.etag}},ae=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(F-d,o.byteLength-f);await c.write({type:"write",position:d,data:o.subarray(f,f+e)}),f+=e,d+=e,l+=e,d==F&&await m()}l>t&&r(a?l/a:0,s.download)}await m()}catch(e){throw await m(),e}return l},oe=e=>["NotAllowedError","NotSupportedError"].includes(e?.name||""),re=e=>{const t=async()=>{const t=await V();if(t)try{return await t.getDirectoryHandle(e,{create:!0})}catch{return}};return{stored:async e=>{const n=await t();return n?await te(n,Q(e)):void 0},download:async({url:n,signal:a,onProgress:o})=>{const r=`${e}|${n}`,i=D.get(r);if(i)return await i;const c=await t();if(!c)return;const l=(async(e,t,n,a)=>{const o=Q(t),r=await ee(e,o);if(r?.complete)return r;let i=await J(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 te(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+F,u)-1:d+F-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 X(i,{total:u,etag:g,complete:!0});continue}await Z(i),await X(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=ne(w,h);if(!n){await w.body.cancel().catch(()=>{}),await Z(i),await X(i,{total:0,etag:"",complete:!1});continue}await X(i,{total:n.total,etag:n.etag,complete:!1});const r=0==n.start?d:0,s=await ae({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 X(i,{total:s,etag:n.etag,complete:!0});const t=await te(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(()=>{D.delete(r)});return D.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,Q(e))},purge:async()=>{const t=await V();t&&await t.removeEntry(e,{recursive:!0}).catch(()=>{})}}},ie="litertlm-models",se=re("litert-store"),ce={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 le=!1;const de=(...e)=>{le&&console.log(...e)},ue=(...e)=>{le&&console.error(...e)},ge=async()=>{try{if("undefined"==typeof caches)return;return await caches.open(ie)}catch{return}},me=new Map,fe=(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)}}))},pe=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 k(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},he=(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},we=async function*({engine:e,mod:t,params:n,messages:a,session:o}){const r=a[a.length-1],i=C(n.limits,n.reasoning);if("user"!=r?.role)throw new Error("No user turn");const s=a.slice(0,-1),c=(await pe([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&&!he(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(!he(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 pe(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=[])}},ye={backend:i.gpu,startLLM:async({model:e,limits:t,onProgress:a,signal:o,logging:r})=>{le=!0===r;const c=e.litert,l=u(e);if(!c||!l)throw new Error("Model has no LiteRT artifact");const d=await n.e(520).then(()=>n(507)),g=globalThis,m=g.Module;g.Module={print:de,printErr:ue};try{await d.getOrLoadGlobalLiteRtLm(v)}finally{g.Module=m}if(o?.aborted)throw new Error("Cancelled");const f=L(c.repo,l.file),p=await ge();let h;const w=p?await p.match(f):void 0;if(w?.body)h=w.body;else{const e=await se.stored(f);e?.complete&&(h=e.file.stream())}if(!h){const e=await se.download({url:f,signal:o,onProgress:a}).catch(e=>{if(o?.aborted)throw new Error("Cancelled");if(!oe(e))throw e;r&&console.log("Local model store failed",e)});e&&(h=e.file.stream())}if(!h&&p)try{await(async(e,t,n,a)=>{const o=me.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=P(o),i=fe(o.body,r,a);await t.put(e,new Response(i,{headers:{"content-type":"application/octet-stream",...r?{"content-length":`${r}`}:{}}}))})().finally(()=>{me.delete(e)});return me.set(e,r),await r})(f,p,o,a);const e=await p.match(f);e?.body&&(h=e.body)}catch(e){if(o?.aborted)throw new Error("Cancelled");r&&console.log("Local model cache failed",e)}if(h||(h=await(async(e,t,n)=>{const a=await fetch(e,{signal:t});if(!a.ok||!a.body)throw new Error("Model download failed");return fe(a.body,P(a),n)})(f,o,a)),o?.aborted)throw new Error("Cancelled");a(1,s.load);const y=await d.Engine.create((({model:e,limits:t,source:n,mod:a})=>{const o=u(e);return{model:n,mainExecutorSettings:{maxNumTokens:4096,...o?.vision?{maxNumImages:Math.max(1,t.maxImages)}:{},backendConfig:ce},...o?.vision?{visionBackend:a.Backend.GPU,maxVisionTokensPerImage:70,minVisionTokensPerImage:70}:{},...o?.audio?{audioBackend:a.Backend.GPU}:{}}})({model:e,limits:t,source:h,mod:d}));if(o?.aborted)throw await y.delete(),new Error("Cancelled");const b={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})=>{le=!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");const i=u(t);if($(r)&&!i?.vision)throw new Error("Model does not accept images");if(j(r)&&!i?.audio)throw new Error("Model does not accept audio");return we({engine:e,mod:n,params:a,messages:r,session:o})}})}catch(e){throw r(),e}})({engine:y,model:e,mod:d,params:t,session:b}),unload:async()=>{b.conversation=void 0,b.sent=[],await y.delete()}}},cached:async e=>{const t=e.litert,n=u(e);if(!t||!n)return!1;const a=L(t.repo,n.file),[o,r]=await Promise.all([ge(),se.stored(a)]);if(r?.complete)return!0;try{return!(!o||!await o.match(a))}catch{return!1}},hasCache:async()=>{try{for(const e of l){const t=e.litert,n=u(e);if(!t||!n)continue;if(await se.stored(L(t.repo,n.file)))return!0}const e=await ge();return!!e&&(await e.keys()).length>0}catch{return!1}},remove:async e=>{try{const t=e.litert,n=u(e);if(!t||!n)return;const a=L(t.repo,n.file),o=await ge();await se.remove(a),o&&await o.delete(a)}catch{}},purge:async()=>{try{await se.purge(),"undefined"!=typeof caches&&await caches.delete(ie)}catch{}}},be=re("wllama-store");let ve,xe,ke="",_e=!1;const Me=e=>`${e.wasm}|${e.compatWasm}|${e.compatWorker}`,Te=e=>{const t={wasm:w,compatWasm:y,compatWorker:b};return{wasm:e?.wasm||t.wasm,compatWasm:e?.compatWasm||t.compatWasm,compatWorker:e?.compatWorker||t.compatWorker}},Ee=e=>{const t=e=>(...t)=>{_e&&("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)}},Ce=async()=>{if(ve)return ve;const e=await n.e(924).then(()=>n(577)),t=xe||Te(),a=new e.Wllama({default:t.wasm},{logger:Ee(e.LoggerWithoutDebug)});return xe||(xe=t,ke=Me(t)),a.setCompat({wasm:t.compatWasm,worker:t.compatWorker}),ve=a,a},Le=async()=>{const e=ve;ve=void 0;try{await(e?.exit())}catch{}},Pe=(e,t)=>e.name.includes(t)||!!e.metadata?.originalURL?.includes(t),Re=e=>{const t=e.cpu;return t?[{name:t.file,url:L(t.repo,t.file)},...t.mmproj?[{name:t.mmproj.file,url:L(t.repo,t.mmproj.file)}]:[]]:[]},$e=async e=>{const t=Re(e);if(!t.length)return!1;for(const e of t){const t=await be.stored(e.url);if(!t?.complete)return!1}return!0},je=async(e,t)=>{const n=await e.cacheManager.list();return Re(t).every(e=>n.some(t=>Pe(t,e.name)))},Se=async({model:e,signal:t,onProgress:n})=>{const a=Re(e),o=[];for(let e=0;e<a.length;e++){if(t?.aborted)throw new Error("Cancelled");const r=await be.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},Ae=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 k(t.url)});t.push({role:"user",content:e})}return t},Be={backend:i.cpu,startLLM:async({model:e,limits:t,onProgress:n,signal:a,logging:o,assets:r})=>{_e=!0===o;const c=d()?4096:8192,l=e.cpu;if(!l)throw new Error("Model has no CPU artifact");const u=Te(r),g=Me(u);ve&&g!=ke&&await Le(),xe=u,ke=g;const m=(f="undefined"!=typeof navigator&&navigator.hardwareConcurrency||4,p=t.maxThreads,Math.max(1,Math.min(Math.max(2,Math.min(8,(f||4)-1)),p)));var f,p;const h=await Ce(),w=await $e(e),y=l.mmproj?.file,b=L(l.repo,l.file),v=y?L(l.repo,y):"";if(a?.aborted)throw await Le(),new Error("Cancelled");if(!w&&y){const e=await h.cacheManager.list(),t=e.some(e=>Pe(e,l.file)),n=e.some(e=>Pe(e,y));t&&!n&&await h.cacheManager.deleteMany(e=>Pe(e,l.file))}const x={n_ctx:c,n_threads:m,reasoning_budget_tokens:t.maxThinkingTokens,n_gpu_layers:0,jinja:!0};try{let t=w?await Se({model:e,signal:a,onProgress:n}):void 0;t||w||await je(h,e).catch(()=>!1)||(t=await Se({model:e,signal:a,onProgress:n}).catch(e=>{if(a?.aborted)throw new Error("Cancelled");if(!oe(e))throw e;o&&console.log("Local model store failed",e)})),t?(n(1,s.load),await h.loadModel(t,x)):await h.loadModelFromUrl(v?{url:b,mmprojUrl:v}:b,{...x,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 Le(),e}if(a?.aborted)throw await Le(),new Error("Cancelled");if(!h.getLoadedContextInfo().n_ctx)throw await Le(),new Error("Failed to create model context");return{model:e,backend:i.cpu,contextSize:c,chat:t=>(async(e,t,n)=>{if(_e=!0===n.logging,e.isModelLoaded())return await N({model:t,params:n,stopped:()=>!!n.signal?.aborted,start:async a=>{if($(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 Ae(a),...C(n.limits,n.reasoning),abortSignal:n.signal,...t.reasoning?{chat_template_kwargs:{enable_thinking:n.reasoning}}:{}})}})})(h,e,t),unload:async()=>{if(ve==h)await Le();else try{await h.exit()}catch{}}}},cached:async e=>{try{return!!e.cpu&&(!!await $e(e)||await je(await Ce(),e))}catch{return!1}},hasCache:async()=>{try{for(const e of l.flatMap(e=>Re(e)))if(await be.stored(e.url))return!0;const e=await Ce();return(await e.cacheManager.list()).length>0}catch{return!1}},remove:async e=>{const t=Re(e);try{for(const e of t)await be.remove(e.url)}catch{}try{const e=await Ce();await e.cacheManager.deleteMany(e=>t.some(t=>Pe(e,t.name)))}catch{}},purge:async()=>{try{await be.purge()}catch{}try{const e=await Ce();await e.cacheManager.clear()}catch{}}},We=((e={})=>{const t=e.engines?.cpu||Be,n=e.engines?.litert||ye;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}})(),Ie=(e,t,n)=>{const a=e.status(),i=g(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&&p(i),supportsAudio:!!i&&h(i),ready:e.ready(),loading:e.loading(),requesting:n.requesting,phase:n.phase,percent:n.percent,...n.error?{error:n.error}:{}}},qe=(e=>{let t,n=0;const o=t=>t.input.onStatus?.(Ie(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 f(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)),g=l||d||u;if(!c)return t.error="Unknown model",void o(t);if(!g)return void o(t);const m=p(c),w=h(c);if(!(l||d&&m||u&&w))return u&&!w?t.error="Model does not accept audio":d&&!m&&(t.error="Model does not accept images"),void o(t);const y=E(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&&!m||u&&!w?r.messages.map(({images:e,audio:t,...n})=>({...n,...e&&m?{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)}})(We),ze=(Oe=We,async(e={})=>{const t=await f(e.model),n=n=>e.onStatus?.(Ie(Oe,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=Oe.ready()&&Oe.model()?.id==t.id;return await Oe.load({model:t,limits:E(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 Oe;export{c as ChatModelId,o as ChatStatus,i as LlmBackend,s as LlmPhase,a as LlmRole,r as LlmStatus,qe as createChat,ze 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
@@ -1,10 +1,16 @@
1
1
  import { ChatModelId } from './enums';
2
- import type { ChatModel } from './types';
2
+ import type { ChatModel, LitertArtifact, MobileNavigator } from './types';
3
3
  declare const llmModels: ChatModel[],
4
4
  /** Context window requested from the CPU engine, a 32k KV cache needs 3.5 GB */
5
5
  cpuContextWindow = 8192,
6
+ /** CPU window on a phone, the smaller cache keeps the tab alive */
7
+ mobileCpuContextWindow = 4096,
6
8
  /** Context window requested from the GPU engine, the wasm heap holds the model */
7
9
  litertContextWindow = 4096,
10
+ /** Whether this client is a phone or tablet */
11
+ mobileDevice: (nav?: MobileNavigator) => boolean,
12
+ /** LiteRT file and capabilities of this device */
13
+ litertArtifact: (model: ChatModel, mobile?: boolean) => LitertArtifact | undefined,
8
14
  /** Look up a catalog entry by id */
9
15
  findLlmModel: (id?: string) => ChatModel | undefined,
10
16
  /** Whether this browser provides a WebGPU adapter */
@@ -14,5 +20,7 @@ defaultLlmModel: () => Promise<ChatModelId | undefined>,
14
20
  /** Resolve the requested model or the default of this device */
15
21
  resolveLlmModel: (id?: ChatModelId) => Promise<ChatModel | undefined>,
16
22
  /** Whether a model accepts images */
17
- supportsImages: (model: ChatModel) => boolean;
18
- export { llmModels, cpuContextWindow, litertContextWindow, findLlmModel, gpuAdapter, defaultLlmModel, resolveLlmModel, supportsImages, };
23
+ supportsImages: (model: ChatModel, mobile?: boolean) => boolean,
24
+ /** Whether a model accepts audio */
25
+ supportsAudio: (model: ChatModel, mobile?: boolean) => boolean;
26
+ export { llmModels, cpuContextWindow, mobileCpuContextWindow, litertContextWindow, mobileDevice, litertArtifact, 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:()=>Ie,preloadModel:()=>ze}),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,web:{file:"gemma-4-E2B-it-gpu.litertlm",vision:!1,audio:!1}}}],u=e=>{const t=e??("undefined"==typeof navigator?void 0:navigator);if(!t)return!1;if("boolean"==typeof t.userAgentData?.mobile)return t.userAgentData.mobile;const n=t.userAgent||"";return/Android|iPhone|iPad|iPod|Mobile/i.test(n)||/Macintosh/.test(n)&&t.maxTouchPoints>1},g=(e,t=u())=>{const n=e.litert;if(n)return t&&n.web?n.web:{file:n.file,vision:n.vision,audio:n.audio}},m=e=>e?d.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}})()?d.find(e=>!!e.litert):d.find(e=>!!e.cpu))?.id,p=async e=>m(e||await f()),h=(e,t=u())=>!!e.cpu?.mmproj||!0===g(e,t)?.vision,w=(e,t=u())=>!!e.cpu?.mmproj?.audio||!0===g(e,t)?.audio,y=require("node:path"),b=()=>{return e=(0,y.join)(__dirname,"../assets/wllama"),{wasm:(0,y.join)(e,"wllama.wasm"),compatWasm:(0,y.join)(e,"compat/wllama.wasm"),compatWorker:(0,y.join)(e,"compat/wllama.js")};var e},v=()=>{return e=(0,y.join)(__dirname,"../assets/litert"),{script:(0,y.join)(e,"litertlm_wasm_internal.js"),wasm:(0,y.join)(e,"litertlm_wasm_internal.wasm")};var e},x=288,k=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},M=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})(x,x),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,x,x),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},T=(e,t,n)=>"number"==typeof e&&Number.isFinite(e)&&e>=n?Math.floor(e):t,E=(e={},t=!1)=>{const n=_;return{maxReplyTokens:T(e.maxReplyTokens,n.maxReplyTokens,1),maxThinkingTokens:T(e.maxThinkingTokens,n.maxThinkingTokens,1),maxReasoningTokens:T(e.maxReasoningTokens,t?1200:n.maxReasoningTokens,1),maxImages:T(e.maxImages,n.maxImages,0),maxAudio:T(e.maxAudio,n.maxAudio,0),maxContentLength:T(e.maxContentLength,n.maxContentLength,1),promptCharBudget:T(e.promptCharBudget,n.promptCharBudget,1),maxThreads:T(e.maxThreads,n.maxThreads,1)}},C=(e,t)=>({stream:!0,temperature:.7,max_tokens:t?e.maxReasoningTokens:e.maxReplyTokens}),L=(e,t)=>`https://huggingface.co/${e}/resolve/main/${t}`,P=e=>Number(e.headers.get("content-length"))||0,j=(e,t)=>e.some(e=>Array.isArray(e.content)&&e.content.some(e=>e.type==t)),S=e=>j(e,"image"),$=e=>j(e,"audio"),A=["<|channel>thought\n","<|channel>thought","..."],R=["<channel|>","..."],q=(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,B=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[...B(e),{type:"text",text:"\n"},...B(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 z=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)}},O=e=>{try{Promise.resolve(e.return?.()).catch(()=>{})}catch{}},N=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,A);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,A);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 z(r(),s,"Model did not start"))[Symbol.asyncIterator]();for(;;){if(i()){O(e);break}let a;try{a=await z(e.next(),m?c:s,m?"Model stopped responding":"Model did not reply")}catch(n){if(n instanceof I&&(d||u)){l?.(),O(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},F=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 M(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!=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:q(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 N({model:e,logging:t.logging,onReason:t.onReason,onText:t.onText,onSpeed:t.onSpeed,start:()=>n(i),stopped:a,onStall:r})},U=33554432,D=new Map;let H=!1;const Q=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}`},G=e=>{const t=/^part-(\d+)$/.exec(e);return t?Number(t[1]):-1},V=async()=>{if(!H)if("undefined"!=typeof navigator&&navigator.storage?.getDirectory)try{return await navigator.storage.getDirectory()}catch{return}else H=!0},J=async(e,t)=>{try{return await e.getDirectoryHandle(t)}catch{return}},K=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}},X=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{}},Y=async function*(e){for await(const t of e)yield t},Z=async e=>{try{for await(const[t,n]of Y(e))"file"==n.kind&&G(t)>=0&&await e.removeEntry(t).catch(()=>{})}catch{}},ee=async(e,t)=>{try{const n=await(await e.getFileHandle(t)).getFile(),a=await K(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}},te=async(e,t)=>{const n=await J(e,t);if(n){const e=await(async e=>await K(await e.getFileHandle("meta.json").catch(()=>{})))(n),t=await(async e=>{const t=[];try{for await(const[n,a]of Y(e)){const e=G(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 ee(e,t)},ne=(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:P(e)||t.total,etag:r||t.etag}},ae=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(U-d,o.byteLength-f);await s.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,c.download)}await m()}catch(e){throw await m(),e}return l},oe=e=>["NotAllowedError","NotSupportedError"].includes(e?.name||""),re=e=>{const t=async()=>{const t=await V();if(t)try{return await t.getDirectoryHandle(e,{create:!0})}catch{return}};return{stored:async e=>{const n=await t();return n?await te(n,Q(e)):void 0},download:async({url:n,signal:a,onProgress:o})=>{const r=`${e}|${n}`,i=D.get(r);if(i)return await i;const s=await t();if(!s)return;const l=(async(e,t,n,a)=>{const o=Q(t),r=await ee(e,o);if(r?.complete)return r;let i=await J(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 te(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+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 X(i,{total:u,etag:g,complete:!0});continue}await Z(i),await X(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=ne(w,h);if(!n){await w.body.cancel().catch(()=>{}),await Z(i),await X(i,{total:0,etag:"",complete:!1});continue}await X(i,{total:n.total,etag:n.etag,complete:!1});const r=0==n.start?d:0,s=await ae({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 X(i,{total:s,etag:n.etag,complete:!0});const t=await te(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(()=>{D.delete(r)});return D.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,Q(e))},purge:async()=>{const t=await V();t&&await t.removeEntry(e,{recursive:!0}).catch(()=>{})}}},ie="litertlm-models",se=re("litert-store"),ce={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 le=!1;const de=(...e)=>{le&&console.log(...e)},ue=(...e)=>{le&&console.error(...e)},ge=async()=>{try{if("undefined"==typeof caches)return;return await caches.open(ie)}catch{return}},me=new Map,fe=(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)}}))},pe=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 k(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},he=(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},we=async function*({engine:e,mod:t,params:n,messages:a,session:o}){const r=a[a.length-1],i=C(n.limits,n.reasoning);if("user"!=r?.role)throw new Error("No user turn");const s=a.slice(0,-1),c=(await pe([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&&!he(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(!he(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 pe(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=[])}},ye={backend:s.gpu,startLLM:async({model:e,limits:t,onProgress:a,signal:o,logging:r})=>{le=!0===r;const i=e.litert,l=g(e);if(!i||!l)throw new Error("Model has no LiteRT artifact");const d=await n.e(520).then(()=>n(507)),u=globalThis,m=u.Module;u.Module={print:de,printErr:ue};try{await d.getOrLoadGlobalLiteRtLm(v().script)}finally{u.Module=m}if(o?.aborted)throw new Error("Cancelled");const f=L(i.repo,l.file),p=await ge();let h;const w=p?await p.match(f):void 0;if(w?.body)h=w.body;else{const e=await se.stored(f);e?.complete&&(h=e.file.stream())}if(!h){const e=await se.download({url:f,signal:o,onProgress:a}).catch(e=>{if(o?.aborted)throw new Error("Cancelled");if(!oe(e))throw e;r&&console.log("Local model store failed",e)});e&&(h=e.file.stream())}if(!h&&p)try{await(async(e,t,n,a)=>{const o=me.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=P(o),i=fe(o.body,r,a);await t.put(e,new Response(i,{headers:{"content-type":"application/octet-stream",...r?{"content-length":`${r}`}:{}}}))})().finally(()=>{me.delete(e)});return me.set(e,r),await r})(f,p,o,a);const e=await p.match(f);e?.body&&(h=e.body)}catch(e){if(o?.aborted)throw new Error("Cancelled");r&&console.log("Local model cache failed",e)}if(h||(h=await(async(e,t,n)=>{const a=await fetch(e,{signal:t});if(!a.ok||!a.body)throw new Error("Model download failed");return fe(a.body,P(a),n)})(f,o,a)),o?.aborted)throw new Error("Cancelled");a(1,c.load);const y=await d.Engine.create((({model:e,limits:t,source:n,mod:a})=>{const o=g(e);return{model:n,mainExecutorSettings:{maxNumTokens:4096,...o?.vision?{maxNumImages:Math.max(1,t.maxImages)}:{},backendConfig:ce},...o?.vision?{visionBackend:a.Backend.GPU,maxVisionTokensPerImage:70,minVisionTokensPerImage:70}:{},...o?.audio?{audioBackend:a.Backend.GPU}:{}}})({model:e,limits:t,source:h,mod:d}));if(o?.aborted)throw await y.delete(),new Error("Cancelled");const b={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})=>{le=!0===a.logging;const r=()=>{try{o.conversation?.cancel()}catch{}o.conversation=void 0,o.sent=[]};try{return await F({model:t,params:a,stopped:()=>!!a.signal?.aborted,onStall:r,start:async r=>{if(a.signal?.aborted)throw new Error("Cancelled");const i=g(t);if(S(r)&&!i?.vision)throw new Error("Model does not accept images");if($(r)&&!i?.audio)throw new Error("Model does not accept audio");return we({engine:e,mod:n,params:a,messages:r,session:o})}})}catch(e){throw r(),e}})({engine:y,model:e,mod:d,params:t,session:b}),unload:async()=>{b.conversation=void 0,b.sent=[],await y.delete()}}},cached:async e=>{const t=e.litert,n=g(e);if(!t||!n)return!1;const a=L(t.repo,n.file),[o,r]=await Promise.all([ge(),se.stored(a)]);if(r?.complete)return!0;try{return!(!o||!await o.match(a))}catch{return!1}},hasCache:async()=>{try{for(const e of d){const t=e.litert,n=g(e);if(!t||!n)continue;if(await se.stored(L(t.repo,n.file)))return!0}const e=await ge();return!!e&&(await e.keys()).length>0}catch{return!1}},remove:async e=>{try{const t=e.litert,n=g(e);if(!t||!n)return;const a=L(t.repo,n.file),o=await ge();await se.remove(a),o&&await o.delete(a)}catch{}},purge:async()=>{try{await se.purge(),"undefined"!=typeof caches&&await caches.delete(ie)}catch{}}},be=re("wllama-store");let ve,xe,ke="",Me=!1;const _e=e=>`${e.wasm}|${e.compatWasm}|${e.compatWorker}`,Te=e=>{const t=b();return{wasm:e?.wasm||t.wasm,compatWasm:e?.compatWasm||t.compatWasm,compatWorker:e?.compatWorker||t.compatWorker}},Ee=e=>{const t=e=>(...t)=>{Me&&("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)}},Ce=async()=>{if(ve)return ve;const e=await n.e(924).then(()=>n(577)),t=xe||Te(),a=new e.Wllama({default:t.wasm},{logger:Ee(e.LoggerWithoutDebug)});return xe||(xe=t,ke=_e(t)),a.setCompat({wasm:t.compatWasm,worker:t.compatWorker}),ve=a,a},Le=async()=>{const e=ve;ve=void 0;try{await(e?.exit())}catch{}},Pe=(e,t)=>e.name.includes(t)||!!e.metadata?.originalURL?.includes(t),je=e=>{const t=e.cpu;return t?[{name:t.file,url:L(t.repo,t.file)},...t.mmproj?[{name:t.mmproj.file,url:L(t.repo,t.mmproj.file)}]:[]]:[]},Se=async e=>{const t=je(e);if(!t.length)return!1;for(const e of t){const t=await be.stored(e.url);if(!t?.complete)return!1}return!0},$e=async(e,t)=>{const n=await e.cacheManager.list();return je(t).every(e=>n.some(t=>Pe(t,e.name)))},Ae=async({model:e,signal:t,onProgress:n})=>{const a=je(e),o=[];for(let e=0;e<a.length;e++){if(t?.aborted)throw new Error("Cancelled");const r=await be.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 k(t.url)});t.push({role:"user",content:e})}return t},qe={backend:s.cpu,startLLM:async({model:e,limits:t,onProgress:n,signal:a,logging:o,assets:r})=>{Me=!0===o;const i=u()?4096:8192,l=e.cpu;if(!l)throw new Error("Model has no CPU artifact");const d=Te(r),g=_e(d);ve&&g!=ke&&await Le(),xe=d,ke=g;const m=(f="undefined"!=typeof navigator&&navigator.hardwareConcurrency||4,p=t.maxThreads,Math.max(1,Math.min(Math.max(2,Math.min(8,(f||4)-1)),p)));var f,p;const h=await Ce(),w=await Se(e),y=l.mmproj?.file,b=L(l.repo,l.file),v=y?L(l.repo,y):"";if(a?.aborted)throw await Le(),new Error("Cancelled");if(!w&&y){const e=await h.cacheManager.list(),t=e.some(e=>Pe(e,l.file)),n=e.some(e=>Pe(e,y));t&&!n&&await h.cacheManager.deleteMany(e=>Pe(e,l.file))}const x={n_ctx:i,n_threads:m,reasoning_budget_tokens:t.maxThinkingTokens,n_gpu_layers:0,jinja:!0};try{let t=w?await Ae({model:e,signal:a,onProgress:n}):void 0;t||w||await $e(h,e).catch(()=>!1)||(t=await Ae({model:e,signal:a,onProgress:n}).catch(e=>{if(a?.aborted)throw new Error("Cancelled");if(!oe(e))throw e;o&&console.log("Local model store failed",e)})),t?(n(1,c.load),await h.loadModel(t,x)):await h.loadModelFromUrl(v?{url:b,mmprojUrl:v}:b,{...x,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 Le(),e}if(a?.aborted)throw await Le(),new Error("Cancelled");if(!h.getLoadedContextInfo().n_ctx)throw await Le(),new Error("Failed to create model context");return{model:e,backend:s.cpu,contextSize:i,chat:t=>(async(e,t,n)=>{if(Me=!0===n.logging,e.isModelLoaded())return await F({model:t,params:n,stopped:()=>!!n.signal?.aborted,start:async a=>{if(S(a)&&!e.supportInputModality("image"))throw new Error("Model does not accept images");if($(a)&&!e.supportInputModality("audio"))throw new Error("Model does not accept audio");return await e.createChatCompletion({messages:await Re(a),...C(n.limits,n.reasoning),abortSignal:n.signal,...t.reasoning?{chat_template_kwargs:{enable_thinking:n.reasoning}}:{}})}})})(h,e,t),unload:async()=>{if(ve==h)await Le();else try{await h.exit()}catch{}}}},cached:async e=>{try{return!!e.cpu&&(!!await Se(e)||await $e(await Ce(),e))}catch{return!1}},hasCache:async()=>{try{for(const e of d.flatMap(e=>je(e)))if(await be.stored(e.url))return!0;const e=await Ce();return(await e.cacheManager.list()).length>0}catch{return!1}},remove:async e=>{const t=je(e);try{for(const e of t)await be.remove(e.url)}catch{}try{const e=await Ce();await e.cacheManager.deleteMany(e=>t.some(t=>Pe(e,t.name)))}catch{}},purge:async()=>{try{await be.purge()}catch{}try{const e=await Ce();await e.cacheManager.clear()}catch{}}},Be=((e={})=>{const t=e.engines?.cpu||qe,n=e.engines?.litert||ye;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}})(),We=(e,t,n)=>{const a=e.status(),o=m(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&&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?.(We(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 p(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 m=h(s),f=w(s);if(!(l||d&&m||u&&f))return u&&!f?t.error="Model does not accept audio":d&&!m&&(t.error="Model does not accept images"),void a(t);const y=E(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&&!m||u&&!f?r.messages.map(({images:e,audio:t,...n})=>({...n,...e&&m?{images:e}:{},...t&&f?{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)}})(Be),ze=(Oe=Be,async(e={})=>{const t=await p(e.model),n=n=>e.onStatus?.(We(Oe,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=Oe.ready()&&Oe.model()?.id==t.id;return await Oe.load({model:t,limits:E(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 Oe;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,web:{file:"gemma-4-E2B-it-gpu.litertlm",vision:!1,audio:!1}}}],m=e=>{const t=e??("undefined"==typeof navigator?void 0:navigator);if(!t)return!1;if("boolean"==typeof t.userAgentData?.mobile)return t.userAgentData.mobile;const n=t.userAgent||"";return/Android|iPhone|iPad|iPod|Mobile/i.test(n)||/Macintosh/.test(n)&&t.maxTouchPoints>1},f=(e,t=m())=>{const n=e.litert;if(n)return t&&n.web?n.web:{file:n.file,vision:n.vision,audio:n.audio}},p=e=>e?g.find(t=>t.id==e):void 0,h=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,w=async e=>p(e||await h()),y=(e,t=m())=>!!e.cpu?.mmproj||!0===f(e,t)?.vision,b=(e,t=m())=>!!e.cpu?.mmproj?.audio||!0===f(e,t)?.audio,v=()=>{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},x=()=>{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},k=288,_=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},M=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})(k,k),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,k,k),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}},T={maxReplyTokens:250,maxThinkingTokens:128,maxReasoningTokens:320,maxImages:2,maxAudio:1,maxContentLength:1e3,promptCharBudget:1e4,maxThreads:6},E=(e,t,n)=>"number"==typeof e&&Number.isFinite(e)&&e>=n?Math.floor(e):t,C=(e={},t=!1)=>{const n=T;return{maxReplyTokens:E(e.maxReplyTokens,n.maxReplyTokens,1),maxThinkingTokens:E(e.maxThinkingTokens,n.maxThinkingTokens,1),maxReasoningTokens:E(e.maxReasoningTokens,t?1200:n.maxReasoningTokens,1),maxImages:E(e.maxImages,n.maxImages,0),maxAudio:E(e.maxAudio,n.maxAudio,0),maxContentLength:E(e.maxContentLength,n.maxContentLength,1),promptCharBudget:E(e.promptCharBudget,n.promptCharBudget,1),maxThreads:E(e.maxThreads,n.maxThreads,1)}},L=(e,t)=>({stream:!0,temperature:.7,max_tokens:t?e.maxReasoningTokens:e.maxReplyTokens}),P=(e,t)=>`https://huggingface.co/${e}/resolve/main/${t}`,j=e=>Number(e.headers.get("content-length"))||0,S=(e,t)=>e.some(e=>Array.isArray(e.content)&&e.content.some(e=>e.type==t)),$=e=>S(e,"image"),A=e=>S(e,"audio"),R=["<|channel>thought\n","<|channel>thought","..."],B=["<channel|>","..."],W=(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,I=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[...I(e),{type:"text",text:"\n"},...I(t)]){const e=n[n.length-1];"text"==a.type&&"text"==e?.type?e.text+=a.text:n.push(a)}return n};class z extends Error{}const O=async(e,t,n)=>{let a;try{return await Promise.race([e,new Promise((e,o)=>{a=setTimeout(()=>o(new z(n)),t)})])}finally{a&&clearTimeout(a)}},N=e=>{try{Promise.resolve(e.return?.()).catch(()=>{})}catch{}},F=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,B);if(t>=0){t&&e(s.slice(0,t)),s=s.slice(t+i.length),a=!1;continue}const c=r(s,B);e(s.slice(0,s.length-c.length)),n=c;break}{const{at:e,tag:i}=o(s,R);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,R);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 O(r(),s,"Model did not start"))[Symbol.asyncIterator]();for(;;){if(i()){N(e);break}let a;try{a=await O(e.next(),m?c:s,m?"Model stopped responding":"Model did not reply")}catch(n){if(n instanceof z&&(d||u)){l?.(),N(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 z&&!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},D=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 M(t.url)}:t);t.push({role:"user",content:e})}return t})(((e,t=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:W(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=q(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 F({model:e,logging:t.logging,onReason:t.onReason,onText:t.onText,onSpeed:t.onSpeed,start:()=>n(r),stopped:a,onStall:o})},U=33554432,H=new Map;let Q=!1;const G=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}`},V=e=>{const t=/^part-(\d+)$/.exec(e);return t?Number(t[1]):-1},J=async()=>{if(!Q)if("undefined"!=typeof navigator&&navigator.storage?.getDirectory)try{return await navigator.storage.getDirectory()}catch{return}else Q=!0},K=async(e,t)=>{try{return await e.getDirectoryHandle(t)}catch{return}},X=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}},Y=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{}},Z=async function*(e){for await(const t of e)yield t},ee=async e=>{try{for await(const[t,n]of Z(e))"file"==n.kind&&V(t)>=0&&await e.removeEntry(t).catch(()=>{})}catch{}},te=async(e,t)=>{try{const n=await(await e.getFileHandle(t)).getFile(),a=await X(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}},ne=async(e,t)=>{const n=await K(e,t);if(n){const e=await(async e=>await X(await e.getFileHandle("meta.json").catch(()=>{})))(n),t=await(async e=>{const t=[];try{for await(const[n,a]of Z(e)){const e=V(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 te(e,t)},ae=(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:j(e)||t.total,etag:r||t.etag}},oe=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(U-l,o.byteLength-f);await s.write({type:"write",position:l,data:o.subarray(f,f+e)}),f+=e,l+=e,c+=e,l==U&&await m()}c>t&&r(a?c/a:0,d.download)}await m()}catch(e){throw await m(),e}return c},re=e=>["NotAllowedError","NotSupportedError"].includes(e?.name||""),ie=e=>{const t=async()=>{const t=await J();if(t)try{return await t.getDirectoryHandle(e,{create:!0})}catch{return}};return{stored:async e=>{const n=await t();return n?await ne(n,G(e)):void 0},download:async({url:n,signal:a,onProgress:o})=>{const r=`${e}|${n}`,i=H.get(r);if(i)return await i;const s=await t();if(!s)return;const c=(async(e,t,n,a)=>{const o=G(t),r=await te(e,o);if(r?.complete)return r;let i=await K(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 ne(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+U,u)-1:l+U-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 Y(i,{total:u,etag:g,complete:!0});continue}await ee(i),await Y(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=ae(w,h);if(!n){await w.body.cancel().catch(()=>{}),await ee(i),await Y(i,{total:0,etag:"",complete:!1});continue}await Y(i,{total:n.total,etag:n.etag,complete:!1});const r=0==n.start?l:0,s=await oe({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 Y(i,{total:s,etag:n.etag,complete:!0});const t=await ne(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(()=>{H.delete(r)});return H.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,G(e))},purge:async()=>{const t=await J();t&&await t.removeEntry(e,{recursive:!0}).catch(()=>{})}}},se="litertlm-models",ce=ie("litert-store"),le={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 de=!1;const ue=(...e)=>{de&&console.log(...e)},ge=(...e)=>{de&&console.error(...e)},me=async()=>{try{if("undefined"==typeof caches)return;return await caches.open(se)}catch{return}},fe=new Map,pe=(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)}}))},he=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 _(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},we=(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},ye=async function*({engine:e,mod:t,params:n,messages:a,session:o}){const r=a[a.length-1],i=L(n.limits,n.reasoning);if("user"!=r?.role)throw new Error("No user turn");const s=a.slice(0,-1),c=(await he([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&&!we(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(!we(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 he(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=[])}},be={backend:l.gpu,startLLM:async({model:e,limits:t,onProgress:n,signal:a,logging:o})=>{de=!0===o;const i=e.litert,s=f(e);if(!i||!s)throw new Error("Model has no LiteRT artifact");const c=await r.e(520).then(()=>r(507)),u=globalThis,g=u.Module;u.Module={print:ue,printErr:ge};try{await c.getOrLoadGlobalLiteRtLm(x().script)}finally{u.Module=g}if(a?.aborted)throw new Error("Cancelled");const m=P(i.repo,s.file),p=await me();let h;const w=p?await p.match(m):void 0;if(w?.body)h=w.body;else{const e=await ce.stored(m);e?.complete&&(h=e.file.stream())}if(!h){const e=await ce.download({url:m,signal:a,onProgress:n}).catch(e=>{if(a?.aborted)throw new Error("Cancelled");if(!re(e))throw e;o&&console.log("Local model store failed",e)});e&&(h=e.file.stream())}if(!h&&p)try{await(async(e,t,n,a)=>{const o=fe.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=j(o),i=pe(o.body,r,a);await t.put(e,new Response(i,{headers:{"content-type":"application/octet-stream",...r?{"content-length":`${r}`}:{}}}))})().finally(()=>{fe.delete(e)});return fe.set(e,r),await r})(m,p,a,n);const e=await p.match(m);e?.body&&(h=e.body)}catch(e){if(a?.aborted)throw new Error("Cancelled");o&&console.log("Local model cache failed",e)}if(h||(h=await(async(e,t,n)=>{const a=await fetch(e,{signal:t});if(!a.ok||!a.body)throw new Error("Model download failed");return pe(a.body,j(a),n)})(m,a,n)),a?.aborted)throw new Error("Cancelled");n(1,d.load);const y=await c.Engine.create((({model:e,limits:t,source:n,mod:a})=>{const o=f(e);return{model:n,mainExecutorSettings:{maxNumTokens:4096,...o?.vision?{maxNumImages:Math.max(1,t.maxImages)}:{},backendConfig:le},...o?.vision?{visionBackend:a.Backend.GPU,maxVisionTokensPerImage:70,minVisionTokensPerImage:70}:{},...o?.audio?{audioBackend:a.Backend.GPU}:{}}})({model:e,limits:t,source:h,mod:c}));if(a?.aborted)throw await y.delete(),new Error("Cancelled");const b={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})=>{de=!0===a.logging;const r=()=>{try{o.conversation?.cancel()}catch{}o.conversation=void 0,o.sent=[]};try{return await D({model:t,params:a,stopped:()=>!!a.signal?.aborted,onStall:r,start:async r=>{if(a.signal?.aborted)throw new Error("Cancelled");const i=f(t);if($(r)&&!i?.vision)throw new Error("Model does not accept images");if(A(r)&&!i?.audio)throw new Error("Model does not accept audio");return ye({engine:e,mod:n,params:a,messages:r,session:o})}})}catch(e){throw r(),e}})({engine:y,model:e,mod:c,params:t,session:b}),unload:async()=>{b.conversation=void 0,b.sent=[],await y.delete()}}},cached:async e=>{const t=e.litert,n=f(e);if(!t||!n)return!1;const a=P(t.repo,n.file),[o,r]=await Promise.all([me(),ce.stored(a)]);if(r?.complete)return!0;try{return!(!o||!await o.match(a))}catch{return!1}},hasCache:async()=>{try{for(const e of g){const t=e.litert,n=f(e);if(!t||!n)continue;if(await ce.stored(P(t.repo,n.file)))return!0}const e=await me();return!!e&&(await e.keys()).length>0}catch{return!1}},remove:async e=>{try{const t=e.litert,n=f(e);if(!t||!n)return;const a=P(t.repo,n.file),o=await me();await ce.remove(a),o&&await o.delete(a)}catch{}},purge:async()=>{try{await ce.purge(),"undefined"!=typeof caches&&await caches.delete(se)}catch{}}},ve=ie("wllama-store");let xe,ke,_e="",Me=!1;const Te=e=>`${e.wasm}|${e.compatWasm}|${e.compatWorker}`,Ee=e=>{const t=v();return{wasm:e?.wasm||t.wasm,compatWasm:e?.compatWasm||t.compatWasm,compatWorker:e?.compatWorker||t.compatWorker}},Ce=e=>{const t=e=>(...t)=>{Me&&("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)}},Le=async()=>{if(xe)return xe;const e=await r.e(924).then(()=>r(577)),t=ke||Ee(),n=new e.Wllama({default:t.wasm},{logger:Ce(e.LoggerWithoutDebug)});return ke||(ke=t,_e=Te(t)),n.setCompat({wasm:t.compatWasm,worker:t.compatWorker}),xe=n,n},Pe=async()=>{const e=xe;xe=void 0;try{await(e?.exit())}catch{}},je=(e,t)=>e.name.includes(t)||!!e.metadata?.originalURL?.includes(t),Se=e=>{const t=e.cpu;return t?[{name:t.file,url:P(t.repo,t.file)},...t.mmproj?[{name:t.mmproj.file,url:P(t.repo,t.mmproj.file)}]:[]]:[]},$e=async e=>{const t=Se(e);if(!t.length)return!1;for(const e of t){const t=await ve.stored(e.url);if(!t?.complete)return!1}return!0},Ae=async(e,t)=>{const n=await e.cacheManager.list();return Se(t).every(e=>n.some(t=>je(t,e.name)))},Re=async({model:e,signal:t,onProgress:n})=>{const a=Se(e),o=[];for(let e=0;e<a.length;e++){if(t?.aborted)throw new Error("Cancelled");const r=await ve.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},Be=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 _(t.url)});t.push({role:"user",content:e})}return t},We={backend:l.cpu,startLLM:async({model:e,limits:t,onProgress:n,signal:a,logging:o,assets:r})=>{Me=!0===o;const i=m()?4096:8192,s=e.cpu;if(!s)throw new Error("Model has no CPU artifact");const c=Ee(r),u=Te(c);xe&&u!=_e&&await Pe(),ke=c,_e=u;const g=(f="undefined"!=typeof navigator&&navigator.hardwareConcurrency||4,p=t.maxThreads,Math.max(1,Math.min(Math.max(2,Math.min(8,(f||4)-1)),p)));var f,p;const h=await Le(),w=await $e(e),y=s.mmproj?.file,b=P(s.repo,s.file),v=y?P(s.repo,y):"";if(a?.aborted)throw await Pe(),new Error("Cancelled");if(!w&&y){const e=await h.cacheManager.list(),t=e.some(e=>je(e,s.file)),n=e.some(e=>je(e,y));t&&!n&&await h.cacheManager.deleteMany(e=>je(e,s.file))}const x={n_ctx:i,n_threads:g,reasoning_budget_tokens:t.maxThinkingTokens,n_gpu_layers:0,jinja:!0};try{let t=w?await Re({model:e,signal:a,onProgress:n}):void 0;t||w||await Ae(h,e).catch(()=>!1)||(t=await Re({model:e,signal:a,onProgress:n}).catch(e=>{if(a?.aborted)throw new Error("Cancelled");if(!re(e))throw e;o&&console.log("Local model store failed",e)})),t?(n(1,d.load),await h.loadModel(t,x)):await h.loadModelFromUrl(v?{url:b,mmprojUrl:v}:b,{...x,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 Pe(),e}if(a?.aborted)throw await Pe(),new Error("Cancelled");if(!h.getLoadedContextInfo().n_ctx)throw await Pe(),new Error("Failed to create model context");return{model:e,backend:l.cpu,contextSize:i,chat:t=>(async(e,t,n)=>{if(Me=!0===n.logging,e.isModelLoaded())return await D({model:t,params:n,stopped:()=>!!n.signal?.aborted,start:async a=>{if($(a)&&!e.supportInputModality("image"))throw new Error("Model does not accept images");if(A(a)&&!e.supportInputModality("audio"))throw new Error("Model does not accept audio");return await e.createChatCompletion({messages:await Be(a),...L(n.limits,n.reasoning),abortSignal:n.signal,...t.reasoning?{chat_template_kwargs:{enable_thinking:n.reasoning}}:{}})}})})(h,e,t),unload:async()=>{if(xe==h)await Pe();else try{await h.exit()}catch{}}}},cached:async e=>{try{return!!e.cpu&&(!!await $e(e)||await Ae(await Le(),e))}catch{return!1}},hasCache:async()=>{try{for(const e of g.flatMap(e=>Se(e)))if(await ve.stored(e.url))return!0;const e=await Le();return(await e.cacheManager.list()).length>0}catch{return!1}},remove:async e=>{const t=Se(e);try{for(const e of t)await ve.remove(e.url)}catch{}try{const e=await Le();await e.cacheManager.deleteMany(e=>t.some(t=>je(e,t.name)))}catch{}},purge:async()=>{try{await ve.purge()}catch{}try{const e=await Le();await e.cacheManager.clear()}catch{}}},Ie=((e={})=>{const t=e.engines?.cpu||We,n=e.engines?.litert||be;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}})(),qe=(e,t,n)=>{const a=e.status(),o=p(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&&y(o),supportsAudio:!!o&&b(o),ready:e.ready(),loading:e.loading(),requesting:n.requesting,phase:n.phase,percent:n.percent,...n.error?{error:n.error}:{}}},ze=(e=>{let t,n=0;const a=t=>t.input.onStatus?.(qe(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 w(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=y(s),f=b(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 p=C(o.limits,!!s.litert),h=await e.load({model:s,limits:p,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(!h)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:p,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)}})(Ie),Oe=(Ne=Ie,async(e={})=>{const t=await w(e.model),n=n=>e.onStatus?.(qe(Ne,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=Ne.ready()&&Ne.model()?.id==t.id;return await Ne.load({model:t,limits:C(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 Ne;export{u as ChatModelId,s as ChatStatus,l as LlmBackend,d as LlmPhase,i as LlmRole,c as LlmStatus,ze as createChat,Oe as preloadModel};
package/dist/types.d.ts CHANGED
@@ -5,6 +5,17 @@ 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[];
10
+ }
11
+ /** One LiteRT-LM model file and its capabilities */
12
+ interface LitertArtifact {
13
+ /** File name inside the model repository */
14
+ file: string;
15
+ /** Whether the build accepts images */
16
+ vision: boolean;
17
+ /** Whether the build accepts audio */
18
+ audio: boolean;
8
19
  }
9
20
  /** One on-device model */
10
21
  interface ChatModel {
@@ -19,14 +30,15 @@ interface ChatModel {
19
30
  /** Vision projector, present when the CPU build accepts images */
20
31
  mmproj?: {
21
32
  file: string;
33
+ /** Whether the projector also accepts audio */
34
+ audio?: boolean;
22
35
  };
23
36
  };
24
37
  /** LiteRT-LM (WebGPU) artifact */
25
- litert?: {
38
+ litert?: LitertArtifact & {
26
39
  repo: string;
27
- file: string;
28
- /** Whether the build accepts images */
29
- vision: boolean;
40
+ /** Browser build used on phones, smaller and text only */
41
+ web?: LitertArtifact;
30
42
  };
31
43
  }
32
44
  /** Token, prompt and thread budgets of an on-device generation */
@@ -39,6 +51,8 @@ interface LlmLimits {
39
51
  maxReasoningTokens: number;
40
52
  /** Max images sent in one prompt */
41
53
  maxImages: number;
54
+ /** Max audio clips sent in one prompt */
55
+ maxAudio: number;
42
56
  /** Max characters kept per message */
43
57
  maxContentLength: number;
44
58
  /** Max characters of the whole prompt */
@@ -54,6 +68,8 @@ interface ChatStatusSnapshot {
54
68
  backend: LlmBackend;
55
69
  /** Whether the requested model accepts images on this backend */
56
70
  supportsImages: boolean;
71
+ /** Whether the requested model accepts audio on this backend */
72
+ supportsAudio: boolean;
57
73
  ready: boolean;
58
74
  loading: boolean;
59
75
  /** Whether a reply is streaming right now */
@@ -114,6 +130,13 @@ interface GpuNavigator extends Navigator {
114
130
  }) => Promise<unknown>;
115
131
  };
116
132
  }
133
+ /** Navigator fields that tell a phone or tablet from a desktop */
134
+ type MobileNavigator = Pick<Navigator, `userAgent` | `maxTouchPoints`> & {
135
+ /** Chromium client hints, the reliable mobile flag */
136
+ userAgentData?: {
137
+ mobile?: boolean;
138
+ };
139
+ };
117
140
  /** Request state that builds one status snapshot */
118
141
  interface ChatState {
119
142
  /** Whether a reply is streaming right now */
@@ -297,13 +320,16 @@ interface LlmEngine {
297
320
  /** Delete every on-device artifact this engine keeps, including orphans */
298
321
  purge: () => Promise<void>;
299
322
  }
300
- /** One text or image segment of a user turn */
323
+ /** One text, image or audio segment of a user turn */
301
324
  type EngineContentPart = {
302
325
  type: `text`;
303
326
  text: string;
304
327
  } | {
305
328
  type: `image`;
306
329
  url: string;
330
+ } | {
331
+ type: `audio`;
332
+ url: string;
307
333
  };
308
334
  /** One wllama content segment */
309
335
  type WllamaContentPart = {
@@ -312,6 +338,9 @@ type WllamaContentPart = {
312
338
  } | {
313
339
  type: `image`;
314
340
  data: ArrayBuffer;
341
+ } | {
342
+ type: `audio`;
343
+ data: ArrayBuffer;
315
344
  };
316
345
  /** One wllama chat turn */
317
346
  type WllamaMessage = {
@@ -324,7 +353,7 @@ type WllamaMessage = {
324
353
  role: `assistant`;
325
354
  content: string;
326
355
  };
327
- /** One OpenAI-style message */
356
+ /** One OpenAI-style message, a user turn may carry media parts */
328
357
  type EngineMessage = {
329
358
  role: `system`;
330
359
  content: string;
@@ -429,4 +458,4 @@ interface EngineRunParams {
429
458
  /** Called when a slow stream keeps the text already delivered */
430
459
  onStall?: () => void;
431
460
  }
432
- export type { ChatMessage, ChatModel, LlmLimits, ChatStatusSnapshot, ChatInput, ChatPreloadInput, ChatState, CancelChat, WllamaAssets, LitertAssets, GpuNavigator, ActiveCall, RuntimeDeps, RuntimeLoadParams, RuntimeLoadWaiter, RuntimeChatParams, StoredPart, StoredFilePart, StoredModel, StoredModelMeta, ResumePlan, StoreDownloadParams, ModelStore, Runtime, EngineChatParams, StartLlmParams, LlmRuntime, LlmEngine, EngineMessage, EngineContentPart, WllamaContentPart, WllamaMessage, LitertContentPart, LitertMessage, LitertEngine, LitertModule, LitertSettings, LitertSettingsParams, LitertSession, LitertChatParams, StreamLitertParams, EngineDelta, EngineChunk, StreamReplyParams, EngineRunParams, };
461
+ export type { ChatMessage, ChatModel, LitertArtifact, LlmLimits, ChatStatusSnapshot, ChatInput, ChatPreloadInput, ChatState, CancelChat, WllamaAssets, LitertAssets, GpuNavigator, MobileNavigator, ActiveCall, RuntimeDeps, RuntimeLoadParams, RuntimeLoadWaiter, RuntimeChatParams, StoredPart, StoredFilePart, StoredModel, StoredModelMeta, ResumePlan, StoreDownloadParams, ModelStore, Runtime, EngineChatParams, StartLlmParams, LlmRuntime, LlmEngine, EngineMessage, EngineContentPart, WllamaContentPart, WllamaMessage, LitertContentPart, LitertMessage, LitertEngine, LitertModule, LitertSettings, LitertSettingsParams, LitertSession, LitertChatParams, StreamLitertParams, EngineDelta, EngineChunk, StreamReplyParams, EngineRunParams, };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@degreesign/chat",
3
- "version": "0.3.7",
3
+ "version": "0.5.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",