@degreesign/chat 0.4.0 → 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
@@ -125,13 +125,16 @@ only to a model with vision support, center-crops each image to a square,
125
125
  resizes it to 288 x 288, and caps the number with `limits.maxImages`. The
126
126
  WebGPU engine asks LiteRT for the smallest Gemma 4 vision budget (70 tokens,
127
127
  630 patches), so it encodes the crop at 384 x 384 instead of upscaling it to
128
- 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.
129
131
 
130
132
  Attach a recording with `audio: [dataUrl]`. Gemma 4 E2B accepts WAV, MP3 and
131
- FLAC on the WebGPU engine and loads its audio encoder on demand. A short 16 kHz
132
- mono WAV is the best input. `limits.maxAudio` caps the clips (default 1). A
133
- model without audio drops the clip when the turn also has text; an audio-only
134
- turn reports `Model does not accept audio`. The package does not transcode, so
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
135
138
  the host supplies one of those formats.
136
139
 
137
140
  The package never writes storage, so the host appends the user message before
@@ -140,8 +143,12 @@ the call and appends the assistant message after the last `onResponse`.
140
143
  ## Enable WebGPU
141
144
 
142
145
  A request without `model` picks `gemma4_e2b` when the browser exposes a WebGPU
143
- adapter and `qwen3_vl_2b` when it does not. The GPU engine is far faster, so
144
- 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:
145
152
 
146
153
  ```js
147
154
  // Run in the console of the page that loads the package
@@ -204,7 +211,8 @@ package requests the smallest Gemma 4 vision budget, 70 tokens and 630 patches,
204
211
  so the encoder runs one small signature and the crop is resized to 384 x 384
205
212
  rather than the full 768 x 768. If the error still appears, close other GPU
206
213
  tabs, unload the model, or switch to the CPU model. A lost device needs a page
207
- or model reload; later calls cannot reuse it.
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.
208
216
 
209
217
  ## Public API
210
218
 
@@ -311,7 +319,12 @@ without it goes quiet again.
311
319
  | `ChatModelId` | Name | Reasoning | Images | Audio | CPU | WebGPU |
312
320
  | ------------- | ---- | --------- | ------ | ----- | --- | ------ |
313
321
  | `qwen3_vl_2b` | Qwen3-VL 2B | Yes | Yes | No | Yes | No |
314
- | `gemma4_e2b` | Gemma 4 E2B | Yes | Yes | Yes | No | Yes |
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.
315
328
 
316
329
  The first call downloads the model before its engine loads it. Later calls
317
330
  reuse the loaded runtime. Only one model stays in memory. An interrupted
@@ -319,11 +332,15 @@ download keeps every part it committed and resumes from the last one on the
319
332
  next attempt, including after a reload.
320
333
 
321
334
  Qwen3-VL 2B downloads a vision projector of about 445 MB with the model, so its
322
- 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.
323
337
 
324
- Gemma 4 E2B downloads one `gemma-4-E2B-it.litertlm` file of about 2.6 GB. It
338
+ Gemma 4 E2B downloads `gemma-4-E2B-it.litertlm`, about 2.6 GB, on a desktop. It
325
339
  runs on the WebGPU engine with its vision and audio encoders in the same file,
326
- so an image or audio prompt does not download a projector.
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.
327
344
 
328
345
  The WebGPU engine keeps one LiteRT conversation for consecutive turns. When the
329
346
  next prompt continues that history, only the new user turn is sent. A changed
@@ -534,20 +551,22 @@ No. The host passes the full prompt on every call and keeps the reply.
534
551
 
535
552
  Yes, on models with vision support. Put image URLs in a user turn's `images`
536
553
  array. Qwen3-VL 2B accepts images on the CPU engine and Gemma 4 E2B accepts
537
- images on the WebGPU engine. Each image is center-cropped to a square and
538
- resized to 288 x 288 before the engine sees it. The WebGPU engine encodes that
539
- crop at 384 x 384 with the smallest Gemma 4 vision budget. A model without
540
- vision drops images when the turn also has text; an image-only turn reports
541
- `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`.
542
560
 
543
561
  ### Does it support audio?
544
562
 
545
563
  Yes, on models with audio support. Put an audio URL in a user turn's `audio`
546
- array. Gemma 4 E2B accepts WAV, MP3 and FLAC on the WebGPU engine, and a short
547
- 16 kHz mono WAV is the best input. `limits.maxAudio` caps the clips (default 1).
548
- The package does not transcode, so the host supplies one of those formats. A
549
- model without audio drops the clip when the turn also has text; an audio-only
550
- turn reports `Model does not accept audio`.
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`.
551
570
 
552
571
  ### Does it need a server?
553
572
 
@@ -571,9 +590,10 @@ when an older version stored the file. The package stores no model data itself.
571
590
  ### Which model should I pick?
572
591
 
573
592
  Qwen3-VL 2B supports reasoning and images on the CPU. Gemma 4 E2B supports
574
- reasoning, images and audio on WebGPU, with much faster media prompts but a
575
- larger download and a slower first compile. Use Qwen for broad browser support
576
- and Gemma when the host has Chromium WebGPU.
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.
577
597
 
578
598
  ## Licensing
579
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,audio:!0}}],d=e=>e?l.find(t=>t.id==e):void 0,u=async()=>(await(async()=>{const e="undefined"==typeof navigator?void 0:navigator.gpu;if(!e)return!1;try{return!!await e.requestAdapter({powerPreference:"high-performance"})}catch{return!1}})()?l.find(e=>!!e.litert):l.find(e=>!!e.cpu))?.id,g=async e=>d(e||await u()),m=e=>!!e.cpu?.mmproj||!!e.litert?.vision,f=e=>!!e.cpu?.mmproj?.audio||!!e.litert?.audio,p=new URL("./../assets/wllama/wllama.wasm",import.meta.url).href,h=new URL("./../assets/wllama/compat/wllama.wasm",import.meta.url).href,w=new URL("./../assets/wllama/compat/wllama.js",import.meta.url).href,y=new URL("./../assets/litert/litertlm_wasm_internal.js",import.meta.url).href,b=(new URL("./../assets/litert/litertlm_wasm_internal.wasm",import.meta.url).href,288),v=async e=>{if(!e.startsWith("data:"))return await(await fetch(e)).arrayBuffer();const t=e.indexOf(","),n=t>=0?e.slice(0,t):"";if(t<0)throw new Error("Invalid image data");if(!n.includes(";base64"))return await(await fetch(e)).arrayBuffer();const a=atob(e.slice(t+1)),o=new Uint8Array(a.length);for(let e=0;e<a.length;e++)o[e]=a.charCodeAt(e);return o.buffer},x=async e=>{if(!e||"function"!=typeof fetch||"function"!=typeof createImageBitmap)return e;try{const t=await fetch(e),n=await createImageBitmap(await t.blob()),a=Math.min(n.width,n.height),o=Math.floor((n.width-a)/2),r=Math.floor((n.height-a)/2),i=((e,t)=>{if("undefined"!=typeof OffscreenCanvas)return new OffscreenCanvas(e,t);if("undefined"==typeof document)return;const n=document.createElement("canvas");return n.width=e,n.height=t,n})(b,b),s=i?i.getContext("2d"):null;if(!i||!s||a<1)return n.close(),e;if(s.drawImage(n,o,r,a,a,0,0,b,b),n.close(),(e=>"undefined"!=typeof OffscreenCanvas&&e instanceof OffscreenCanvas)(i)){const t=await i.convertToBlob({type:"image/jpeg",quality:.85});return await(e=>new Promise(t=>{if("undefined"==typeof FileReader)return void t("");const n=new FileReader;n.onerror=()=>t(""),n.onload=()=>t(`${n.result}`),n.readAsDataURL(e)}))(t)||e}return i.toDataURL("image/jpeg",.85)}catch{return e}},k={maxReplyTokens:250,maxThinkingTokens:128,maxReasoningTokens:320,maxImages:2,maxAudio:1,maxContentLength:1e3,promptCharBudget:1e4,maxThreads:6},_=(e,t,n)=>"number"==typeof e&&Number.isFinite(e)&&e>=n?Math.floor(e):t,M=(e={},t=!1)=>{const n=k;return{maxReplyTokens:_(e.maxReplyTokens,n.maxReplyTokens,1),maxThinkingTokens:_(e.maxThinkingTokens,n.maxThinkingTokens,1),maxReasoningTokens:_(e.maxReasoningTokens,t?1200:n.maxReasoningTokens,1),maxImages:_(e.maxImages,n.maxImages,0),maxAudio:_(e.maxAudio,n.maxAudio,0),maxContentLength:_(e.maxContentLength,n.maxContentLength,1),promptCharBudget:_(e.promptCharBudget,n.promptCharBudget,1),maxThreads:_(e.maxThreads,n.maxThreads,1)}},T=(e,t)=>({stream:!0,temperature:.7,max_tokens:t?e.maxReasoningTokens:e.maxReplyTokens}),E=(e,t)=>`https://huggingface.co/${e}/resolve/main/${t}`,C=e=>Number(e.headers.get("content-length"))||0,L=(e,t)=>e.some(e=>Array.isArray(e.content)&&e.content.some(e=>e.type==t)),P=e=>L(e,"image"),R=e=>L(e,"audio"),$=["<|channel>thought\n","<|channel>thought","..."],j=["<channel|>","..."],S=(e,t,n)=>t.length||n.length?[...e?[{type:"text",text:e}]:[],...t.map(e=>({type:"image",url:e})),...n.map(e=>({type:"audio",url:e}))]:e,A=e=>"string"==typeof e?e?[{type:"text",text:e}]:[]:e,W=(e,t)=>{if("string"==typeof e&&"string"==typeof t)return`${e}\n${t}`;const n=[];for(const a of[...A(e),{type:"text",text:"\n"},...A(t)]){const e=n[n.length-1];"text"==a.type&&"text"==e?.type?e.text+=a.text:n.push(a)}return n};class B extends Error{}const I=async(e,t,n)=>{let a;try{return await Promise.race([e,new Promise((e,o)=>{a=setTimeout(()=>o(new B(n)),t)})])}finally{a&&clearTimeout(a)}},q=e=>{try{Promise.resolve(e.return?.()).catch(()=>{})}catch{}},z=async({model:e,logging:t,onReason:n,onText:a,onSpeed:o,start:r,stopped:i,firstTimeoutMs:s=18e4,idleTimeoutMs:c=3e4,onStall:l})=>{let d="",u="",g="",m=!1,f=0,p=0;const h=((e,t)=>{let n="",a=!1;const o=(e,t)=>{let n=-1,a="";for(const o of t){const t=e.indexOf(o);t>=0&&(n<0||t<n)&&(n=t,a=o)}return{at:n,tag:a}},r=(e,t)=>{let n="";for(const a of t)for(let t=Math.min(e.length,a.length-1);t>n.length;t--)if(e.endsWith(a.slice(0,t))){n=e.slice(e.length-t);break}return n};return{feed:i=>{let s=n+i;for(n="";s;){if(a){const{at:t,tag:i}=o(s,j);if(t>=0){t&&e(s.slice(0,t)),s=s.slice(t+i.length),a=!1;continue}const c=r(s,j);e(s.slice(0,s.length-c.length)),n=c;break}{const{at:e,tag:i}=o(s,$);if(e>=0){e&&t(s.slice(0,e)),s=s.slice(e+i.length),s.startsWith("\n")&&(s=s.slice(1)),a=!0;continue}const c=r(s,$);t(s.slice(0,s.length-c.length)),n=c;break}}},flush:()=>{n&&(a?e(n):t(n),n="")}}})(e=>{u+=e,n(e)},e=>{d+=e,a(e)});try{const e=(await I(r(),s,"Model did not start"))[Symbol.asyncIterator]();for(;;){if(i()){q(e);break}let a;try{a=await I(e.next(),m?c:s,m?"Model stopped responding":"Model did not reply")}catch(n){if(n instanceof B&&(d||u)){l?.(),q(e),t&&console.log("Local chat stalled",n);break}throw n}if(a.done)break;m=!0;const r=a.value?.choices?.[0],w=r?.delta,y="string"==typeof w?.reasoning_content?w.reasoning_content:"string"==typeof w?.reasoning?w.reasoning:"";if(r?.finish_reason&&(g=r.finish_reason),y&&(u+=y,n(y)),w?.content&&h.feed(w.content),y||w?.content){const e=performance.now();f||(p=e),f+=1,f>1&&e>p&&o?.(1e3*f/(e-p))}}h.flush()}catch(e){if(t&&console.log("Local chat failed",e),e instanceof B&&!i())throw e}const w=i()?void 0:d||void 0;return t&&(console.log("Local chat reply",e.id,w?"****":""),console.log("Local chat finish",e.id,g||"none",d.length,u.length)),w},O=async({model:e,params:t,start:n,stopped:o,onStall:r})=>{const i=await(async e=>{const t=[];for(const n of e){if("string"==typeof n.content){t.push(n);continue}const e=[];for(const t of n.content)e.push("image"==t.type?{type:"image",url:await x(t.url)}:t);t.push({role:"user",content:e})}return t})(((e,t=k)=>{const n=t.maxContentLength,o=e.filter(e=>"string"==typeof e.content&&!!e.content.trim()||!(e.role!=a.user||!e.images?.length&&!e.audio?.length)),r=[],i=[];let s=0,c=t.maxImages,l=t.maxAudio;for(let e=o.length-1;e>=0;e--){const d=o[e],u=("string"==typeof d.content?d.content:"").slice(0,n),g=d.role==a.user?(d.images||[]).filter(e=>"string"==typeof e&&!!e).slice(0,Math.max(0,c)):[],m=d.role==a.user?(d.audio||[]).filter(e=>"string"==typeof e&&!!e).slice(0,Math.max(0,l)):[];if(u||g.length||m.length){if(u&&s+u.length>t.promptCharBudget)break;s+=u.length,c-=g.length,l-=m.length,d.role==a.system?r.unshift(u):d.role==a.assistant?i.unshift({role:"assistant",content:u}):i.unshift({role:"user",content:S(u,g,m)})}}for(;i.length&&"user"!=i[0].role;)i.shift();const d=[];for(const e of i){const t=d[d.length-1];"assistant"==t?.role&&"assistant"==e.role?t.content=`${t.content}\n${e.content}`:"user"==t?.role&&"user"==e.role?t.content=W(t.content,e.content):"assistant"==e.role?d.push({role:"assistant",content:e.content}):d.push({role:"user",content:e.content})}const u=r.join("\n\n"),g=[];return u&&g.push({role:"system",content:u}),g.push(...d),g})(t.messages,t.limits));return t.logging&&((e,t)=>{console.log("Local chat call",e.id,t.map(({role:e,content:t})=>({role:e,content:"****",...Array.isArray(t)?{images:t.filter(e=>"image"==e.type).length,audio:t.filter(e=>"audio"==e.type).length}:{}})))})(e,i),await z({model:e,logging:t.logging,onReason:t.onReason,onText:t.onText,onSpeed:t.onSpeed,start:()=>n(i),stopped:o,onStall:r})},U=33554432,N=new Map;let F=!1;const D=e=>{const t=(e.split("/").pop()||"model").replace(/[^\w.-]+/g,"_");let n=0;for(let t=0;t<e.length;t++)n=31*n+e.charCodeAt(t)>>>0;return`${n.toString(16)}-${t}`},H=e=>{const t=/^part-(\d+)$/.exec(e);return t?Number(t[1]):-1},Q=async()=>{if(!F)if("undefined"!=typeof navigator&&navigator.storage?.getDirectory)try{return await navigator.storage.getDirectory()}catch{return}else F=!0},G=async(e,t)=>{try{return await e.getDirectoryHandle(t)}catch{return}},V=async e=>{if(e)try{const t=await e.getFile(),n=JSON.parse(await t.text());return{total:Number(n.total)||0,etag:`${n.etag||""}`,complete:!0===n.complete}}catch{return}},J=async(e,t)=>{try{const n=await e.getFileHandle("meta.json",{create:!0}),a=await n.createWritable();await a.write(JSON.stringify(t)),await a.close()}catch{}},K=async function*(e){for await(const t of e)yield t},X=async e=>{try{for await(const[t,n]of K(e))"file"==n.kind&&H(t)>=0&&await e.removeEntry(t).catch(()=>{})}catch{}},Y=async(e,t)=>{try{const n=await(await e.getFileHandle(t)).getFile(),a=await V(await e.getFileHandle(`${t}.json`).catch(()=>{}));if(!a)return;return{file:n,size:n.size,total:a.total,etag:a.etag,complete:a.total?n.size>=a.total:!0===a.complete}}catch{return}},Z=async(e,t)=>{const n=await G(e,t);if(n){const e=await(async e=>await V(await e.getFileHandle("meta.json").catch(()=>{})))(n),t=await(async e=>{const t=[];try{for await(const[n,a]of K(e)){const e=H(n);if("file"!=a.kind||e<0)continue;const o=await a.getFile();t.push({part:{start:e,size:o.size},file:o})}}catch{return}const n=(e=>{const t=[];let n=0;for(const a of[...e].sort((e,t)=>e.start-t.start))if(a.size){if(a.start!=n)break;t.push(a),n+=a.size}return t})(t.map(e=>e.part)),a=new Set(n.map(e=>e.start)),o=t.filter(e=>a.has(e.part.start)).sort((e,t)=>e.part.start-t.part.start).map(e=>e.file);if(o.length)return{file:1==o.length?o[0]:new Blob(o),size:n.reduce((e,t)=>e+t.size,0)}})(n),a=e?.total||0,o=t?.size??0,r=t?.file??new Blob;return{file:a&&o>a?r.slice(0,a):r,size:o,total:a,etag:e?.etag||"",complete:a?o>=a:!0===e?.complete}}return await Y(e,t)},ee=(e,t)=>{const n=e.headers.get("content-range"),a=/bytes (\d+)-/.exec(n||""),o=/\/(\d+)\s*$/.exec(n||""),r=e.headers.get("etag")||"";if(206==e.status){const e=a?Number(a[1]):t.size,n=o?Number(o[1]):t.total;if(e!=t.size)return;if(t.total&&n&&t.total!=n)return;if(t.etag&&r&&t.etag!=r)return;return{start:e,total:n||t.total,etag:r||t.etag}}if(!t.etag||!r||t.etag==r)return{start:0,total:C(e)||t.total,etag:r||t.etag}},te=async({dir:e,start:t,skip:n,total:a,body:o,onProgress:r,onChunk:i})=>{let c,l=t,d=0,u=n;const g=async()=>{const t=await e.getFileHandle((e=>`part-${e}`)(l),{create:!0});c=await t.createWritable(),d=0},m=async()=>{const e=c;c=void 0,e&&await e.close().catch(()=>{})};try{const e=o.getReader();for(;;){const{done:n,value:o}=await e.read();if(n)break;i();let f=0;for(u&&(f=Math.min(u,o.byteLength),u-=f);f<o.byteLength;){c||await g();const e=Math.min(U-d,o.byteLength-f);await c.write({type:"write",position:d,data:o.subarray(f,f+e)}),f+=e,d+=e,l+=e,d==U&&await m()}l>t&&r(a?l/a:0,s.download)}await m()}catch(e){throw await m(),e}return l},ne=e=>["NotAllowedError","NotSupportedError"].includes(e?.name||""),ae=e=>{const t=async()=>{const t=await Q();if(t)try{return await t.getDirectoryHandle(e,{create:!0})}catch{return}};return{stored:async e=>{const n=await t();return n?await Z(n,D(e)):void 0},download:async({url:n,signal:a,onProgress:o})=>{const r=`${e}|${n}`,i=N.get(r);if(i)return await i;const c=await t();if(!c)return;const l=(async(e,t,n,a)=>{const o=D(t),r=await Y(e,o);if(r?.complete)return r;let i=await G(e,o);i||(await e.removeEntry(o).catch(()=>{}),await e.removeEntry(`${o}.json`).catch(()=>{}),i=await e.getDirectoryHandle(o,{create:!0}));let c=0,l=0;for(;;){if(n?.aborted)throw new Error("Cancelled");const r=await Z(e,o);if(r?.complete)return r;const d=r?.size??0,u=r?.total||0,g=r?.etag||"";if(d>c)c=d,l=0;else if(++l>4)throw new Error("Model download failed");d&&u&&a(d/u,s.download);const m=u?Math.min(d+U,u)-1:d+U-1,f=new AbortController,p=()=>f.abort(),h={size:d,total:u,etag:g};let w,y=setTimeout(p,3e4);n?.addEventListener("abort",p,{once:!0});try{if(w=await fetch(t,{signal:f.signal,headers:{Range:`bytes=${d}-${m}`}}),416==w.status){if(await(w.body?.cancel().catch(()=>{})),u&&d>=u){await J(i,{total:u,etag:g,complete:!0});continue}await X(i),await J(i,{total:0,etag:"",complete:!1});continue}if(!w.ok||!w.body)throw await(w.body?.cancel().catch(()=>{})),new Error("Model download failed");const n=ee(w,h);if(!n){await w.body.cancel().catch(()=>{}),await X(i),await J(i,{total:0,etag:"",complete:!1});continue}await J(i,{total:n.total,etag:n.etag,complete:!1});const r=0==n.start?d:0,s=await te({dir:i,start:n.start+r,skip:r,total:n.total,body:w.body,onProgress:a,onChunk:()=>{clearTimeout(y),y=setTimeout(p,3e4)}});if(!n.total){await J(i,{total:s,etag:n.etag,complete:!0});const t=await Z(e,o);if(t)return t}}catch(e){if(n?.aborted)throw new Error("Cancelled");if(e instanceof DOMException&&["QuotaExceededError","NotAllowedError","NotSupportedError","InvalidStateError","TypeMismatchError","SecurityError"].includes(e.name))throw e;await new Promise(e=>setTimeout(e,250+500*l))}finally{clearTimeout(y),n?.removeEventListener("abort",p)}}})(c,n,a,o).finally(()=>{N.delete(r)});return N.set(r,l),await l},remove:async e=>{const n=await t();n&&await(async(e,t)=>{await e.removeEntry(t,{recursive:!0}).catch(()=>{}),await e.removeEntry(`${t}.json`).catch(()=>{})})(n,D(e))},purge:async()=>{const t=await Q();t&&await t.removeEntry(e,{recursive:!0}).catch(()=>{})}}},oe="litertlm-models",re=ae("litert-store"),ie={num_output_candidates:1,wait_for_weight_uploads:!0,num_decode_steps_per_sync:3,sequence_batch_size:0,supported_lora_ranks:[],max_top_k:40,enable_decode_logits:!1,enable_external_embeddings:!1,use_submodel:!1,use_autosized_ringbuffers:!0};let se=!1;const ce=(...e)=>{se&&console.log(...e)},le=(...e)=>{se&&console.error(...e)},de=async()=>{try{if("undefined"==typeof caches)return;return await caches.open(oe)}catch{return}},ue=new Map,ge=(e,t,n)=>{let a=0;return e.pipeThrough(new TransformStream({transform:(e,o)=>{a+=e.byteLength,n(t?a/t:0,s.download),o.enqueue(e)}}))},me=async(e,t=!0)=>{const n=[];for(const a of e){if("string"==typeof a.content){n.push({role:a.role,content:a.content});continue}const e=[];for(const n of a.content)"text"!=n.type?t?e.push({type:n.type,data:await v(n.url)}):e.push({type:"text",text:`[${n.type}]`}):e.push({type:"text",text:n.text});n.push({role:a.role,content:e})}return n},fe=(e,t)=>{if(e.role!=t.role)return!1;if("string"==typeof e.content||"string"==typeof t.content)return e.content==t.content;if(e.content.length!=t.content.length)return!1;for(let n=0;n<e.content.length;n++){const a=e.content[n],o=t.content[n];if(a.type!=o.type)return!1;if("text"==a.type&&"text"==o.type){if(a.text!=o.text)return!1}else if(("image"==a.type&&"image"==o.type||"audio"==a.type&&"audio"==o.type)&&a.url!=o.url)return!1}return!0},pe=async function*({engine:e,mod:t,params:n,messages:a,session:o}){const r=a[a.length-1],i=T(n.limits,n.reasoning);if("user"!=r?.role)throw new Error("No user turn");const s=a.slice(0,-1),c=(await me([r]))[0];if(!c)throw new Error("Empty prompt");const l=!!o.conversation&&o.reasoning==n.reasoning&&o.maxTokens==i.max_tokens&&((e,t)=>{const n="system"==e[0]?.role?e[0]:void 0,a="system"==t[0]?.role?t[0]:void 0;if(!n!=!a)return!1;if(n&&a&&!fe(n,a))return!1;const o=n?e.slice(1):e,r=a?t.slice(1):t;if(!r.length||r.length>o.length)return!1;const i=o.length-r.length;for(let e=0;e<r.length;e++)if(!fe(o[i+e],r[e]))return!1;return!0})(o.sent,s);let d;if(l&&o.conversation)d=o.conversation;else{const a=await me(s,!1);o.conversation=void 0,o.sent=[],d=await e.createConversation({sessionConfig:{maxOutputTokens:i.max_tokens,samplerParams:{type:t.SamplerType.TOP_K,temperature:i.temperature,k:40,p:.95}},preface:{...a.length?{messages:a}:{},extra_context:{enable_thinking:n.reasoning}},prefillPrefaceOnInit:a.length>0}),o.conversation=d,o.reasoning=n.reasoning,o.maxTokens=i.max_tokens,o.sent=[...s]}o.sent.push(r);let u,g=!1,m="";const f=()=>{u&&u.cancel().catch(()=>{});try{d.cancel()}catch{}};n.signal?.addEventListener("abort",f,{once:!0});try{if(n.signal?.aborted)return;const e=d.sendMessageStreaming(c).getReader();for(u=e;;){const{done:t,value:n}=await e.read();if(t)break;const a={};let o="string"==typeof n.content?n.content:"";if(Array.isArray(n.content))for(const e of n.content)"text"==e.type&&(o+=e.text);o&&(a.content=o,m+=o);const r=n.channels?.thought;r&&(a.reasoning=r),(a.content||a.reasoning)&&(yield{choices:[{delta:a}]})}g=!0}finally{n.signal?.removeEventListener("abort",f),g&&m?o.sent.push({role:"assistant",content:m}):(o.conversation=void 0,o.sent=[])}},he={backend:i.gpu,startLLM:async({model:e,limits:t,onProgress:a,signal:o,logging:r})=>{se=!0===r;const c=e.litert;if(!c)throw new Error("Model has no LiteRT artifact");const l=await n.e(520).then(()=>n(507)),d=globalThis,u=d.Module;d.Module={print:ce,printErr:le};try{await l.getOrLoadGlobalLiteRtLm(y)}finally{d.Module=u}if(o?.aborted)throw new Error("Cancelled");const g=E(c.repo,c.file),m=await de();let f;const p=m?await m.match(g):void 0;if(p?.body)f=p.body;else{const e=await re.stored(g);e?.complete&&(f=e.file.stream())}if(!f){const e=await re.download({url:g,signal:o,onProgress:a}).catch(e=>{if(o?.aborted)throw new Error("Cancelled");if(!ne(e))throw e;r&&console.log("Local model store failed",e)});e&&(f=e.file.stream())}if(!f&&m)try{await(async(e,t,n,a)=>{const o=ue.get(e);if(o)return await o;const r=(async()=>{const o=await fetch(e,{signal:n});if(!o.ok||!o.body)throw new Error("Model download failed");const r=C(o),i=ge(o.body,r,a);await t.put(e,new Response(i,{headers:{"content-type":"application/octet-stream",...r?{"content-length":`${r}`}:{}}}))})().finally(()=>{ue.delete(e)});return ue.set(e,r),await r})(g,m,o,a);const e=await m.match(g);e?.body&&(f=e.body)}catch(e){if(o?.aborted)throw new Error("Cancelled");r&&console.log("Local model cache failed",e)}if(f||(f=await(async(e,t,n)=>{const a=await fetch(e,{signal:t});if(!a.ok||!a.body)throw new Error("Model download failed");return ge(a.body,C(a),n)})(g,o,a)),o?.aborted)throw new Error("Cancelled");a(1,s.load);const h=await l.Engine.create((({model:e,limits:t,source:n,mod:a})=>({model:n,mainExecutorSettings:{maxNumTokens:4096,...e.litert?.vision?{maxNumImages:Math.max(1,t.maxImages)}:{},backendConfig:ie},...e.litert?.vision?{visionBackend:a.Backend.GPU,maxVisionTokensPerImage:70,minVisionTokensPerImage:70}:{},...e.litert?.audio?{audioBackend:a.Backend.GPU}:{}}))({model:e,limits:t,source:f,mod:l}));if(o?.aborted)throw await h.delete(),new Error("Cancelled");const w={sent:[],reasoning:!1,maxTokens:0};return{model:e,backend:i.gpu,contextSize:4096,chat:t=>(async({engine:e,model:t,mod:n,params:a,session:o})=>{se=!0===a.logging;const r=()=>{try{o.conversation?.cancel()}catch{}o.conversation=void 0,o.sent=[]};try{return await O({model:t,params:a,stopped:()=>!!a.signal?.aborted,onStall:r,start:async r=>{if(a.signal?.aborted)throw new Error("Cancelled");if(P(r)&&!t.litert?.vision)throw new Error("Model does not accept images");if(R(r)&&!t.litert?.audio)throw new Error("Model does not accept audio");return pe({engine:e,mod:n,params:a,messages:r,session:o})}})}catch(e){throw r(),e}})({engine:h,model:e,mod:l,params:t,session:w}),unload:async()=>{w.conversation=void 0,w.sent=[],await h.delete()}}},cached:async e=>{const t=e.litert;if(!t)return!1;const n=E(t.repo,t.file),[a,o]=await Promise.all([de(),re.stored(n)]);if(o?.complete)return!0;try{return!(!a||!await a.match(n))}catch{return!1}},hasCache:async()=>{try{for(const e of l){const t=e.litert;if(!t)continue;if(await re.stored(E(t.repo,t.file)))return!0}const e=await de();return!!e&&(await e.keys()).length>0}catch{return!1}},remove:async e=>{try{const t=e.litert;if(!t)return;const n=E(t.repo,t.file),a=await de();await re.remove(n),a&&await a.delete(n)}catch{}},purge:async()=>{try{await re.purge(),"undefined"!=typeof caches&&await caches.delete(oe)}catch{}}},we=ae("wllama-store");let ye,be,ve="",xe=!1;const ke=e=>`${e.wasm}|${e.compatWasm}|${e.compatWorker}`,_e=e=>{const t={wasm:p,compatWasm:h,compatWorker:w};return{wasm:e?.wasm||t.wasm,compatWasm:e?.compatWasm||t.compatWasm,compatWorker:e?.compatWorker||t.compatWorker}},Me=e=>{const t=e=>(...t)=>{xe&&("string"==typeof t[0]&&t[0].includes("ggml_webgpu: Failed to get an adapter")||e(...t))};return{...e,log:t(e.log),warn:t(e.warn),error:t(e.error)}},Te=async()=>{if(ye)return ye;const e=await n.e(924).then(()=>n(577)),t=be||_e(),a=new e.Wllama({default:t.wasm},{logger:Me(e.LoggerWithoutDebug)});return be||(be=t,ve=ke(t)),a.setCompat({wasm:t.compatWasm,worker:t.compatWorker}),ye=a,a},Ee=async()=>{const e=ye;ye=void 0;try{await(e?.exit())}catch{}},Ce=(e,t)=>e.name.includes(t)||!!e.metadata?.originalURL?.includes(t),Le=e=>{const t=e.cpu;return t?[{name:t.file,url:E(t.repo,t.file)},...t.mmproj?[{name:t.mmproj.file,url:E(t.repo,t.mmproj.file)}]:[]]:[]},Pe=async e=>{const t=Le(e);if(!t.length)return!1;for(const e of t){const t=await we.stored(e.url);if(!t?.complete)return!1}return!0},Re=async(e,t)=>{const n=await e.cacheManager.list();return Le(t).every(e=>n.some(t=>Ce(t,e.name)))},$e=async({model:e,signal:t,onProgress:n})=>{const a=Le(e),o=[];for(let e=0;e<a.length;e++){if(t?.aborted)throw new Error("Cancelled");const r=await we.download({url:a[e].url,signal:t,onProgress:(t,o)=>n((e+t)/a.length,o)});if(!r)return;o.push(r.file)}return o.length?o:void 0},je=async e=>{const t=[];for(const n of e){if("string"==typeof n.content){"system"==n.role?t.push({role:"system",content:n.content}):"assistant"==n.role?t.push({role:"assistant",content:n.content}):t.push({role:"user",content:n.content});continue}const e=[];for(const t of n.content)"text"==t.type?e.push({type:"text",text:t.text}):e.push({type:t.type,data:await v(t.url)});t.push({role:"user",content:e})}return t},Se={backend:i.cpu,startLLM:async({model:e,limits:t,onProgress:n,signal:a,logging:o,assets:r})=>{xe=!0===o;const c=e.cpu;if(!c)throw new Error("Model has no CPU artifact");const l=_e(r),d=ke(l);ye&&d!=ve&&await Ee(),be=l,ve=d;const u=(g="undefined"!=typeof navigator&&navigator.hardwareConcurrency||4,m=t.maxThreads,Math.max(1,Math.min(Math.max(2,Math.min(8,(g||4)-1)),m)));var g,m;const f=await Te(),p=await Pe(e),h=c.mmproj?.file,w=E(c.repo,c.file),y=h?E(c.repo,h):"";if(a?.aborted)throw await Ee(),new Error("Cancelled");if(!p&&h){const e=await f.cacheManager.list(),t=e.some(e=>Ce(e,c.file)),n=e.some(e=>Ce(e,h));t&&!n&&await f.cacheManager.deleteMany(e=>Ce(e,c.file))}const b={n_ctx:8192,n_threads:u,reasoning_budget_tokens:t.maxThinkingTokens,n_gpu_layers:0,jinja:!0};try{let t=p?await $e({model:e,signal:a,onProgress:n}):void 0;t||p||await Re(f,e).catch(()=>!1)||(t=await $e({model:e,signal:a,onProgress:n}).catch(e=>{if(a?.aborted)throw new Error("Cancelled");if(!ne(e))throw e;o&&console.log("Local model store failed",e)})),t?(n(1,s.load),await f.loadModel(t,b)):await f.loadModelFromUrl(y?{url:w,mmprojUrl:y}:w,{...b,useCache:!0,signal:a,progressCallback:({loaded:e,total:t})=>{const a=t?e/t:0;n(a,a>=1?s.load:s.download)}})}catch(e){throw await Ee(),e}if(a?.aborted)throw await Ee(),new Error("Cancelled");if(!f.getLoadedContextInfo().n_ctx)throw await Ee(),new Error("Failed to create model context");return{model:e,backend:i.cpu,contextSize:8192,chat:t=>(async(e,t,n)=>{if(xe=!0===n.logging,e.isModelLoaded())return await O({model:t,params:n,stopped:()=>!!n.signal?.aborted,start:async a=>{if(P(a)&&!e.supportInputModality("image"))throw new Error("Model does not accept images");if(R(a)&&!e.supportInputModality("audio"))throw new Error("Model does not accept audio");return await e.createChatCompletion({messages:await je(a),...T(n.limits,n.reasoning),abortSignal:n.signal,...t.reasoning?{chat_template_kwargs:{enable_thinking:n.reasoning}}:{}})}})})(f,e,t),unload:async()=>{if(ye==f)await Ee();else try{await f.exit()}catch{}}}},cached:async e=>{try{return!!e.cpu&&(!!await Pe(e)||await Re(await Te(),e))}catch{return!1}},hasCache:async()=>{try{for(const e of l.flatMap(e=>Le(e)))if(await we.stored(e.url))return!0;const e=await Te();return(await e.cacheManager.list()).length>0}catch{return!1}},remove:async e=>{const t=Le(e);try{for(const e of t)await we.remove(e.url)}catch{}try{const e=await Te();await e.cacheManager.deleteMany(e=>t.some(t=>Ce(e,t.name)))}catch{}},purge:async()=>{try{await we.purge()}catch{}try{const e=await Te();await e.cacheManager.clear()}catch{}}},Ae=((e={})=>{const t=e.engines?.cpu||Se,n=e.engines?.litert||he;let a,o,c,d,u,g,m,f=i.none,p=r.idle,h=[],w=0,y={percent:0,phase:s.download};const b=a=>{if(e.backend!=i.none)return e.backend==i.cpu?a.cpu?t:void 0:e.backend==i.gpu?a.litert?n:void 0:a.litert&&"undefined"!=typeof navigator&&navigator.gpu&&"undefined"!=typeof WebAssembly?n:!a.cpu||"undefined"==typeof window&&"undefined"==typeof self&&"undefined"==typeof Worker||"undefined"==typeof WebAssembly?void 0:t},v=(e,t)=>{y={percent:Math.max(0,Math.min(1,e)),phase:t||s.download};for(const e of[...h])e.report(y.percent,y.phase)},x=e=>{p=e},k=()=>{w++,g?.abort(),g=void 0,u=void 0,p==r.downloading&&x(r.idle)},_=(e,t)=>{const n=d;return n?new Promise(a=>{let o=!1;const r={report:e,settle:e=>{o||(o=!0,t?.removeEventListener("abort",i),a(e))}},i=()=>{(e=>{h=h.filter(t=>t!=e),queueMicrotask(()=>{!h.length&&d&&k()})})(r),r.settle(!1)};h.push(r),t?.aborted?i():t?.addEventListener("abort",i,{once:!0}),n.then(r.settle,()=>r.settle(!1))}):Promise.resolve(!1)},M=()=>p==r.ready&&!!a&&!!o;return{load:async({model:e,limits:t,assets:n,logging:i,signal:m,onProgress:p})=>{if(m?.aborted)return!1;if(M()&&o?.id==e.id)return!0;if(d&&u==e.id)return p(y.percent,y.phase),await _(p,m);d&&(k(),h=[],await d.catch(()=>!1));const T=++w,E=(async()=>{await(async()=>{try{if("undefined"==typeof navigator)return;await(navigator.storage?.persist?.())}catch{}})();const d=b(e);if(T!=w)return!1;if(!d)return x(r.unsupported),!1;if(await(async(e,t)=>{if(o&&o.id!=e.id){const e=o,t=c,n=a;a=void 0,o=void 0,c=void 0,await(n?.unload()),await(t?.remove(e))}if(await t.hasCache())for(const n of l)n.id!=e.id&&b(n)==t&&await t.remove(n)})(e,d),T!=w)return!1;const u=await d.cached(e);if(T!=w)return!1;x(r.downloading),v(0,s.download),u&&v(1,s.load);const m=new AbortController;g=m;try{const l=await d.startLLM({model:e,limits:t,logging:i,signal:m.signal,assets:n,onProgress:(e,t)=>{T==w&&v(e,t)}});return T!=w||m.signal.aborted?(await l.unload(),!1):(a=l,o=e,c=d,f=d.backend,x(r.ready),v(1,s.load),!0)}catch(e){return T!=w||(a=void 0,o=void 0,c=void 0,i&&console.log("Local model failed",e),x(r.error)),!1}finally{g==m&&(g=void 0)}})().finally(()=>{d==E&&(d=void 0,u=void 0,h=[])});return d=E,u=e.id,await _(p,m)},chat:async e=>{if(!M()||!a)return;for(;m;)await m.catch(()=>{});if(!M()||!a)return;const t=a.chat(e);m=t;try{return await t}finally{m==t&&(m=void 0)}},unload:async()=>{const e=a;k(),a=void 0,o=void 0,c=void 0,x(r.idle),await(e?.unload())},ready:M,loading:()=>!M()&&p==r.downloading,status:()=>p,backend:()=>f,model:()=>o}})(),We=(e,t,n)=>{const a=e.status(),i=d(t);return{availability:a==r.ready?o.online:a==r.error||a==r.unsupported?o.offline:o.away,status:a,backend:e.backend(),supportsImages:!!i&&m(i),supportsAudio:!!i&&f(i),ready:e.ready(),loading:e.loading(),requesting:n.requesting,phase:n.phase,percent:n.percent,...n.error?{error:n.error}:{}}},Be=(e=>{let t,n=0;const o=t=>t.input.onStatus?.(We(e,t.model??t.input.model,t));return i=>{t?.controller.abort();const c={token:++n,input:i,controller:new AbortController,percent:e.ready()?100:0,phase:e.ready()?s.load:s.download,requesting:!1,cancelled:!1};return t=c,o(c),(async t=>{const{input:r}=t,i=!0===r.logging,c=await g(r.model);if(t.model=c?.id,t.token!=n)return;const l=r.messages.some(e=>e.role!=a.system&&"string"==typeof e.content&&!!e.content.trim()),d=r.messages.some(e=>e.role==a.user&&!!e.images?.some(e=>"string"==typeof e&&!!e)),u=r.messages.some(e=>e.role==a.user&&!!e.audio?.some(e=>"string"==typeof e&&!!e)),p=l||d||u;if(!c)return t.error="Unknown model",void o(t);if(!p)return void o(t);const h=m(c),w=f(c);if(!(l||d&&h||u&&w))return u&&!w?t.error="Model does not accept audio":d&&!h&&(t.error="Model does not accept images"),void o(t);const y=M(r.limits,!!c.litert),b=await e.load({model:c,limits:y,logging:i,signal:t.controller.signal,onProgress:(e,a)=>{t.token==n&&(t.percent=Math.round(100*e),t.phase=a,o(t))}}).catch(e=>(t.token==n&&(t.error=`${e?.message||`${e}`}`),!1));if(t.token!=n)return;if(t.cancelled)return;if(!b)return t.error||(t.error="Model not ready"),void o(t);const v=d&&!h||u&&!w?r.messages.map(({images:e,audio:t,...n})=>({...n,...e&&h?{images:e}:{},...t&&w?{audio:t}:{}})):r.messages;t.requesting=!0,t.error=void 0,t.percent=100,t.phase=s.load,o(t);let x,k="",_="";try{x=await e.chat({messages:v,limits:y,reasoning:!0===r.reasoning,logging:i,onReason:e=>{t.token!=n||t.cancelled||(k+=e,r.onThinking?.(k))},onText:e=>{t.token!=n||t.cancelled||(_+=e,r.onResponse?.(_))},onSpeed:e=>{t.token!=n||t.cancelled||r.onSpeed?.(e)},signal:t.controller.signal})}catch(e){if(t.token!=n)return;return t.requesting=!1,t.error=`${e?.message||`${e}`}`,void o(t)}t.token==n&&(t.requesting=!1,x||t.cancelled||(t.error="No reply"),o(t))})(c),()=>(t=>{t.token==n&&(t.cancelled=!0,t.controller.abort(),e.status()!=r.ready?queueMicrotask(()=>o(t)):e.unload().then(()=>o(t)))})(c)}})(Ae),Ie=(qe=Ae,async(e={})=>{const t=await g(e.model),n=n=>e.onStatus?.(We(qe,t?.id,n));if(!t)return n({requesting:!1,phase:s.download,percent:0,error:"Unknown model"}),!1;let a,o=0,r=s.download;const i=qe.ready()&&qe.model()?.id==t.id;return await qe.load({model:t,limits:M(e.limits,!!t.litert),logging:!0===e.logging,onProgress:(e,t)=>{o=Math.round(100*e),r=t,n({requesting:!1,phase:r,percent:o})}}).catch(e=>(a=`${e?.message||`${e}`}`,!1))?(i&&n({requesting:!1,phase:s.load,percent:100}),!0):(n({requesting:!1,phase:r,percent:o,error:a||"Model not ready"}),!1)});var qe;export{c as ChatModelId,o as ChatStatus,i as LlmBackend,s as LlmPhase,a as LlmRole,r as LlmStatus,Be as createChat,Ie as preloadModel};
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};
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,7 +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,
23
+ supportsImages: (model: ChatModel, mobile?: boolean) => boolean,
18
24
  /** Whether a model accepts audio */
19
- supportsAudio: (model: ChatModel) => boolean;
20
- export { llmModels, cpuContextWindow, litertContextWindow, findLlmModel, gpuAdapter, defaultLlmModel, resolveLlmModel, supportsImages, supportsAudio, };
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:()=>Be,preloadModel:()=>We}),function(e){e.system="system",e.user="user",e.assistant="assistant"}(o||(o={})),function(e){e[e.online=0]="online",e[e.away=1]="away",e[e.offline=2]="offline"}(r||(r={})),function(e){e[e.idle=0]="idle",e[e.downloading=1]="downloading",e[e.ready=2]="ready",e[e.error=3]="error",e[e.unsupported=4]="unsupported"}(i||(i={})),function(e){e.cpu="cpu",e.gpu="gpu",e.none="none"}(s||(s={})),function(e){e.download="download",e.load="load"}(c||(c={})),function(e){e.qwen3_vl_2b="qwen3-vl-2b",e.gemma4_e2b="gemma4-e2b"}(l||(l={}));const d=[{id:l.qwen3_vl_2b,reasoning:!0,cpu:{repo:"Qwen/Qwen3-VL-2B-Thinking-GGUF",file:"Qwen3VL-2B-Thinking-Q4_K_M.gguf",mmproj:{file:"mmproj-Qwen3VL-2B-Thinking-Q8_0.gguf"}}},{id:l.gemma4_e2b,reasoning:!0,litert:{repo:"litert-community/gemma-4-E2B-it-litert-lm",file:"gemma-4-E2B-it.litertlm",vision:!0,audio:!0}}],u=e=>e?d.find(t=>t.id==e):void 0,g=async()=>(await(async()=>{const e="undefined"==typeof navigator?void 0:navigator.gpu;if(!e)return!1;try{return!!await e.requestAdapter({powerPreference:"high-performance"})}catch{return!1}})()?d.find(e=>!!e.litert):d.find(e=>!!e.cpu))?.id,m=async e=>u(e||await g()),f=e=>!!e.cpu?.mmproj||!!e.litert?.vision,p=e=>!!e.cpu?.mmproj?.audio||!!e.litert?.audio,h=require("node:path"),w=()=>{return e=(0,h.join)(__dirname,"../assets/wllama"),{wasm:(0,h.join)(e,"wllama.wasm"),compatWasm:(0,h.join)(e,"compat/wllama.wasm"),compatWorker:(0,h.join)(e,"compat/wllama.js")};var e},y=()=>{return e=(0,h.join)(__dirname,"../assets/litert"),{script:(0,h.join)(e,"litertlm_wasm_internal.js"),wasm:(0,h.join)(e,"litertlm_wasm_internal.wasm")};var e},b=288,v=async e=>{if(!e.startsWith("data:"))return await(await fetch(e)).arrayBuffer();const t=e.indexOf(","),n=t>=0?e.slice(0,t):"";if(t<0)throw new Error("Invalid image data");if(!n.includes(";base64"))return await(await fetch(e)).arrayBuffer();const a=atob(e.slice(t+1)),o=new Uint8Array(a.length);for(let e=0;e<a.length;e++)o[e]=a.charCodeAt(e);return o.buffer},x=async e=>{if(!e||"function"!=typeof fetch||"function"!=typeof createImageBitmap)return e;try{const t=await fetch(e),n=await createImageBitmap(await t.blob()),a=Math.min(n.width,n.height),o=Math.floor((n.width-a)/2),r=Math.floor((n.height-a)/2),i=((e,t)=>{if("undefined"!=typeof OffscreenCanvas)return new OffscreenCanvas(e,t);if("undefined"==typeof document)return;const n=document.createElement("canvas");return n.width=e,n.height=t,n})(b,b),s=i?i.getContext("2d"):null;if(!i||!s||a<1)return n.close(),e;if(s.drawImage(n,o,r,a,a,0,0,b,b),n.close(),(e=>"undefined"!=typeof OffscreenCanvas&&e instanceof OffscreenCanvas)(i)){const t=await i.convertToBlob({type:"image/jpeg",quality:.85});return await(e=>new Promise(t=>{if("undefined"==typeof FileReader)return void t("");const n=new FileReader;n.onerror=()=>t(""),n.onload=()=>t(`${n.result}`),n.readAsDataURL(e)}))(t)||e}return i.toDataURL("image/jpeg",.85)}catch{return e}},k={maxReplyTokens:250,maxThinkingTokens:128,maxReasoningTokens:320,maxImages:2,maxAudio:1,maxContentLength:1e3,promptCharBudget:1e4,maxThreads:6},M=(e,t,n)=>"number"==typeof e&&Number.isFinite(e)&&e>=n?Math.floor(e):t,_=(e={},t=!1)=>{const n=k;return{maxReplyTokens:M(e.maxReplyTokens,n.maxReplyTokens,1),maxThinkingTokens:M(e.maxThinkingTokens,n.maxThinkingTokens,1),maxReasoningTokens:M(e.maxReasoningTokens,t?1200:n.maxReasoningTokens,1),maxImages:M(e.maxImages,n.maxImages,0),maxAudio:M(e.maxAudio,n.maxAudio,0),maxContentLength:M(e.maxContentLength,n.maxContentLength,1),promptCharBudget:M(e.promptCharBudget,n.promptCharBudget,1),maxThreads:M(e.maxThreads,n.maxThreads,1)}},T=(e,t)=>({stream:!0,temperature:.7,max_tokens:t?e.maxReasoningTokens:e.maxReplyTokens}),E=(e,t)=>`https://huggingface.co/${e}/resolve/main/${t}`,C=e=>Number(e.headers.get("content-length"))||0,L=(e,t)=>e.some(e=>Array.isArray(e.content)&&e.content.some(e=>e.type==t)),P=e=>L(e,"image"),j=e=>L(e,"audio"),S=["<|channel>thought\n","<|channel>thought","..."],$=["<channel|>","..."],R=(e,t,n)=>t.length||n.length?[...e?[{type:"text",text:e}]:[],...t.map(e=>({type:"image",url:e})),...n.map(e=>({type:"audio",url:e}))]:e,A=e=>"string"==typeof e?e?[{type:"text",text:e}]:[]:e,q=(e,t)=>{if("string"==typeof e&&"string"==typeof t)return`${e}\n${t}`;const n=[];for(const a of[...A(e),{type:"text",text:"\n"},...A(t)]){const e=n[n.length-1];"text"==a.type&&"text"==e?.type?e.text+=a.text:n.push(a)}return n};class B extends Error{}const W=async(e,t,n)=>{let a;try{return await Promise.race([e,new Promise((e,o)=>{a=setTimeout(()=>o(new B(n)),t)})])}finally{a&&clearTimeout(a)}},I=e=>{try{Promise.resolve(e.return?.()).catch(()=>{})}catch{}},z=async({model:e,logging:t,onReason:n,onText:a,onSpeed:o,start:r,stopped:i,firstTimeoutMs:s=18e4,idleTimeoutMs:c=3e4,onStall:l})=>{let d="",u="",g="",m=!1,f=0,p=0;const h=((e,t)=>{let n="",a=!1;const o=(e,t)=>{let n=-1,a="";for(const o of t){const t=e.indexOf(o);t>=0&&(n<0||t<n)&&(n=t,a=o)}return{at:n,tag:a}},r=(e,t)=>{let n="";for(const a of t)for(let t=Math.min(e.length,a.length-1);t>n.length;t--)if(e.endsWith(a.slice(0,t))){n=e.slice(e.length-t);break}return n};return{feed:i=>{let s=n+i;for(n="";s;){if(a){const{at:t,tag:i}=o(s,$);if(t>=0){t&&e(s.slice(0,t)),s=s.slice(t+i.length),a=!1;continue}const c=r(s,$);e(s.slice(0,s.length-c.length)),n=c;break}{const{at:e,tag:i}=o(s,S);if(e>=0){e&&t(s.slice(0,e)),s=s.slice(e+i.length),s.startsWith("\n")&&(s=s.slice(1)),a=!0;continue}const c=r(s,S);t(s.slice(0,s.length-c.length)),n=c;break}}},flush:()=>{n&&(a?e(n):t(n),n="")}}})(e=>{u+=e,n(e)},e=>{d+=e,a(e)});try{const e=(await W(r(),s,"Model did not start"))[Symbol.asyncIterator]();for(;;){if(i()){I(e);break}let a;try{a=await W(e.next(),m?c:s,m?"Model stopped responding":"Model did not reply")}catch(n){if(n instanceof B&&(d||u)){l?.(),I(e),t&&console.log("Local chat stalled",n);break}throw n}if(a.done)break;m=!0;const r=a.value?.choices?.[0],w=r?.delta,y="string"==typeof w?.reasoning_content?w.reasoning_content:"string"==typeof w?.reasoning?w.reasoning:"";if(r?.finish_reason&&(g=r.finish_reason),y&&(u+=y,n(y)),w?.content&&h.feed(w.content),y||w?.content){const e=performance.now();f||(p=e),f+=1,f>1&&e>p&&o?.(1e3*f/(e-p))}}h.flush()}catch(e){if(t&&console.log("Local chat failed",e),e instanceof B&&!i())throw e}const w=i()?void 0:d||void 0;return t&&(console.log("Local chat reply",e.id,w?"****":""),console.log("Local chat finish",e.id,g||"none",d.length,u.length)),w},O=async({model:e,params:t,start:n,stopped:a,onStall:r})=>{const i=await(async e=>{const t=[];for(const n of e){if("string"==typeof n.content){t.push(n);continue}const e=[];for(const t of n.content)e.push("image"==t.type?{type:"image",url:await x(t.url)}:t);t.push({role:"user",content:e})}return t})(((e,t=k)=>{const n=t.maxContentLength,a=e.filter(e=>"string"==typeof e.content&&!!e.content.trim()||!(e.role!=o.user||!e.images?.length&&!e.audio?.length)),r=[],i=[];let s=0,c=t.maxImages,l=t.maxAudio;for(let e=a.length-1;e>=0;e--){const d=a[e],u=("string"==typeof d.content?d.content:"").slice(0,n),g=d.role==o.user?(d.images||[]).filter(e=>"string"==typeof e&&!!e).slice(0,Math.max(0,c)):[],m=d.role==o.user?(d.audio||[]).filter(e=>"string"==typeof e&&!!e).slice(0,Math.max(0,l)):[];if(u||g.length||m.length){if(u&&s+u.length>t.promptCharBudget)break;s+=u.length,c-=g.length,l-=m.length,d.role==o.system?r.unshift(u):d.role==o.assistant?i.unshift({role:"assistant",content:u}):i.unshift({role:"user",content:R(u,g,m)})}}for(;i.length&&"user"!=i[0].role;)i.shift();const d=[];for(const e of i){const t=d[d.length-1];"assistant"==t?.role&&"assistant"==e.role?t.content=`${t.content}\n${e.content}`:"user"==t?.role&&"user"==e.role?t.content=q(t.content,e.content):"assistant"==e.role?d.push({role:"assistant",content:e.content}):d.push({role:"user",content:e.content})}const u=r.join("\n\n"),g=[];return u&&g.push({role:"system",content:u}),g.push(...d),g})(t.messages,t.limits));return t.logging&&((e,t)=>{console.log("Local chat call",e.id,t.map(({role:e,content:t})=>({role:e,content:"****",...Array.isArray(t)?{images:t.filter(e=>"image"==e.type).length,audio:t.filter(e=>"audio"==e.type).length}:{}})))})(e,i),await z({model:e,logging:t.logging,onReason:t.onReason,onText:t.onText,onSpeed:t.onSpeed,start:()=>n(i),stopped:a,onStall:r})},N=33554432,F=new Map;let U=!1;const D=e=>{const t=(e.split("/").pop()||"model").replace(/[^\w.-]+/g,"_");let n=0;for(let t=0;t<e.length;t++)n=31*n+e.charCodeAt(t)>>>0;return`${n.toString(16)}-${t}`},H=e=>{const t=/^part-(\d+)$/.exec(e);return t?Number(t[1]):-1},Q=async()=>{if(!U)if("undefined"!=typeof navigator&&navigator.storage?.getDirectory)try{return await navigator.storage.getDirectory()}catch{return}else U=!0},G=async(e,t)=>{try{return await e.getDirectoryHandle(t)}catch{return}},V=async e=>{if(e)try{const t=await e.getFile(),n=JSON.parse(await t.text());return{total:Number(n.total)||0,etag:`${n.etag||""}`,complete:!0===n.complete}}catch{return}},J=async(e,t)=>{try{const n=await e.getFileHandle("meta.json",{create:!0}),a=await n.createWritable();await a.write(JSON.stringify(t)),await a.close()}catch{}},K=async function*(e){for await(const t of e)yield t},X=async e=>{try{for await(const[t,n]of K(e))"file"==n.kind&&H(t)>=0&&await e.removeEntry(t).catch(()=>{})}catch{}},Y=async(e,t)=>{try{const n=await(await e.getFileHandle(t)).getFile(),a=await V(await e.getFileHandle(`${t}.json`).catch(()=>{}));if(!a)return;return{file:n,size:n.size,total:a.total,etag:a.etag,complete:a.total?n.size>=a.total:!0===a.complete}}catch{return}},Z=async(e,t)=>{const n=await G(e,t);if(n){const e=await(async e=>await V(await e.getFileHandle("meta.json").catch(()=>{})))(n),t=await(async e=>{const t=[];try{for await(const[n,a]of K(e)){const e=H(n);if("file"!=a.kind||e<0)continue;const o=await a.getFile();t.push({part:{start:e,size:o.size},file:o})}}catch{return}const n=(e=>{const t=[];let n=0;for(const a of[...e].sort((e,t)=>e.start-t.start))if(a.size){if(a.start!=n)break;t.push(a),n+=a.size}return t})(t.map(e=>e.part)),a=new Set(n.map(e=>e.start)),o=t.filter(e=>a.has(e.part.start)).sort((e,t)=>e.part.start-t.part.start).map(e=>e.file);if(o.length)return{file:1==o.length?o[0]:new Blob(o),size:n.reduce((e,t)=>e+t.size,0)}})(n),a=e?.total||0,o=t?.size??0,r=t?.file??new Blob;return{file:a&&o>a?r.slice(0,a):r,size:o,total:a,etag:e?.etag||"",complete:a?o>=a:!0===e?.complete}}return await Y(e,t)},ee=(e,t)=>{const n=e.headers.get("content-range"),a=/bytes (\d+)-/.exec(n||""),o=/\/(\d+)\s*$/.exec(n||""),r=e.headers.get("etag")||"";if(206==e.status){const e=a?Number(a[1]):t.size,n=o?Number(o[1]):t.total;if(e!=t.size)return;if(t.total&&n&&t.total!=n)return;if(t.etag&&r&&t.etag!=r)return;return{start:e,total:n||t.total,etag:r||t.etag}}if(!t.etag||!r||t.etag==r)return{start:0,total:C(e)||t.total,etag:r||t.etag}},te=async({dir:e,start:t,skip:n,total:a,body:o,onProgress:r,onChunk:i})=>{let s,l=t,d=0,u=n;const g=async()=>{const t=await e.getFileHandle((e=>`part-${e}`)(l),{create:!0});s=await t.createWritable(),d=0},m=async()=>{const e=s;s=void 0,e&&await e.close().catch(()=>{})};try{const e=o.getReader();for(;;){const{done:n,value:o}=await e.read();if(n)break;i();let f=0;for(u&&(f=Math.min(u,o.byteLength),u-=f);f<o.byteLength;){s||await g();const e=Math.min(N-d,o.byteLength-f);await s.write({type:"write",position:d,data:o.subarray(f,f+e)}),f+=e,d+=e,l+=e,d==N&&await m()}l>t&&r(a?l/a:0,c.download)}await m()}catch(e){throw await m(),e}return l},ne=e=>["NotAllowedError","NotSupportedError"].includes(e?.name||""),ae=e=>{const t=async()=>{const t=await Q();if(t)try{return await t.getDirectoryHandle(e,{create:!0})}catch{return}};return{stored:async e=>{const n=await t();return n?await Z(n,D(e)):void 0},download:async({url:n,signal:a,onProgress:o})=>{const r=`${e}|${n}`,i=F.get(r);if(i)return await i;const s=await t();if(!s)return;const l=(async(e,t,n,a)=>{const o=D(t),r=await Y(e,o);if(r?.complete)return r;let i=await G(e,o);i||(await e.removeEntry(o).catch(()=>{}),await e.removeEntry(`${o}.json`).catch(()=>{}),i=await e.getDirectoryHandle(o,{create:!0}));let s=0,l=0;for(;;){if(n?.aborted)throw new Error("Cancelled");const r=await Z(e,o);if(r?.complete)return r;const d=r?.size??0,u=r?.total||0,g=r?.etag||"";if(d>s)s=d,l=0;else if(++l>4)throw new Error("Model download failed");d&&u&&a(d/u,c.download);const m=u?Math.min(d+N,u)-1:d+N-1,f=new AbortController,p=()=>f.abort(),h={size:d,total:u,etag:g};let w,y=setTimeout(p,3e4);n?.addEventListener("abort",p,{once:!0});try{if(w=await fetch(t,{signal:f.signal,headers:{Range:`bytes=${d}-${m}`}}),416==w.status){if(await(w.body?.cancel().catch(()=>{})),u&&d>=u){await J(i,{total:u,etag:g,complete:!0});continue}await X(i),await J(i,{total:0,etag:"",complete:!1});continue}if(!w.ok||!w.body)throw await(w.body?.cancel().catch(()=>{})),new Error("Model download failed");const n=ee(w,h);if(!n){await w.body.cancel().catch(()=>{}),await X(i),await J(i,{total:0,etag:"",complete:!1});continue}await J(i,{total:n.total,etag:n.etag,complete:!1});const r=0==n.start?d:0,s=await te({dir:i,start:n.start+r,skip:r,total:n.total,body:w.body,onProgress:a,onChunk:()=>{clearTimeout(y),y=setTimeout(p,3e4)}});if(!n.total){await J(i,{total:s,etag:n.etag,complete:!0});const t=await Z(e,o);if(t)return t}}catch(e){if(n?.aborted)throw new Error("Cancelled");if(e instanceof DOMException&&["QuotaExceededError","NotAllowedError","NotSupportedError","InvalidStateError","TypeMismatchError","SecurityError"].includes(e.name))throw e;await new Promise(e=>setTimeout(e,250+500*l))}finally{clearTimeout(y),n?.removeEventListener("abort",p)}}})(s,n,a,o).finally(()=>{F.delete(r)});return F.set(r,l),await l},remove:async e=>{const n=await t();n&&await(async(e,t)=>{await e.removeEntry(t,{recursive:!0}).catch(()=>{}),await e.removeEntry(`${t}.json`).catch(()=>{})})(n,D(e))},purge:async()=>{const t=await Q();t&&await t.removeEntry(e,{recursive:!0}).catch(()=>{})}}},oe="litertlm-models",re=ae("litert-store"),ie={num_output_candidates:1,wait_for_weight_uploads:!0,num_decode_steps_per_sync:3,sequence_batch_size:0,supported_lora_ranks:[],max_top_k:40,enable_decode_logits:!1,enable_external_embeddings:!1,use_submodel:!1,use_autosized_ringbuffers:!0};let se=!1;const ce=(...e)=>{se&&console.log(...e)},le=(...e)=>{se&&console.error(...e)},de=async()=>{try{if("undefined"==typeof caches)return;return await caches.open(oe)}catch{return}},ue=new Map,ge=(e,t,n)=>{let a=0;return e.pipeThrough(new TransformStream({transform:(e,o)=>{a+=e.byteLength,n(t?a/t:0,c.download),o.enqueue(e)}}))},me=async(e,t=!0)=>{const n=[];for(const a of e){if("string"==typeof a.content){n.push({role:a.role,content:a.content});continue}const e=[];for(const n of a.content)"text"!=n.type?t?e.push({type:n.type,data:await v(n.url)}):e.push({type:"text",text:`[${n.type}]`}):e.push({type:"text",text:n.text});n.push({role:a.role,content:e})}return n},fe=(e,t)=>{if(e.role!=t.role)return!1;if("string"==typeof e.content||"string"==typeof t.content)return e.content==t.content;if(e.content.length!=t.content.length)return!1;for(let n=0;n<e.content.length;n++){const a=e.content[n],o=t.content[n];if(a.type!=o.type)return!1;if("text"==a.type&&"text"==o.type){if(a.text!=o.text)return!1}else if(("image"==a.type&&"image"==o.type||"audio"==a.type&&"audio"==o.type)&&a.url!=o.url)return!1}return!0},pe=async function*({engine:e,mod:t,params:n,messages:a,session:o}){const r=a[a.length-1],i=T(n.limits,n.reasoning);if("user"!=r?.role)throw new Error("No user turn");const s=a.slice(0,-1),c=(await me([r]))[0];if(!c)throw new Error("Empty prompt");const l=!!o.conversation&&o.reasoning==n.reasoning&&o.maxTokens==i.max_tokens&&((e,t)=>{const n="system"==e[0]?.role?e[0]:void 0,a="system"==t[0]?.role?t[0]:void 0;if(!n!=!a)return!1;if(n&&a&&!fe(n,a))return!1;const o=n?e.slice(1):e,r=a?t.slice(1):t;if(!r.length||r.length>o.length)return!1;const i=o.length-r.length;for(let e=0;e<r.length;e++)if(!fe(o[i+e],r[e]))return!1;return!0})(o.sent,s);let d;if(l&&o.conversation)d=o.conversation;else{const a=await me(s,!1);o.conversation=void 0,o.sent=[],d=await e.createConversation({sessionConfig:{maxOutputTokens:i.max_tokens,samplerParams:{type:t.SamplerType.TOP_K,temperature:i.temperature,k:40,p:.95}},preface:{...a.length?{messages:a}:{},extra_context:{enable_thinking:n.reasoning}},prefillPrefaceOnInit:a.length>0}),o.conversation=d,o.reasoning=n.reasoning,o.maxTokens=i.max_tokens,o.sent=[...s]}o.sent.push(r);let u,g=!1,m="";const f=()=>{u&&u.cancel().catch(()=>{});try{d.cancel()}catch{}};n.signal?.addEventListener("abort",f,{once:!0});try{if(n.signal?.aborted)return;const e=d.sendMessageStreaming(c).getReader();for(u=e;;){const{done:t,value:n}=await e.read();if(t)break;const a={};let o="string"==typeof n.content?n.content:"";if(Array.isArray(n.content))for(const e of n.content)"text"==e.type&&(o+=e.text);o&&(a.content=o,m+=o);const r=n.channels?.thought;r&&(a.reasoning=r),(a.content||a.reasoning)&&(yield{choices:[{delta:a}]})}g=!0}finally{n.signal?.removeEventListener("abort",f),g&&m?o.sent.push({role:"assistant",content:m}):(o.conversation=void 0,o.sent=[])}},he={backend:s.gpu,startLLM:async({model:e,limits:t,onProgress:a,signal:o,logging:r})=>{se=!0===r;const i=e.litert;if(!i)throw new Error("Model has no LiteRT artifact");const l=await n.e(520).then(()=>n(507)),d=globalThis,u=d.Module;d.Module={print:ce,printErr:le};try{await l.getOrLoadGlobalLiteRtLm(y().script)}finally{d.Module=u}if(o?.aborted)throw new Error("Cancelled");const g=E(i.repo,i.file),m=await de();let f;const p=m?await m.match(g):void 0;if(p?.body)f=p.body;else{const e=await re.stored(g);e?.complete&&(f=e.file.stream())}if(!f){const e=await re.download({url:g,signal:o,onProgress:a}).catch(e=>{if(o?.aborted)throw new Error("Cancelled");if(!ne(e))throw e;r&&console.log("Local model store failed",e)});e&&(f=e.file.stream())}if(!f&&m)try{await(async(e,t,n,a)=>{const o=ue.get(e);if(o)return await o;const r=(async()=>{const o=await fetch(e,{signal:n});if(!o.ok||!o.body)throw new Error("Model download failed");const r=C(o),i=ge(o.body,r,a);await t.put(e,new Response(i,{headers:{"content-type":"application/octet-stream",...r?{"content-length":`${r}`}:{}}}))})().finally(()=>{ue.delete(e)});return ue.set(e,r),await r})(g,m,o,a);const e=await m.match(g);e?.body&&(f=e.body)}catch(e){if(o?.aborted)throw new Error("Cancelled");r&&console.log("Local model cache failed",e)}if(f||(f=await(async(e,t,n)=>{const a=await fetch(e,{signal:t});if(!a.ok||!a.body)throw new Error("Model download failed");return ge(a.body,C(a),n)})(g,o,a)),o?.aborted)throw new Error("Cancelled");a(1,c.load);const h=await l.Engine.create((({model:e,limits:t,source:n,mod:a})=>({model:n,mainExecutorSettings:{maxNumTokens:4096,...e.litert?.vision?{maxNumImages:Math.max(1,t.maxImages)}:{},backendConfig:ie},...e.litert?.vision?{visionBackend:a.Backend.GPU,maxVisionTokensPerImage:70,minVisionTokensPerImage:70}:{},...e.litert?.audio?{audioBackend:a.Backend.GPU}:{}}))({model:e,limits:t,source:f,mod:l}));if(o?.aborted)throw await h.delete(),new Error("Cancelled");const w={sent:[],reasoning:!1,maxTokens:0};return{model:e,backend:s.gpu,contextSize:4096,chat:t=>(async({engine:e,model:t,mod:n,params:a,session:o})=>{se=!0===a.logging;const r=()=>{try{o.conversation?.cancel()}catch{}o.conversation=void 0,o.sent=[]};try{return await O({model:t,params:a,stopped:()=>!!a.signal?.aborted,onStall:r,start:async r=>{if(a.signal?.aborted)throw new Error("Cancelled");if(P(r)&&!t.litert?.vision)throw new Error("Model does not accept images");if(j(r)&&!t.litert?.audio)throw new Error("Model does not accept audio");return pe({engine:e,mod:n,params:a,messages:r,session:o})}})}catch(e){throw r(),e}})({engine:h,model:e,mod:l,params:t,session:w}),unload:async()=>{w.conversation=void 0,w.sent=[],await h.delete()}}},cached:async e=>{const t=e.litert;if(!t)return!1;const n=E(t.repo,t.file),[a,o]=await Promise.all([de(),re.stored(n)]);if(o?.complete)return!0;try{return!(!a||!await a.match(n))}catch{return!1}},hasCache:async()=>{try{for(const e of d){const t=e.litert;if(!t)continue;if(await re.stored(E(t.repo,t.file)))return!0}const e=await de();return!!e&&(await e.keys()).length>0}catch{return!1}},remove:async e=>{try{const t=e.litert;if(!t)return;const n=E(t.repo,t.file),a=await de();await re.remove(n),a&&await a.delete(n)}catch{}},purge:async()=>{try{await re.purge(),"undefined"!=typeof caches&&await caches.delete(oe)}catch{}}},we=ae("wllama-store");let ye,be,ve="",xe=!1;const ke=e=>`${e.wasm}|${e.compatWasm}|${e.compatWorker}`,Me=e=>{const t=w();return{wasm:e?.wasm||t.wasm,compatWasm:e?.compatWasm||t.compatWasm,compatWorker:e?.compatWorker||t.compatWorker}},_e=e=>{const t=e=>(...t)=>{xe&&("string"==typeof t[0]&&t[0].includes("ggml_webgpu: Failed to get an adapter")||e(...t))};return{...e,log:t(e.log),warn:t(e.warn),error:t(e.error)}},Te=async()=>{if(ye)return ye;const e=await n.e(924).then(()=>n(577)),t=be||Me(),a=new e.Wllama({default:t.wasm},{logger:_e(e.LoggerWithoutDebug)});return be||(be=t,ve=ke(t)),a.setCompat({wasm:t.compatWasm,worker:t.compatWorker}),ye=a,a},Ee=async()=>{const e=ye;ye=void 0;try{await(e?.exit())}catch{}},Ce=(e,t)=>e.name.includes(t)||!!e.metadata?.originalURL?.includes(t),Le=e=>{const t=e.cpu;return t?[{name:t.file,url:E(t.repo,t.file)},...t.mmproj?[{name:t.mmproj.file,url:E(t.repo,t.mmproj.file)}]:[]]:[]},Pe=async e=>{const t=Le(e);if(!t.length)return!1;for(const e of t){const t=await we.stored(e.url);if(!t?.complete)return!1}return!0},je=async(e,t)=>{const n=await e.cacheManager.list();return Le(t).every(e=>n.some(t=>Ce(t,e.name)))},Se=async({model:e,signal:t,onProgress:n})=>{const a=Le(e),o=[];for(let e=0;e<a.length;e++){if(t?.aborted)throw new Error("Cancelled");const r=await we.download({url:a[e].url,signal:t,onProgress:(t,o)=>n((e+t)/a.length,o)});if(!r)return;o.push(r.file)}return o.length?o:void 0},$e=async e=>{const t=[];for(const n of e){if("string"==typeof n.content){"system"==n.role?t.push({role:"system",content:n.content}):"assistant"==n.role?t.push({role:"assistant",content:n.content}):t.push({role:"user",content:n.content});continue}const e=[];for(const t of n.content)"text"==t.type?e.push({type:"text",text:t.text}):e.push({type:t.type,data:await v(t.url)});t.push({role:"user",content:e})}return t},Re={backend:s.cpu,startLLM:async({model:e,limits:t,onProgress:n,signal:a,logging:o,assets:r})=>{xe=!0===o;const i=e.cpu;if(!i)throw new Error("Model has no CPU artifact");const l=Me(r),d=ke(l);ye&&d!=ve&&await Ee(),be=l,ve=d;const u=(g="undefined"!=typeof navigator&&navigator.hardwareConcurrency||4,m=t.maxThreads,Math.max(1,Math.min(Math.max(2,Math.min(8,(g||4)-1)),m)));var g,m;const f=await Te(),p=await Pe(e),h=i.mmproj?.file,w=E(i.repo,i.file),y=h?E(i.repo,h):"";if(a?.aborted)throw await Ee(),new Error("Cancelled");if(!p&&h){const e=await f.cacheManager.list(),t=e.some(e=>Ce(e,i.file)),n=e.some(e=>Ce(e,h));t&&!n&&await f.cacheManager.deleteMany(e=>Ce(e,i.file))}const b={n_ctx:8192,n_threads:u,reasoning_budget_tokens:t.maxThinkingTokens,n_gpu_layers:0,jinja:!0};try{let t=p?await Se({model:e,signal:a,onProgress:n}):void 0;t||p||await je(f,e).catch(()=>!1)||(t=await Se({model:e,signal:a,onProgress:n}).catch(e=>{if(a?.aborted)throw new Error("Cancelled");if(!ne(e))throw e;o&&console.log("Local model store failed",e)})),t?(n(1,c.load),await f.loadModel(t,b)):await f.loadModelFromUrl(y?{url:w,mmprojUrl:y}:w,{...b,useCache:!0,signal:a,progressCallback:({loaded:e,total:t})=>{const a=t?e/t:0;n(a,a>=1?c.load:c.download)}})}catch(e){throw await Ee(),e}if(a?.aborted)throw await Ee(),new Error("Cancelled");if(!f.getLoadedContextInfo().n_ctx)throw await Ee(),new Error("Failed to create model context");return{model:e,backend:s.cpu,contextSize:8192,chat:t=>(async(e,t,n)=>{if(xe=!0===n.logging,e.isModelLoaded())return await O({model:t,params:n,stopped:()=>!!n.signal?.aborted,start:async a=>{if(P(a)&&!e.supportInputModality("image"))throw new Error("Model does not accept images");if(j(a)&&!e.supportInputModality("audio"))throw new Error("Model does not accept audio");return await e.createChatCompletion({messages:await $e(a),...T(n.limits,n.reasoning),abortSignal:n.signal,...t.reasoning?{chat_template_kwargs:{enable_thinking:n.reasoning}}:{}})}})})(f,e,t),unload:async()=>{if(ye==f)await Ee();else try{await f.exit()}catch{}}}},cached:async e=>{try{return!!e.cpu&&(!!await Pe(e)||await je(await Te(),e))}catch{return!1}},hasCache:async()=>{try{for(const e of d.flatMap(e=>Le(e)))if(await we.stored(e.url))return!0;const e=await Te();return(await e.cacheManager.list()).length>0}catch{return!1}},remove:async e=>{const t=Le(e);try{for(const e of t)await we.remove(e.url)}catch{}try{const e=await Te();await e.cacheManager.deleteMany(e=>t.some(t=>Ce(e,t.name)))}catch{}},purge:async()=>{try{await we.purge()}catch{}try{const e=await Te();await e.cacheManager.clear()}catch{}}},Ae=((e={})=>{const t=e.engines?.cpu||Re,n=e.engines?.litert||he;let a,o,r,l,u,g,m,f=s.none,p=i.idle,h=[],w=0,y={percent:0,phase:c.download};const b=a=>{if(e.backend!=s.none)return e.backend==s.cpu?a.cpu?t:void 0:e.backend==s.gpu?a.litert?n:void 0:a.litert&&"undefined"!=typeof navigator&&navigator.gpu&&"undefined"!=typeof WebAssembly?n:!a.cpu||"undefined"==typeof window&&"undefined"==typeof self&&"undefined"==typeof Worker||"undefined"==typeof WebAssembly?void 0:t},v=(e,t)=>{y={percent:Math.max(0,Math.min(1,e)),phase:t||c.download};for(const e of[...h])e.report(y.percent,y.phase)},x=e=>{p=e},k=()=>{w++,g?.abort(),g=void 0,u=void 0,p==i.downloading&&x(i.idle)},M=(e,t)=>{const n=l;return n?new Promise(a=>{let o=!1;const r={report:e,settle:e=>{o||(o=!0,t?.removeEventListener("abort",i),a(e))}},i=()=>{(e=>{h=h.filter(t=>t!=e),queueMicrotask(()=>{!h.length&&l&&k()})})(r),r.settle(!1)};h.push(r),t?.aborted?i():t?.addEventListener("abort",i,{once:!0}),n.then(r.settle,()=>r.settle(!1))}):Promise.resolve(!1)},_=()=>p==i.ready&&!!a&&!!o;return{load:async({model:e,limits:t,assets:n,logging:s,signal:m,onProgress:p})=>{if(m?.aborted)return!1;if(_()&&o?.id==e.id)return!0;if(l&&u==e.id)return p(y.percent,y.phase),await M(p,m);l&&(k(),h=[],await l.catch(()=>!1));const T=++w,E=(async()=>{await(async()=>{try{if("undefined"==typeof navigator)return;await(navigator.storage?.persist?.())}catch{}})();const l=b(e);if(T!=w)return!1;if(!l)return x(i.unsupported),!1;if(await(async(e,t)=>{if(o&&o.id!=e.id){const e=o,t=r,n=a;a=void 0,o=void 0,r=void 0,await(n?.unload()),await(t?.remove(e))}if(await t.hasCache())for(const n of d)n.id!=e.id&&b(n)==t&&await t.remove(n)})(e,l),T!=w)return!1;const u=await l.cached(e);if(T!=w)return!1;x(i.downloading),v(0,c.download),u&&v(1,c.load);const m=new AbortController;g=m;try{const d=await l.startLLM({model:e,limits:t,logging:s,signal:m.signal,assets:n,onProgress:(e,t)=>{T==w&&v(e,t)}});return T!=w||m.signal.aborted?(await d.unload(),!1):(a=d,o=e,r=l,f=l.backend,x(i.ready),v(1,c.load),!0)}catch(e){return T!=w||(a=void 0,o=void 0,r=void 0,s&&console.log("Local model failed",e),x(i.error)),!1}finally{g==m&&(g=void 0)}})().finally(()=>{l==E&&(l=void 0,u=void 0,h=[])});return l=E,u=e.id,await M(p,m)},chat:async e=>{if(!_()||!a)return;for(;m;)await m.catch(()=>{});if(!_()||!a)return;const t=a.chat(e);m=t;try{return await t}finally{m==t&&(m=void 0)}},unload:async()=>{const e=a;k(),a=void 0,o=void 0,r=void 0,x(i.idle),await(e?.unload())},ready:_,loading:()=>!_()&&p==i.downloading,status:()=>p,backend:()=>f,model:()=>o}})(),qe=(e,t,n)=>{const a=e.status(),o=u(t);return{availability:a==i.ready?r.online:a==i.error||a==i.unsupported?r.offline:r.away,status:a,backend:e.backend(),supportsImages:!!o&&f(o),supportsAudio:!!o&&p(o),ready:e.ready(),loading:e.loading(),requesting:n.requesting,phase:n.phase,percent:n.percent,...n.error?{error:n.error}:{}}},Be=(e=>{let t,n=0;const a=t=>t.input.onStatus?.(qe(e,t.model??t.input.model,t));return r=>{t?.controller.abort();const s={token:++n,input:r,controller:new AbortController,percent:e.ready()?100:0,phase:e.ready()?c.load:c.download,requesting:!1,cancelled:!1};return t=s,a(s),(async t=>{const{input:r}=t,i=!0===r.logging,s=await m(r.model);if(t.model=s?.id,t.token!=n)return;const l=r.messages.some(e=>e.role!=o.system&&"string"==typeof e.content&&!!e.content.trim()),d=r.messages.some(e=>e.role==o.user&&!!e.images?.some(e=>"string"==typeof e&&!!e)),u=r.messages.some(e=>e.role==o.user&&!!e.audio?.some(e=>"string"==typeof e&&!!e)),g=l||d||u;if(!s)return t.error="Unknown model",void a(t);if(!g)return void a(t);const h=f(s),w=p(s);if(!(l||d&&h||u&&w))return u&&!w?t.error="Model does not accept audio":d&&!h&&(t.error="Model does not accept images"),void a(t);const y=_(r.limits,!!s.litert),b=await e.load({model:s,limits:y,logging:i,signal:t.controller.signal,onProgress:(e,o)=>{t.token==n&&(t.percent=Math.round(100*e),t.phase=o,a(t))}}).catch(e=>(t.token==n&&(t.error=`${e?.message||`${e}`}`),!1));if(t.token!=n)return;if(t.cancelled)return;if(!b)return t.error||(t.error="Model not ready"),void a(t);const v=d&&!h||u&&!w?r.messages.map(({images:e,audio:t,...n})=>({...n,...e&&h?{images:e}:{},...t&&w?{audio:t}:{}})):r.messages;t.requesting=!0,t.error=void 0,t.percent=100,t.phase=c.load,a(t);let x,k="",M="";try{x=await e.chat({messages:v,limits:y,reasoning:!0===r.reasoning,logging:i,onReason:e=>{t.token!=n||t.cancelled||(k+=e,r.onThinking?.(k))},onText:e=>{t.token!=n||t.cancelled||(M+=e,r.onResponse?.(M))},onSpeed:e=>{t.token!=n||t.cancelled||r.onSpeed?.(e)},signal:t.controller.signal})}catch(e){if(t.token!=n)return;return t.requesting=!1,t.error=`${e?.message||`${e}`}`,void a(t)}t.token==n&&(t.requesting=!1,x||t.cancelled||(t.error="No reply"),a(t))})(s),()=>(t=>{t.token==n&&(t.cancelled=!0,t.controller.abort(),e.status()!=i.ready?queueMicrotask(()=>a(t)):e.unload().then(()=>a(t)))})(s)}})(Ae),We=(Ie=Ae,async(e={})=>{const t=await m(e.model),n=n=>e.onStatus?.(qe(Ie,t?.id,n));if(!t)return n({requesting:!1,phase:c.download,percent:0,error:"Unknown model"}),!1;let a,o=0,r=c.download;const i=Ie.ready()&&Ie.model()?.id==t.id;return await Ie.load({model:t,limits:_(e.limits,!!t.litert),logging:!0===e.logging,onProgress:(e,t)=>{o=Math.round(100*e),r=t,n({requesting:!1,phase:r,percent:o})}}).catch(e=>(a=`${e?.message||`${e}`}`,!1))?(i&&n({requesting:!1,phase:c.load,percent:100}),!0):(n({requesting:!1,phase:r,percent:o,error:a||"Model not ready"}),!1)});var Ie;module.exports=a})();
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 i,s,c,l,d,u;r.m=a,r.d=(e,t)=>{for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.f={},r.e=e=>{const t=[];return r.f.j(e,t),Promise.all(t)},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r.r=e=>{Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{const e={520:()=>import("./engine-litert.node.min.mjs"),924:()=>import("./engine-wllama.node.min.mjs")},t={792:0},n=e=>{let{__webpack_esm_ids__:n,__webpack_esm_modules__:a,__webpack_esm_runtime__:o}=e;var i,s,c=0;for(i in a)r.o(a,i)&&(r.m[i]=a[i]);for(o&&o(r);c<n.length;c++)s=n[c],r.o(t,s)&&t[s]&&t[s][0](),t[s]=0};r.f.j=(a,o)=>{let i=r.o(t,a)?t[a]:void 0;if(0!==i)if(i)o.push(i[1]);else{let r=e[a]().then(n,e=>{throw 0!==t[a]&&(t[a]=void 0),e});r=Promise.race([r,new Promise(e=>i=t[a]=[e])]),o.push(i[1]=r)}}})(),function(e){e.system="system",e.user="user",e.assistant="assistant"}(i||(i={})),function(e){e[e.online=0]="online",e[e.away=1]="away",e[e.offline=2]="offline"}(s||(s={})),function(e){e[e.idle=0]="idle",e[e.downloading=1]="downloading",e[e.ready=2]="ready",e[e.error=3]="error",e[e.unsupported=4]="unsupported"}(c||(c={})),function(e){e.cpu="cpu",e.gpu="gpu",e.none="none"}(l||(l={})),function(e){e.download="download",e.load="load"}(d||(d={})),function(e){e.qwen3_vl_2b="qwen3-vl-2b",e.gemma4_e2b="gemma4-e2b"}(u||(u={}));const g=[{id:u.qwen3_vl_2b,reasoning:!0,cpu:{repo:"Qwen/Qwen3-VL-2B-Thinking-GGUF",file:"Qwen3VL-2B-Thinking-Q4_K_M.gguf",mmproj:{file:"mmproj-Qwen3VL-2B-Thinking-Q8_0.gguf"}}},{id:u.gemma4_e2b,reasoning:!0,litert:{repo:"litert-community/gemma-4-E2B-it-litert-lm",file:"gemma-4-E2B-it.litertlm",vision:!0,audio:!0}}],m=e=>e?g.find(t=>t.id==e):void 0,f=async()=>(await(async()=>{const e="undefined"==typeof navigator?void 0:navigator.gpu;if(!e)return!1;try{return!!await e.requestAdapter({powerPreference:"high-performance"})}catch{return!1}})()?g.find(e=>!!e.litert):g.find(e=>!!e.cpu))?.id,p=async e=>m(e||await f()),h=e=>!!e.cpu?.mmproj||!!e.litert?.vision,w=e=>!!e.cpu?.mmproj?.audio||!!e.litert?.audio,y=()=>{return a=t(e(n("file:///media/x/store/code/DegreeSign/ds_chat/src/assets/nodeEsm.js")),"../assets/wllama"),{wasm:t(a,"wllama.wasm"),compatWasm:t(a,"compat/wllama.wasm"),compatWorker:t(a,"compat/wllama.js")};var a},b=()=>{return a=t(e(n("file:///media/x/store/code/DegreeSign/ds_chat/src/assets/nodeEsm.js")),"../assets/litert"),{script:t(a,"litertlm_wasm_internal.js"),wasm:t(a,"litertlm_wasm_internal.wasm")};var a},v=288,x=async e=>{if(!e.startsWith("data:"))return await(await fetch(e)).arrayBuffer();const t=e.indexOf(","),n=t>=0?e.slice(0,t):"";if(t<0)throw new Error("Invalid image data");if(!n.includes(";base64"))return await(await fetch(e)).arrayBuffer();const a=atob(e.slice(t+1)),o=new Uint8Array(a.length);for(let e=0;e<a.length;e++)o[e]=a.charCodeAt(e);return o.buffer},k=async e=>{if(!e||"function"!=typeof fetch||"function"!=typeof createImageBitmap)return e;try{const t=await fetch(e),n=await createImageBitmap(await t.blob()),a=Math.min(n.width,n.height),o=Math.floor((n.width-a)/2),r=Math.floor((n.height-a)/2),i=((e,t)=>{if("undefined"!=typeof OffscreenCanvas)return new OffscreenCanvas(e,t);if("undefined"==typeof document)return;const n=document.createElement("canvas");return n.width=e,n.height=t,n})(v,v),s=i?i.getContext("2d"):null;if(!i||!s||a<1)return n.close(),e;if(s.drawImage(n,o,r,a,a,0,0,v,v),n.close(),(e=>"undefined"!=typeof OffscreenCanvas&&e instanceof OffscreenCanvas)(i)){const t=await i.convertToBlob({type:"image/jpeg",quality:.85});return await(e=>new Promise(t=>{if("undefined"==typeof FileReader)return void t("");const n=new FileReader;n.onerror=()=>t(""),n.onload=()=>t(`${n.result}`),n.readAsDataURL(e)}))(t)||e}return i.toDataURL("image/jpeg",.85)}catch{return e}},_={maxReplyTokens:250,maxThinkingTokens:128,maxReasoningTokens:320,maxImages:2,maxAudio:1,maxContentLength:1e3,promptCharBudget:1e4,maxThreads:6},M=(e,t,n)=>"number"==typeof e&&Number.isFinite(e)&&e>=n?Math.floor(e):t,T=(e={},t=!1)=>{const n=_;return{maxReplyTokens:M(e.maxReplyTokens,n.maxReplyTokens,1),maxThinkingTokens:M(e.maxThinkingTokens,n.maxThinkingTokens,1),maxReasoningTokens:M(e.maxReasoningTokens,t?1200:n.maxReasoningTokens,1),maxImages:M(e.maxImages,n.maxImages,0),maxAudio:M(e.maxAudio,n.maxAudio,0),maxContentLength:M(e.maxContentLength,n.maxContentLength,1),promptCharBudget:M(e.promptCharBudget,n.promptCharBudget,1),maxThreads:M(e.maxThreads,n.maxThreads,1)}},E=(e,t)=>({stream:!0,temperature:.7,max_tokens:t?e.maxReasoningTokens:e.maxReplyTokens}),C=(e,t)=>`https://huggingface.co/${e}/resolve/main/${t}`,L=e=>Number(e.headers.get("content-length"))||0,P=(e,t)=>e.some(e=>Array.isArray(e.content)&&e.content.some(e=>e.type==t)),j=e=>P(e,"image"),S=e=>P(e,"audio"),$=["<|channel>thought\n","<|channel>thought","..."],R=["<channel|>","..."],A=(e,t,n)=>t.length||n.length?[...e?[{type:"text",text:e}]:[],...t.map(e=>({type:"image",url:e})),...n.map(e=>({type:"audio",url:e}))]:e,W=e=>"string"==typeof e?e?[{type:"text",text:e}]:[]:e,B=(e,t)=>{if("string"==typeof e&&"string"==typeof t)return`${e}\n${t}`;const n=[];for(const a of[...W(e),{type:"text",text:"\n"},...W(t)]){const e=n[n.length-1];"text"==a.type&&"text"==e?.type?e.text+=a.text:n.push(a)}return n};class I extends Error{}const q=async(e,t,n)=>{let a;try{return await Promise.race([e,new Promise((e,o)=>{a=setTimeout(()=>o(new I(n)),t)})])}finally{a&&clearTimeout(a)}},z=e=>{try{Promise.resolve(e.return?.()).catch(()=>{})}catch{}},O=async({model:e,logging:t,onReason:n,onText:a,onSpeed:o,start:r,stopped:i,firstTimeoutMs:s=18e4,idleTimeoutMs:c=3e4,onStall:l})=>{let d="",u="",g="",m=!1,f=0,p=0;const h=((e,t)=>{let n="",a=!1;const o=(e,t)=>{let n=-1,a="";for(const o of t){const t=e.indexOf(o);t>=0&&(n<0||t<n)&&(n=t,a=o)}return{at:n,tag:a}},r=(e,t)=>{let n="";for(const a of t)for(let t=Math.min(e.length,a.length-1);t>n.length;t--)if(e.endsWith(a.slice(0,t))){n=e.slice(e.length-t);break}return n};return{feed:i=>{let s=n+i;for(n="";s;){if(a){const{at:t,tag:i}=o(s,R);if(t>=0){t&&e(s.slice(0,t)),s=s.slice(t+i.length),a=!1;continue}const c=r(s,R);e(s.slice(0,s.length-c.length)),n=c;break}{const{at:e,tag:i}=o(s,$);if(e>=0){e&&t(s.slice(0,e)),s=s.slice(e+i.length),s.startsWith("\n")&&(s=s.slice(1)),a=!0;continue}const c=r(s,$);t(s.slice(0,s.length-c.length)),n=c;break}}},flush:()=>{n&&(a?e(n):t(n),n="")}}})(e=>{u+=e,n(e)},e=>{d+=e,a(e)});try{const e=(await q(r(),s,"Model did not start"))[Symbol.asyncIterator]();for(;;){if(i()){z(e);break}let a;try{a=await q(e.next(),m?c:s,m?"Model stopped responding":"Model did not reply")}catch(n){if(n instanceof I&&(d||u)){l?.(),z(e),t&&console.log("Local chat stalled",n);break}throw n}if(a.done)break;m=!0;const r=a.value?.choices?.[0],w=r?.delta,y="string"==typeof w?.reasoning_content?w.reasoning_content:"string"==typeof w?.reasoning?w.reasoning:"";if(r?.finish_reason&&(g=r.finish_reason),y&&(u+=y,n(y)),w?.content&&h.feed(w.content),y||w?.content){const e=performance.now();f||(p=e),f+=1,f>1&&e>p&&o?.(1e3*f/(e-p))}}h.flush()}catch(e){if(t&&console.log("Local chat failed",e),e instanceof I&&!i())throw e}const w=i()?void 0:d||void 0;return t&&(console.log("Local chat reply",e.id,w?"****":""),console.log("Local chat finish",e.id,g||"none",d.length,u.length)),w},N=async({model:e,params:t,start:n,stopped:a,onStall:o})=>{const r=await(async e=>{const t=[];for(const n of e){if("string"==typeof n.content){t.push(n);continue}const e=[];for(const t of n.content)e.push("image"==t.type?{type:"image",url:await k(t.url)}:t);t.push({role:"user",content:e})}return t})(((e,t=_)=>{const n=t.maxContentLength,a=e.filter(e=>"string"==typeof e.content&&!!e.content.trim()||!(e.role!=i.user||!e.images?.length&&!e.audio?.length)),o=[],r=[];let s=0,c=t.maxImages,l=t.maxAudio;for(let e=a.length-1;e>=0;e--){const d=a[e],u=("string"==typeof d.content?d.content:"").slice(0,n),g=d.role==i.user?(d.images||[]).filter(e=>"string"==typeof e&&!!e).slice(0,Math.max(0,c)):[],m=d.role==i.user?(d.audio||[]).filter(e=>"string"==typeof e&&!!e).slice(0,Math.max(0,l)):[];if(u||g.length||m.length){if(u&&s+u.length>t.promptCharBudget)break;s+=u.length,c-=g.length,l-=m.length,d.role==i.system?o.unshift(u):d.role==i.assistant?r.unshift({role:"assistant",content:u}):r.unshift({role:"user",content:A(u,g,m)})}}for(;r.length&&"user"!=r[0].role;)r.shift();const d=[];for(const e of r){const t=d[d.length-1];"assistant"==t?.role&&"assistant"==e.role?t.content=`${t.content}\n${e.content}`:"user"==t?.role&&"user"==e.role?t.content=B(t.content,e.content):"assistant"==e.role?d.push({role:"assistant",content:e.content}):d.push({role:"user",content:e.content})}const u=o.join("\n\n"),g=[];return u&&g.push({role:"system",content:u}),g.push(...d),g})(t.messages,t.limits));return t.logging&&((e,t)=>{console.log("Local chat call",e.id,t.map(({role:e,content:t})=>({role:e,content:"****",...Array.isArray(t)?{images:t.filter(e=>"image"==e.type).length,audio:t.filter(e=>"audio"==e.type).length}:{}})))})(e,r),await O({model:e,logging:t.logging,onReason:t.onReason,onText:t.onText,onSpeed:t.onSpeed,start:()=>n(r),stopped:a,onStall:o})},F=33554432,U=new Map;let D=!1;const H=e=>{const t=(e.split("/").pop()||"model").replace(/[^\w.-]+/g,"_");let n=0;for(let t=0;t<e.length;t++)n=31*n+e.charCodeAt(t)>>>0;return`${n.toString(16)}-${t}`},Q=e=>{const t=/^part-(\d+)$/.exec(e);return t?Number(t[1]):-1},G=async()=>{if(!D)if("undefined"!=typeof navigator&&navigator.storage?.getDirectory)try{return await navigator.storage.getDirectory()}catch{return}else D=!0},V=async(e,t)=>{try{return await e.getDirectoryHandle(t)}catch{return}},J=async e=>{if(e)try{const t=await e.getFile(),n=JSON.parse(await t.text());return{total:Number(n.total)||0,etag:`${n.etag||""}`,complete:!0===n.complete}}catch{return}},K=async(e,t)=>{try{const n=await e.getFileHandle("meta.json",{create:!0}),a=await n.createWritable();await a.write(JSON.stringify(t)),await a.close()}catch{}},X=async function*(e){for await(const t of e)yield t},Y=async e=>{try{for await(const[t,n]of X(e))"file"==n.kind&&Q(t)>=0&&await e.removeEntry(t).catch(()=>{})}catch{}},Z=async(e,t)=>{try{const n=await(await e.getFileHandle(t)).getFile(),a=await J(await e.getFileHandle(`${t}.json`).catch(()=>{}));if(!a)return;return{file:n,size:n.size,total:a.total,etag:a.etag,complete:a.total?n.size>=a.total:!0===a.complete}}catch{return}},ee=async(e,t)=>{const n=await V(e,t);if(n){const e=await(async e=>await J(await e.getFileHandle("meta.json").catch(()=>{})))(n),t=await(async e=>{const t=[];try{for await(const[n,a]of X(e)){const e=Q(n);if("file"!=a.kind||e<0)continue;const o=await a.getFile();t.push({part:{start:e,size:o.size},file:o})}}catch{return}const n=(e=>{const t=[];let n=0;for(const a of[...e].sort((e,t)=>e.start-t.start))if(a.size){if(a.start!=n)break;t.push(a),n+=a.size}return t})(t.map(e=>e.part)),a=new Set(n.map(e=>e.start)),o=t.filter(e=>a.has(e.part.start)).sort((e,t)=>e.part.start-t.part.start).map(e=>e.file);if(o.length)return{file:1==o.length?o[0]:new Blob(o),size:n.reduce((e,t)=>e+t.size,0)}})(n),a=e?.total||0,o=t?.size??0,r=t?.file??new Blob;return{file:a&&o>a?r.slice(0,a):r,size:o,total:a,etag:e?.etag||"",complete:a?o>=a:!0===e?.complete}}return await Z(e,t)},te=(e,t)=>{const n=e.headers.get("content-range"),a=/bytes (\d+)-/.exec(n||""),o=/\/(\d+)\s*$/.exec(n||""),r=e.headers.get("etag")||"";if(206==e.status){const e=a?Number(a[1]):t.size,n=o?Number(o[1]):t.total;if(e!=t.size)return;if(t.total&&n&&t.total!=n)return;if(t.etag&&r&&t.etag!=r)return;return{start:e,total:n||t.total,etag:r||t.etag}}if(!t.etag||!r||t.etag==r)return{start:0,total:L(e)||t.total,etag:r||t.etag}},ne=async({dir:e,start:t,skip:n,total:a,body:o,onProgress:r,onChunk:i})=>{let s,c=t,l=0,u=n;const g=async()=>{const t=await e.getFileHandle((e=>`part-${e}`)(c),{create:!0});s=await t.createWritable(),l=0},m=async()=>{const e=s;s=void 0,e&&await e.close().catch(()=>{})};try{const e=o.getReader();for(;;){const{done:n,value:o}=await e.read();if(n)break;i();let f=0;for(u&&(f=Math.min(u,o.byteLength),u-=f);f<o.byteLength;){s||await g();const e=Math.min(F-l,o.byteLength-f);await s.write({type:"write",position:l,data:o.subarray(f,f+e)}),f+=e,l+=e,c+=e,l==F&&await m()}c>t&&r(a?c/a:0,d.download)}await m()}catch(e){throw await m(),e}return c},ae=e=>["NotAllowedError","NotSupportedError"].includes(e?.name||""),oe=e=>{const t=async()=>{const t=await G();if(t)try{return await t.getDirectoryHandle(e,{create:!0})}catch{return}};return{stored:async e=>{const n=await t();return n?await ee(n,H(e)):void 0},download:async({url:n,signal:a,onProgress:o})=>{const r=`${e}|${n}`,i=U.get(r);if(i)return await i;const s=await t();if(!s)return;const c=(async(e,t,n,a)=>{const o=H(t),r=await Z(e,o);if(r?.complete)return r;let i=await V(e,o);i||(await e.removeEntry(o).catch(()=>{}),await e.removeEntry(`${o}.json`).catch(()=>{}),i=await e.getDirectoryHandle(o,{create:!0}));let s=0,c=0;for(;;){if(n?.aborted)throw new Error("Cancelled");const r=await ee(e,o);if(r?.complete)return r;const l=r?.size??0,u=r?.total||0,g=r?.etag||"";if(l>s)s=l,c=0;else if(++c>4)throw new Error("Model download failed");l&&u&&a(l/u,d.download);const m=u?Math.min(l+F,u)-1:l+F-1,f=new AbortController,p=()=>f.abort(),h={size:l,total:u,etag:g};let w,y=setTimeout(p,3e4);n?.addEventListener("abort",p,{once:!0});try{if(w=await fetch(t,{signal:f.signal,headers:{Range:`bytes=${l}-${m}`}}),416==w.status){if(await(w.body?.cancel().catch(()=>{})),u&&l>=u){await K(i,{total:u,etag:g,complete:!0});continue}await Y(i),await K(i,{total:0,etag:"",complete:!1});continue}if(!w.ok||!w.body)throw await(w.body?.cancel().catch(()=>{})),new Error("Model download failed");const n=te(w,h);if(!n){await w.body.cancel().catch(()=>{}),await Y(i),await K(i,{total:0,etag:"",complete:!1});continue}await K(i,{total:n.total,etag:n.etag,complete:!1});const r=0==n.start?l:0,s=await ne({dir:i,start:n.start+r,skip:r,total:n.total,body:w.body,onProgress:a,onChunk:()=>{clearTimeout(y),y=setTimeout(p,3e4)}});if(!n.total){await K(i,{total:s,etag:n.etag,complete:!0});const t=await ee(e,o);if(t)return t}}catch(e){if(n?.aborted)throw new Error("Cancelled");if(e instanceof DOMException&&["QuotaExceededError","NotAllowedError","NotSupportedError","InvalidStateError","TypeMismatchError","SecurityError"].includes(e.name))throw e;await new Promise(e=>setTimeout(e,250+500*c))}finally{clearTimeout(y),n?.removeEventListener("abort",p)}}})(s,n,a,o).finally(()=>{U.delete(r)});return U.set(r,c),await c},remove:async e=>{const n=await t();n&&await(async(e,t)=>{await e.removeEntry(t,{recursive:!0}).catch(()=>{}),await e.removeEntry(`${t}.json`).catch(()=>{})})(n,H(e))},purge:async()=>{const t=await G();t&&await t.removeEntry(e,{recursive:!0}).catch(()=>{})}}},re="litertlm-models",ie=oe("litert-store"),se={num_output_candidates:1,wait_for_weight_uploads:!0,num_decode_steps_per_sync:3,sequence_batch_size:0,supported_lora_ranks:[],max_top_k:40,enable_decode_logits:!1,enable_external_embeddings:!1,use_submodel:!1,use_autosized_ringbuffers:!0};let ce=!1;const le=(...e)=>{ce&&console.log(...e)},de=(...e)=>{ce&&console.error(...e)},ue=async()=>{try{if("undefined"==typeof caches)return;return await caches.open(re)}catch{return}},ge=new Map,me=(e,t,n)=>{let a=0;return e.pipeThrough(new TransformStream({transform:(e,o)=>{a+=e.byteLength,n(t?a/t:0,d.download),o.enqueue(e)}}))},fe=async(e,t=!0)=>{const n=[];for(const a of e){if("string"==typeof a.content){n.push({role:a.role,content:a.content});continue}const e=[];for(const n of a.content)"text"!=n.type?t?e.push({type:n.type,data:await x(n.url)}):e.push({type:"text",text:`[${n.type}]`}):e.push({type:"text",text:n.text});n.push({role:a.role,content:e})}return n},pe=(e,t)=>{if(e.role!=t.role)return!1;if("string"==typeof e.content||"string"==typeof t.content)return e.content==t.content;if(e.content.length!=t.content.length)return!1;for(let n=0;n<e.content.length;n++){const a=e.content[n],o=t.content[n];if(a.type!=o.type)return!1;if("text"==a.type&&"text"==o.type){if(a.text!=o.text)return!1}else if(("image"==a.type&&"image"==o.type||"audio"==a.type&&"audio"==o.type)&&a.url!=o.url)return!1}return!0},he=async function*({engine:e,mod:t,params:n,messages:a,session:o}){const r=a[a.length-1],i=E(n.limits,n.reasoning);if("user"!=r?.role)throw new Error("No user turn");const s=a.slice(0,-1),c=(await fe([r]))[0];if(!c)throw new Error("Empty prompt");const l=!!o.conversation&&o.reasoning==n.reasoning&&o.maxTokens==i.max_tokens&&((e,t)=>{const n="system"==e[0]?.role?e[0]:void 0,a="system"==t[0]?.role?t[0]:void 0;if(!n!=!a)return!1;if(n&&a&&!pe(n,a))return!1;const o=n?e.slice(1):e,r=a?t.slice(1):t;if(!r.length||r.length>o.length)return!1;const i=o.length-r.length;for(let e=0;e<r.length;e++)if(!pe(o[i+e],r[e]))return!1;return!0})(o.sent,s);let d;if(l&&o.conversation)d=o.conversation;else{const a=await fe(s,!1);o.conversation=void 0,o.sent=[],d=await e.createConversation({sessionConfig:{maxOutputTokens:i.max_tokens,samplerParams:{type:t.SamplerType.TOP_K,temperature:i.temperature,k:40,p:.95}},preface:{...a.length?{messages:a}:{},extra_context:{enable_thinking:n.reasoning}},prefillPrefaceOnInit:a.length>0}),o.conversation=d,o.reasoning=n.reasoning,o.maxTokens=i.max_tokens,o.sent=[...s]}o.sent.push(r);let u,g=!1,m="";const f=()=>{u&&u.cancel().catch(()=>{});try{d.cancel()}catch{}};n.signal?.addEventListener("abort",f,{once:!0});try{if(n.signal?.aborted)return;const e=d.sendMessageStreaming(c).getReader();for(u=e;;){const{done:t,value:n}=await e.read();if(t)break;const a={};let o="string"==typeof n.content?n.content:"";if(Array.isArray(n.content))for(const e of n.content)"text"==e.type&&(o+=e.text);o&&(a.content=o,m+=o);const r=n.channels?.thought;r&&(a.reasoning=r),(a.content||a.reasoning)&&(yield{choices:[{delta:a}]})}g=!0}finally{n.signal?.removeEventListener("abort",f),g&&m?o.sent.push({role:"assistant",content:m}):(o.conversation=void 0,o.sent=[])}},we={backend:l.gpu,startLLM:async({model:e,limits:t,onProgress:n,signal:a,logging:o})=>{ce=!0===o;const i=e.litert;if(!i)throw new Error("Model has no LiteRT artifact");const s=await r.e(520).then(()=>r(507)),c=globalThis,u=c.Module;c.Module={print:le,printErr:de};try{await s.getOrLoadGlobalLiteRtLm(b().script)}finally{c.Module=u}if(a?.aborted)throw new Error("Cancelled");const g=C(i.repo,i.file),m=await ue();let f;const p=m?await m.match(g):void 0;if(p?.body)f=p.body;else{const e=await ie.stored(g);e?.complete&&(f=e.file.stream())}if(!f){const e=await ie.download({url:g,signal:a,onProgress:n}).catch(e=>{if(a?.aborted)throw new Error("Cancelled");if(!ae(e))throw e;o&&console.log("Local model store failed",e)});e&&(f=e.file.stream())}if(!f&&m)try{await(async(e,t,n,a)=>{const o=ge.get(e);if(o)return await o;const r=(async()=>{const o=await fetch(e,{signal:n});if(!o.ok||!o.body)throw new Error("Model download failed");const r=L(o),i=me(o.body,r,a);await t.put(e,new Response(i,{headers:{"content-type":"application/octet-stream",...r?{"content-length":`${r}`}:{}}}))})().finally(()=>{ge.delete(e)});return ge.set(e,r),await r})(g,m,a,n);const e=await m.match(g);e?.body&&(f=e.body)}catch(e){if(a?.aborted)throw new Error("Cancelled");o&&console.log("Local model cache failed",e)}if(f||(f=await(async(e,t,n)=>{const a=await fetch(e,{signal:t});if(!a.ok||!a.body)throw new Error("Model download failed");return me(a.body,L(a),n)})(g,a,n)),a?.aborted)throw new Error("Cancelled");n(1,d.load);const h=await s.Engine.create((({model:e,limits:t,source:n,mod:a})=>({model:n,mainExecutorSettings:{maxNumTokens:4096,...e.litert?.vision?{maxNumImages:Math.max(1,t.maxImages)}:{},backendConfig:se},...e.litert?.vision?{visionBackend:a.Backend.GPU,maxVisionTokensPerImage:70,minVisionTokensPerImage:70}:{},...e.litert?.audio?{audioBackend:a.Backend.GPU}:{}}))({model:e,limits:t,source:f,mod:s}));if(a?.aborted)throw await h.delete(),new Error("Cancelled");const w={sent:[],reasoning:!1,maxTokens:0};return{model:e,backend:l.gpu,contextSize:4096,chat:t=>(async({engine:e,model:t,mod:n,params:a,session:o})=>{ce=!0===a.logging;const r=()=>{try{o.conversation?.cancel()}catch{}o.conversation=void 0,o.sent=[]};try{return await N({model:t,params:a,stopped:()=>!!a.signal?.aborted,onStall:r,start:async r=>{if(a.signal?.aborted)throw new Error("Cancelled");if(j(r)&&!t.litert?.vision)throw new Error("Model does not accept images");if(S(r)&&!t.litert?.audio)throw new Error("Model does not accept audio");return he({engine:e,mod:n,params:a,messages:r,session:o})}})}catch(e){throw r(),e}})({engine:h,model:e,mod:s,params:t,session:w}),unload:async()=>{w.conversation=void 0,w.sent=[],await h.delete()}}},cached:async e=>{const t=e.litert;if(!t)return!1;const n=C(t.repo,t.file),[a,o]=await Promise.all([ue(),ie.stored(n)]);if(o?.complete)return!0;try{return!(!a||!await a.match(n))}catch{return!1}},hasCache:async()=>{try{for(const e of g){const t=e.litert;if(!t)continue;if(await ie.stored(C(t.repo,t.file)))return!0}const e=await ue();return!!e&&(await e.keys()).length>0}catch{return!1}},remove:async e=>{try{const t=e.litert;if(!t)return;const n=C(t.repo,t.file),a=await ue();await ie.remove(n),a&&await a.delete(n)}catch{}},purge:async()=>{try{await ie.purge(),"undefined"!=typeof caches&&await caches.delete(re)}catch{}}},ye=oe("wllama-store");let be,ve,xe="",ke=!1;const _e=e=>`${e.wasm}|${e.compatWasm}|${e.compatWorker}`,Me=e=>{const t=y();return{wasm:e?.wasm||t.wasm,compatWasm:e?.compatWasm||t.compatWasm,compatWorker:e?.compatWorker||t.compatWorker}},Te=e=>{const t=e=>(...t)=>{ke&&("string"==typeof t[0]&&t[0].includes("ggml_webgpu: Failed to get an adapter")||e(...t))};return{...e,log:t(e.log),warn:t(e.warn),error:t(e.error)}},Ee=async()=>{if(be)return be;const e=await r.e(924).then(()=>r(577)),t=ve||Me(),n=new e.Wllama({default:t.wasm},{logger:Te(e.LoggerWithoutDebug)});return ve||(ve=t,xe=_e(t)),n.setCompat({wasm:t.compatWasm,worker:t.compatWorker}),be=n,n},Ce=async()=>{const e=be;be=void 0;try{await(e?.exit())}catch{}},Le=(e,t)=>e.name.includes(t)||!!e.metadata?.originalURL?.includes(t),Pe=e=>{const t=e.cpu;return t?[{name:t.file,url:C(t.repo,t.file)},...t.mmproj?[{name:t.mmproj.file,url:C(t.repo,t.mmproj.file)}]:[]]:[]},je=async e=>{const t=Pe(e);if(!t.length)return!1;for(const e of t){const t=await ye.stored(e.url);if(!t?.complete)return!1}return!0},Se=async(e,t)=>{const n=await e.cacheManager.list();return Pe(t).every(e=>n.some(t=>Le(t,e.name)))},$e=async({model:e,signal:t,onProgress:n})=>{const a=Pe(e),o=[];for(let e=0;e<a.length;e++){if(t?.aborted)throw new Error("Cancelled");const r=await ye.download({url:a[e].url,signal:t,onProgress:(t,o)=>n((e+t)/a.length,o)});if(!r)return;o.push(r.file)}return o.length?o:void 0},Re=async e=>{const t=[];for(const n of e){if("string"==typeof n.content){"system"==n.role?t.push({role:"system",content:n.content}):"assistant"==n.role?t.push({role:"assistant",content:n.content}):t.push({role:"user",content:n.content});continue}const e=[];for(const t of n.content)"text"==t.type?e.push({type:"text",text:t.text}):e.push({type:t.type,data:await x(t.url)});t.push({role:"user",content:e})}return t},Ae={backend:l.cpu,startLLM:async({model:e,limits:t,onProgress:n,signal:a,logging:o,assets:r})=>{ke=!0===o;const i=e.cpu;if(!i)throw new Error("Model has no CPU artifact");const s=Me(r),c=_e(s);be&&c!=xe&&await Ce(),ve=s,xe=c;const u=(g="undefined"!=typeof navigator&&navigator.hardwareConcurrency||4,m=t.maxThreads,Math.max(1,Math.min(Math.max(2,Math.min(8,(g||4)-1)),m)));var g,m;const f=await Ee(),p=await je(e),h=i.mmproj?.file,w=C(i.repo,i.file),y=h?C(i.repo,h):"";if(a?.aborted)throw await Ce(),new Error("Cancelled");if(!p&&h){const e=await f.cacheManager.list(),t=e.some(e=>Le(e,i.file)),n=e.some(e=>Le(e,h));t&&!n&&await f.cacheManager.deleteMany(e=>Le(e,i.file))}const b={n_ctx:8192,n_threads:u,reasoning_budget_tokens:t.maxThinkingTokens,n_gpu_layers:0,jinja:!0};try{let t=p?await $e({model:e,signal:a,onProgress:n}):void 0;t||p||await Se(f,e).catch(()=>!1)||(t=await $e({model:e,signal:a,onProgress:n}).catch(e=>{if(a?.aborted)throw new Error("Cancelled");if(!ae(e))throw e;o&&console.log("Local model store failed",e)})),t?(n(1,d.load),await f.loadModel(t,b)):await f.loadModelFromUrl(y?{url:w,mmprojUrl:y}:w,{...b,useCache:!0,signal:a,progressCallback:({loaded:e,total:t})=>{const a=t?e/t:0;n(a,a>=1?d.load:d.download)}})}catch(e){throw await Ce(),e}if(a?.aborted)throw await Ce(),new Error("Cancelled");if(!f.getLoadedContextInfo().n_ctx)throw await Ce(),new Error("Failed to create model context");return{model:e,backend:l.cpu,contextSize:8192,chat:t=>(async(e,t,n)=>{if(ke=!0===n.logging,e.isModelLoaded())return await N({model:t,params:n,stopped:()=>!!n.signal?.aborted,start:async a=>{if(j(a)&&!e.supportInputModality("image"))throw new Error("Model does not accept images");if(S(a)&&!e.supportInputModality("audio"))throw new Error("Model does not accept audio");return await e.createChatCompletion({messages:await Re(a),...E(n.limits,n.reasoning),abortSignal:n.signal,...t.reasoning?{chat_template_kwargs:{enable_thinking:n.reasoning}}:{}})}})})(f,e,t),unload:async()=>{if(be==f)await Ce();else try{await f.exit()}catch{}}}},cached:async e=>{try{return!!e.cpu&&(!!await je(e)||await Se(await Ee(),e))}catch{return!1}},hasCache:async()=>{try{for(const e of g.flatMap(e=>Pe(e)))if(await ye.stored(e.url))return!0;const e=await Ee();return(await e.cacheManager.list()).length>0}catch{return!1}},remove:async e=>{const t=Pe(e);try{for(const e of t)await ye.remove(e.url)}catch{}try{const e=await Ee();await e.cacheManager.deleteMany(e=>t.some(t=>Le(e,t.name)))}catch{}},purge:async()=>{try{await ye.purge()}catch{}try{const e=await Ee();await e.cacheManager.clear()}catch{}}},We=((e={})=>{const t=e.engines?.cpu||Ae,n=e.engines?.litert||we;let a,o,r,i,s,u,m,f=l.none,p=c.idle,h=[],w=0,y={percent:0,phase:d.download};const b=a=>{if(e.backend!=l.none)return e.backend==l.cpu?a.cpu?t:void 0:e.backend==l.gpu?a.litert?n:void 0:a.litert&&"undefined"!=typeof navigator&&navigator.gpu&&"undefined"!=typeof WebAssembly?n:!a.cpu||"undefined"==typeof window&&"undefined"==typeof self&&"undefined"==typeof Worker||"undefined"==typeof WebAssembly?void 0:t},v=(e,t)=>{y={percent:Math.max(0,Math.min(1,e)),phase:t||d.download};for(const e of[...h])e.report(y.percent,y.phase)},x=e=>{p=e},k=()=>{w++,u?.abort(),u=void 0,s=void 0,p==c.downloading&&x(c.idle)},_=(e,t)=>{const n=i;return n?new Promise(a=>{let o=!1;const r={report:e,settle:e=>{o||(o=!0,t?.removeEventListener("abort",s),a(e))}},s=()=>{(e=>{h=h.filter(t=>t!=e),queueMicrotask(()=>{!h.length&&i&&k()})})(r),r.settle(!1)};h.push(r),t?.aborted?s():t?.addEventListener("abort",s,{once:!0}),n.then(r.settle,()=>r.settle(!1))}):Promise.resolve(!1)},M=()=>p==c.ready&&!!a&&!!o;return{load:async({model:e,limits:t,assets:n,logging:l,signal:m,onProgress:p})=>{if(m?.aborted)return!1;if(M()&&o?.id==e.id)return!0;if(i&&s==e.id)return p(y.percent,y.phase),await _(p,m);i&&(k(),h=[],await i.catch(()=>!1));const T=++w,E=(async()=>{await(async()=>{try{if("undefined"==typeof navigator)return;await(navigator.storage?.persist?.())}catch{}})();const i=b(e);if(T!=w)return!1;if(!i)return x(c.unsupported),!1;if(await(async(e,t)=>{if(o&&o.id!=e.id){const e=o,t=r,n=a;a=void 0,o=void 0,r=void 0,await(n?.unload()),await(t?.remove(e))}if(await t.hasCache())for(const n of g)n.id!=e.id&&b(n)==t&&await t.remove(n)})(e,i),T!=w)return!1;const s=await i.cached(e);if(T!=w)return!1;x(c.downloading),v(0,d.download),s&&v(1,d.load);const m=new AbortController;u=m;try{const s=await i.startLLM({model:e,limits:t,logging:l,signal:m.signal,assets:n,onProgress:(e,t)=>{T==w&&v(e,t)}});return T!=w||m.signal.aborted?(await s.unload(),!1):(a=s,o=e,r=i,f=i.backend,x(c.ready),v(1,d.load),!0)}catch(e){return T!=w||(a=void 0,o=void 0,r=void 0,l&&console.log("Local model failed",e),x(c.error)),!1}finally{u==m&&(u=void 0)}})().finally(()=>{i==E&&(i=void 0,s=void 0,h=[])});return i=E,s=e.id,await _(p,m)},chat:async e=>{if(!M()||!a)return;for(;m;)await m.catch(()=>{});if(!M()||!a)return;const t=a.chat(e);m=t;try{return await t}finally{m==t&&(m=void 0)}},unload:async()=>{const e=a;k(),a=void 0,o=void 0,r=void 0,x(c.idle),await(e?.unload())},ready:M,loading:()=>!M()&&p==c.downloading,status:()=>p,backend:()=>f,model:()=>o}})(),Be=(e,t,n)=>{const a=e.status(),o=m(t);return{availability:a==c.ready?s.online:a==c.error||a==c.unsupported?s.offline:s.away,status:a,backend:e.backend(),supportsImages:!!o&&h(o),supportsAudio:!!o&&w(o),ready:e.ready(),loading:e.loading(),requesting:n.requesting,phase:n.phase,percent:n.percent,...n.error?{error:n.error}:{}}},Ie=(e=>{let t,n=0;const a=t=>t.input.onStatus?.(Be(e,t.model??t.input.model,t));return o=>{t?.controller.abort();const r={token:++n,input:o,controller:new AbortController,percent:e.ready()?100:0,phase:e.ready()?d.load:d.download,requesting:!1,cancelled:!1};return t=r,a(r),(async t=>{const{input:o}=t,r=!0===o.logging,s=await p(o.model);if(t.model=s?.id,t.token!=n)return;const c=o.messages.some(e=>e.role!=i.system&&"string"==typeof e.content&&!!e.content.trim()),l=o.messages.some(e=>e.role==i.user&&!!e.images?.some(e=>"string"==typeof e&&!!e)),u=o.messages.some(e=>e.role==i.user&&!!e.audio?.some(e=>"string"==typeof e&&!!e)),g=c||l||u;if(!s)return t.error="Unknown model",void a(t);if(!g)return void a(t);const m=h(s),f=w(s);if(!(c||l&&m||u&&f))return u&&!f?t.error="Model does not accept audio":l&&!m&&(t.error="Model does not accept images"),void a(t);const y=T(o.limits,!!s.litert),b=await e.load({model:s,limits:y,logging:r,signal:t.controller.signal,onProgress:(e,o)=>{t.token==n&&(t.percent=Math.round(100*e),t.phase=o,a(t))}}).catch(e=>(t.token==n&&(t.error=`${e?.message||`${e}`}`),!1));if(t.token!=n)return;if(t.cancelled)return;if(!b)return t.error||(t.error="Model not ready"),void a(t);const v=l&&!m||u&&!f?o.messages.map(({images:e,audio:t,...n})=>({...n,...e&&m?{images:e}:{},...t&&f?{audio:t}:{}})):o.messages;t.requesting=!0,t.error=void 0,t.percent=100,t.phase=d.load,a(t);let x,k="",_="";try{x=await e.chat({messages:v,limits:y,reasoning:!0===o.reasoning,logging:r,onReason:e=>{t.token!=n||t.cancelled||(k+=e,o.onThinking?.(k))},onText:e=>{t.token!=n||t.cancelled||(_+=e,o.onResponse?.(_))},onSpeed:e=>{t.token!=n||t.cancelled||o.onSpeed?.(e)},signal:t.controller.signal})}catch(e){if(t.token!=n)return;return t.requesting=!1,t.error=`${e?.message||`${e}`}`,void a(t)}t.token==n&&(t.requesting=!1,x||t.cancelled||(t.error="No reply"),a(t))})(r),()=>(t=>{t.token==n&&(t.cancelled=!0,t.controller.abort(),e.status()!=c.ready?queueMicrotask(()=>a(t)):e.unload().then(()=>a(t)))})(r)}})(We),qe=(ze=We,async(e={})=>{const t=await p(e.model),n=n=>e.onStatus?.(Be(ze,t?.id,n));if(!t)return n({requesting:!1,phase:d.download,percent:0,error:"Unknown model"}),!1;let a,o=0,r=d.download;const i=ze.ready()&&ze.model()?.id==t.id;return await ze.load({model:t,limits:T(e.limits,!!t.litert),logging:!0===e.logging,onProgress:(e,t)=>{o=Math.round(100*e),r=t,n({requesting:!1,phase:r,percent:o})}}).catch(e=>(a=`${e?.message||`${e}`}`,!1))?(i&&n({requesting:!1,phase:d.load,percent:100}),!0):(n({requesting:!1,phase:r,percent:o,error:a||"Model not ready"}),!1)});var ze;export{u as ChatModelId,s as ChatStatus,l as LlmBackend,d as LlmPhase,i as LlmRole,c as LlmStatus,Ie as createChat,qe as preloadModel};
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
@@ -8,6 +8,15 @@ interface ChatMessage {
8
8
  /** Audio data URLs or URLs attached to the turn */
9
9
  audio?: string[];
10
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;
19
+ }
11
20
  /** One on-device model */
12
21
  interface ChatModel {
13
22
  /** Stable catalog id, passed by the host */
@@ -26,13 +35,10 @@ interface ChatModel {
26
35
  };
27
36
  };
28
37
  /** LiteRT-LM (WebGPU) artifact */
29
- litert?: {
38
+ litert?: LitertArtifact & {
30
39
  repo: string;
31
- file: string;
32
- /** Whether the build accepts images */
33
- vision: boolean;
34
- /** Whether the build accepts audio */
35
- audio: boolean;
40
+ /** Browser build used on phones, smaller and text only */
41
+ web?: LitertArtifact;
36
42
  };
37
43
  }
38
44
  /** Token, prompt and thread budgets of an on-device generation */
@@ -124,6 +130,13 @@ interface GpuNavigator extends Navigator {
124
130
  }) => Promise<unknown>;
125
131
  };
126
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
+ };
127
140
  /** Request state that builds one status snapshot */
128
141
  interface ChatState {
129
142
  /** Whether a reply is streaming right now */
@@ -445,4 +458,4 @@ interface EngineRunParams {
445
458
  /** Called when a slow stream keeps the text already delivered */
446
459
  onStall?: () => void;
447
460
  }
448
- 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.4.0",
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",