@degreesign/chat 0.3.5 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,6 +15,7 @@ package loads one model, streams one reply and reports status.
15
15
  - [Why](#why)
16
16
  - [Install](#install)
17
17
  - [Quick start](#quick-start)
18
+ - [Enable WebGPU](#enable-webgpu)
18
19
  - [Public API](#public-api)
19
20
  - [Status](#status)
20
21
  - [Models](#models)
@@ -83,12 +84,7 @@ call returns one cancel function and starts the model behind the status
83
84
  callback.
84
85
 
85
86
  ```ts
86
- import {
87
- ChatModelId,
88
- LlmRole,
89
- createChat,
90
- preloadModel,
91
- } from "@degreesign/chat";
87
+ import { LlmRole, createChat, preloadModel } from "@degreesign/chat";
92
88
 
93
89
  const messages = [
94
90
  { role: LlmRole.system, content: "Answer in two sentences." },
@@ -96,26 +92,27 @@ const messages = [
96
92
  ];
97
93
 
98
94
  // Optional: download and load the model before the first send.
99
- await preloadModel({ model: ChatModelId.qwen3_vl_2b });
95
+ await preloadModel();
100
96
 
101
97
  const cancel = createChat({
102
- model: ChatModelId.qwen3_vl_2b,
103
98
  messages,
104
- limits: { maxReplyTokens: 320 },
105
- reasoning: false,
106
- onThinking: (text) => renderThinking(text),
107
99
  onResponse: (text) => renderReply(text),
108
- onStatus: (snapshot) => renderStatus(snapshot),
100
+ // onThinking: (text) => renderThinking(text),
101
+ // onStatus: (snapshot) => renderStatus(snapshot),
109
102
  });
110
103
 
111
104
  // Later, stop the reply, cancel the load, or free the model.
112
105
  cancel();
113
106
  ```
114
107
 
108
+ Without `model` the package picks the GPU model when the browser provides a
109
+ WebGPU adapter and the CPU model when it does not. `limits`, `reasoning`,
110
+ `onThinking` and `onStatus` are optional per call; see the API below.
111
+
115
112
  There is no promise and no controller object on `createChat`. Every result
116
- arrives through the three callbacks, and `onStatus` reports `requesting: false`
117
- when the reply ends. `preloadModel` is the one promise: it resolves once the
118
- model is loaded, or `false` when it cannot load.
113
+ arrives through `onResponse`, and `onStatus` reports `requesting: false` when
114
+ the reply ends. `preloadModel` is the one promise: it resolves once the model
115
+ is loaded, or `false` when it cannot load.
119
116
 
120
117
  Attach images to a user turn with `images: [dataUrl]`. The package sends them
121
118
  only to a model with vision support, center-crops each image to a square,
@@ -124,11 +121,66 @@ resizes it to 288 x 288, and caps the number with `limits.maxImages`.
124
121
  The package never writes storage, so the host appends the user message before
125
122
  the call and appends the assistant message after the last `onResponse`.
126
123
 
124
+ ## Enable WebGPU
125
+
126
+ A request without `model` picks `gemma4_e2b` when the browser exposes a WebGPU
127
+ adapter and `qwen3_vl_2b` when it does not. The GPU engine is far faster, so
128
+ check the browser before blaming the package:
129
+
130
+ ```js
131
+ // Run in the console of the page that loads the package
132
+ const adapter = await navigator.gpu?.requestAdapter();
133
+ adapter
134
+ ? `${adapter.info.vendor} ${adapter.info.architecture}`
135
+ : `no adapter`;
136
+ ```
137
+
138
+ `null` means the browser has the API but no usable adapter, and the package
139
+ falls back to the CPU model. Chromium browsers report the whole state at
140
+ `chrome://gpu`: `WebGPU` should read `Hardware accelerated` and `Vulkan` should
141
+ read `Enabled`. Firefox lists the detected features at `about:support` under
142
+ `Graphics`.
143
+
144
+ ### Firefox
145
+
146
+ WebGPU ships enabled on Windows and macOS. On Linux, and on any build that
147
+ reports `navigator.gpu` as undefined:
148
+
149
+ 1. Open `about:config`.
150
+ 2. Set `dom.webgpu.enabled` to `true`.
151
+ 3. Restart the browser and run the snippet again.
152
+
153
+ Firefox runs WebGPU on its own wgpu stack, so an adapter can appear even with a
154
+ software driver. Check the renderer in `about:support` when replies feel slow.
155
+
156
+ ### Chrome, Edge and Brave
157
+
158
+ WebGPU ships enabled on Windows, macOS and ChromeOS since version 113, so there
159
+ is nothing to configure.
160
+
161
+ On Linux the GPU process only exposes an adapter when ANGLE runs on Vulkan,
162
+ which is not the default. Start the browser with both flags:
163
+
164
+ ```bash
165
+ google-chrome --enable-features=Vulkan --use-angle=vulkan
166
+ microsoft-edge --enable-features=Vulkan --use-angle=vulkan
167
+ brave-browser --enable-features=Vulkan --use-angle=vulkan
168
+ ```
169
+
170
+ Either flag alone leaves `requestAdapter()` returning `null`. To keep them, add
171
+ them to the `Exec` line of the browser entry under
172
+ `~/.local/share/applications`, or to the target of a desktop shortcut.
173
+
174
+ `--enable-unsafe-webgpu` is a different switch: it only unlocks software
175
+ adapters, so an adapter whose `info.vendor` reads `google` is SwiftShader on the
176
+ CPU, not the GPU. Remove it when measuring speed. Headless Chromium hides
177
+ `navigator.gpu` entirely, so check the adapter in a normal window.
178
+
127
179
  ## Public API
128
180
 
129
181
  ```ts
130
182
  createChat(input: ChatInput): CancelChat
131
- preloadModel(input: ChatPreloadInput): Promise<boolean>
183
+ preloadModel(input?: ChatPreloadInput): Promise<boolean>
132
184
  ```
133
185
 
134
186
  Call it once per response. A new call aborts the previous stream. When the
@@ -138,15 +190,21 @@ requested model is already loaded, no download or load happens.
138
190
 
139
191
  | Field | Description |
140
192
  | ----- | ----------- |
141
- | `model` | `ChatModelId` to use for this request. A different model replaces the loaded one. |
193
+ | `model?` | `ChatModelId` to use; a different model replaces the loaded one. |
142
194
  | `messages` | The complete prompt, including system messages and the new user turn. |
143
- | `limits?` | Partial `LlmLimits`; `maxImages` caps images and `maxThreads` caps CPU threads (default 2). |
195
+ | `limits?` | Partial `LlmLimits`; see the budget defaults below. |
144
196
  | `reasoning?` | Whether the model may think first. Defaults to `false`. |
145
197
  | `logging?` | Whether package and engine console logs are written. Defaults to `false`. |
146
198
  | `onThinking?` | Receives the accumulated reasoning text as it streams. |
147
199
  | `onResponse` | Receives the accumulated reply text as it streams. Required. |
148
200
  | `onStatus?` | Receives a `ChatStatusSnapshot` on every state change. |
149
201
 
202
+ Without `model` the package picks the GPU build when the browser provides a
203
+ WebGPU adapter and the CPU build when it does not. The budget defaults are 250
204
+ reply tokens, 128 CPU thinking tokens, 1200 GPU reasoning tokens (320 on the
205
+ CPU), 2 images, 1000 characters per message, 10000 prompt characters and 6 CPU
206
+ threads.
207
+
150
208
  `CancelChat` is `() => void`. Its meaning depends on the current state:
151
209
 
152
210
  | State | Effect |
@@ -162,7 +220,7 @@ and never starts a second download.
162
220
  ### Preload
163
221
 
164
222
  ```ts
165
- preloadModel(input: ChatPreloadInput): Promise<boolean>
223
+ preloadModel(input?: ChatPreloadInput): Promise<boolean>
166
224
  ```
167
225
 
168
226
  Call it before the first `createChat` to download and load the model early. It
@@ -176,7 +234,7 @@ running.
176
234
 
177
235
  | Field | Description |
178
236
  | ----- | ----------- |
179
- | `model` | `ChatModelId` to download and load. |
237
+ | `model?` | `ChatModelId` to download and load. Defaults like `ChatInput.model`. |
180
238
  | `limits?` | Partial `LlmLimits`; `maxThreads` applies when the model loads. |
181
239
  | `logging?` | Whether package and engine console logs are written. Defaults to `false`. |
182
240
  | `onStatus?` | Receives a `ChatStatusSnapshot` on every progress and state change. |
@@ -205,6 +263,10 @@ on failure. A preload owns no cancel function; a new load replaces it.
205
263
  The first snapshot fires synchronously inside `createChat`. A final snapshot
206
264
  with `requesting: false` marks the end of the call.
207
265
 
266
+ A stream that never produces its first chunk ends with an error after 180
267
+ seconds instead of waiting forever. Once a chunk has arrived, 30 seconds of
268
+ silence ends the stream and keeps the text already delivered.
269
+
208
270
  ## Logging
209
271
 
210
272
  Console output stays off by default. Pass `logging: true` in a call to receive
@@ -232,6 +294,11 @@ Gemma 4 E2B downloads one `gemma-4-E2B-it.litertlm` file of about 2.6 GB. It
232
294
  runs on the WebGPU engine with its vision encoder in the same file, so an
233
295
  image prompt does not download a projector.
234
296
 
297
+ The WebGPU engine keeps one LiteRT conversation for consecutive turns. When the
298
+ next prompt continues that history, only the new user turn is sent. A changed
299
+ history opens a fresh conversation, and older images become `[image]` notes in
300
+ its preface.
301
+
235
302
  ## Prompt assembly
236
303
 
237
304
  The `messages` array is the whole prompt. The package bounds it and shapes it
@@ -276,7 +343,7 @@ onProgress })` contract and return the runtime object with `model`, `backend`,
276
343
  matches the requested model.
277
344
 
278
345
  The CPU engine derives a raw thread count from the device, then caps it at
279
- `limits.maxThreads`, default 2. The cap is applied after the raw maximum, so the
346
+ `limits.maxThreads`, default 6. The cap is applied after the raw maximum, so the
280
347
  engine never exceeds the device or the caller budget.
281
348
 
282
349
  The CPU engine needs cross-origin isolation for its threads. Serve the page with
@@ -1,2 +1,2 @@
1
1
  /*! For license information please see chat.min.mjs.LICENSE.txt */
2
- var e={};const t={};function a(n){const o=t[n];if(void 0!==o)return o.exports;const r=t[n]={exports:{}};return e[n](r,r.exports,a),r.exports}var n,o,r,i,s,c;a.m=e,a.d=(e,t)=>{for(var n in t)a.o(t,n)&&!a.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},a.f={},a.e=e=>{const t=[];return a.f.j(e,t),Promise.all(t)},a.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),a.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},n=e=>{let{__webpack_esm_ids__:n,__webpack_esm_modules__:o,__webpack_esm_runtime__:r}=e;var i,s,c=0;for(i in o)a.o(o,i)&&(a.m[i]=o[i]);for(r&&r(a);c<n.length;c++)s=n[c],a.o(t,s)&&t[s]&&t[s][0](),t[s]=0};a.f.j=(o,r)=>{let i=a.o(t,o)?t[o]:void 0;if(0!==i)if(i)r.push(i[1]);else{let a=e[o]().then(n,e=>{throw 0!==t[o]&&(t[o]=void 0),e});a=Promise.race([a,new Promise(e=>i=t[o]=[e])]),r.push(i[1]=a)}}})(),function(e){e.system="system",e.user="user",e.assistant="assistant"}(n||(n={})),function(e){e[e.online=0]="online",e[e.away=1]="away",e[e.offline=2]="offline"}(o||(o={})),function(e){e[e.idle=0]="idle",e[e.downloading=1]="downloading",e[e.ready=2]="ready",e[e.error=3]="error",e[e.unsupported=4]="unsupported"}(r||(r={})),function(e){e.cpu="cpu",e.gpu="gpu",e.none="none"}(i||(i={})),function(e){e.download="download",e.load="load"}(s||(s={})),function(e){e.qwen3_vl_2b="qwen3-vl-2b",e.gemma4_e2b="gemma4-e2b"}(c||(c={}));const l=[{id:c.qwen3_vl_2b,reasoning:!0,cpu:{repo:"Qwen/Qwen3-VL-2B-Thinking-GGUF",file:"Qwen3VL-2B-Thinking-Q4_K_M.gguf",mmproj:{file:"mmproj-Qwen3VL-2B-Thinking-Q8_0.gguf"}}},{id:c.gemma4_e2b,reasoning:!0,litert:{repo:"litert-community/gemma-4-E2B-it-litert-lm",file:"gemma-4-E2B-it.litertlm",vision:!0}}],d=e=>e?l.find(t=>t.id==e):void 0,g=e=>!!e.cpu?.mmproj||!!e.litert?.vision,u=new URL("./../assets/wllama/wllama.wasm",import.meta.url).href,m=new URL("./../assets/wllama/compat/wllama.wasm",import.meta.url).href,f=new URL("./../assets/wllama/compat/wllama.js",import.meta.url).href,w=new URL("./../assets/litert/litertlm_wasm_internal.js",import.meta.url).href,p=(new URL("./../assets/litert/litertlm_wasm_internal.wasm",import.meta.url).href,288),h=async e=>{if(!e.startsWith("data:"))return await(await fetch(e)).arrayBuffer();const t=e.indexOf(","),a=t>=0?e.slice(0,t):"";if(t<0)throw new Error("Invalid image data");if(!a.includes(";base64"))return await(await fetch(e)).arrayBuffer();const n=atob(e.slice(t+1)),o=new Uint8Array(n.length);for(let e=0;e<n.length;e++)o[e]=n.charCodeAt(e);return o.buffer},y=async e=>{if(!e||"function"!=typeof fetch||"function"!=typeof createImageBitmap)return e;try{const t=await fetch(e),a=await createImageBitmap(await t.blob()),n=Math.min(a.width,a.height),o=Math.floor((a.width-n)/2),r=Math.floor((a.height-n)/2),i=((e,t)=>{if("undefined"!=typeof OffscreenCanvas)return new OffscreenCanvas(e,t);if("undefined"==typeof document)return;const a=document.createElement("canvas");return a.width=e,a.height=t,a})(p,p),s=i?i.getContext("2d"):null;if(!i||!s||n<1)return a.close(),e;if(s.drawImage(a,o,r,n,n,0,0,p,p),a.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 a=new FileReader;a.onerror=()=>t(""),a.onload=()=>t(`${a.result}`),a.readAsDataURL(e)}))(t)||e}return i.toDataURL("image/jpeg",.85)}catch{return e}},b={maxReplyTokens:200,maxThinkingTokens:64,maxReasoningTokens:150,maxImages:2,maxContentLength:1e3,promptCharBudget:1e4,maxThreads:2},v=(e,t,a)=>"number"==typeof e&&Number.isFinite(e)&&e>=a?Math.floor(e):t,k=(e={})=>{const t=b;return{maxReplyTokens:v(e.maxReplyTokens,t.maxReplyTokens,1),maxThinkingTokens:v(e.maxThinkingTokens,t.maxThinkingTokens,1),maxReasoningTokens:v(e.maxReasoningTokens,t.maxReasoningTokens,1),maxImages:v(e.maxImages,t.maxImages,0),maxContentLength:v(e.maxContentLength,t.maxContentLength,1),promptCharBudget:v(e.promptCharBudget,t.promptCharBudget,1),maxThreads:v(e.maxThreads,t.maxThreads,1)}},x=(e,t)=>({stream:!0,temperature:.7,max_tokens:t?e.maxReasoningTokens:e.maxReplyTokens}),_=(e,t)=>`https://huggingface.co/${e}/resolve/main/${t}`,M=e=>Number(e.headers.get("content-length"))||0,E=e=>e.some(e=>Array.isArray(e.content)&&e.content.some(e=>"image"==e.type)),C=["<|channel>thought\n","<|channel>thought","..."],T=["<channel|>","..."],L=(e,t)=>t.length?[...e?[{type:"text",text:e}]:[],...t.map(e=>({type:"image",url:e}))]:e,R=e=>"string"==typeof e?e?[{type:"text",text:e}]:[]:e,P=(e,t)=>{if("string"==typeof e&&"string"==typeof t)return`${e}\n${t}`;const a=[];for(const n of[...R(e),{type:"text",text:"\n"},...R(t)]){const e=a[a.length-1];"text"==n.type&&"text"==e?.type?e.text+=n.text:a.push(n)}return a},$=async({model:e,logging:t,onReason:a,onText:n,start:o,stopped:r})=>{let i="",s="",c="";const l=((e,t)=>{let a="",n=!1;const o=(e,t)=>{let a=-1,n="";for(const o of t){const t=e.indexOf(o);t>=0&&(a<0||t<a)&&(a=t,n=o)}return{at:a,tag:n}},r=(e,t)=>{let a="";for(const n of t)for(let t=Math.min(e.length,n.length-1);t>a.length;t--)if(e.endsWith(n.slice(0,t))){a=e.slice(e.length-t);break}return a};return{feed:i=>{let s=a+i;for(a="";s;){if(n){const{at:t,tag:i}=o(s,T);if(t>=0){t&&e(s.slice(0,t)),s=s.slice(t+i.length),n=!1;continue}const c=r(s,T);e(s.slice(0,s.length-c.length)),a=c;break}{const{at:e,tag:i}=o(s,C);if(e>=0){e&&t(s.slice(0,e)),s=s.slice(e+i.length),s.startsWith("\n")&&(s=s.slice(1)),n=!0;continue}const c=r(s,C);t(s.slice(0,s.length-c.length)),a=c;break}}},flush:()=>{a&&(n?e(a):t(a),a="")}}})(e=>{s+=e,a(e)},e=>{i+=e,n(e)});try{const e=await o();for await(const t of e){if(r())break;const e=t?.choices?.[0],n=e?.delta,o="string"==typeof n?.reasoning_content?n.reasoning_content:"string"==typeof n?.reasoning?n.reasoning:"";e?.finish_reason&&(c=e.finish_reason),o&&(s+=o,a(o)),n?.content&&l.feed(n.content)}l.flush()}catch(e){t&&console.log("Local chat failed",e)}const d=r()?void 0:i||void 0;return t&&(console.log("Local chat reply",e.id,d?"****":""),console.log("Local chat finish",e.id,c||"none",i.length,s.length)),d},j=async({model:e,params:t,start:a,stopped:o})=>{const r=await(async e=>{const t=[];for(const a of e){if("string"==typeof a.content){t.push(a);continue}const e=[];for(const t of a.content)e.push("image"==t.type?{type:"image",url:await y(t.url)}:t);t.push({role:"user",content:e})}return t})(((e,t=b)=>{const a=t.maxContentLength,o=e.filter(e=>"string"==typeof e.content&&!!e.content.trim()||!(e.role!=n.user||!e.images?.length)),r=[],i=[];let s=0,c=t.maxImages;for(let e=o.length-1;e>=0;e--){const l=o[e],d=("string"==typeof l.content?l.content:"").slice(0,a),g=l.role==n.user?(l.images||[]).filter(e=>"string"==typeof e&&!!e).slice(0,Math.max(0,c)):[];if(d||g.length){if(d&&s+d.length>t.promptCharBudget)break;s+=d.length,c-=g.length,l.role==n.system?r.unshift(d):l.role==n.assistant?i.unshift({role:"assistant",content:d}):i.unshift({role:"user",content:L(d,g)})}}for(;i.length&&"user"!=i[0].role;)i.shift();const l=[];for(const e of i){const t=l[l.length-1];"assistant"==t?.role&&"assistant"==e.role?t.content=`${t.content}\n${e.content}`:"user"==t?.role&&"user"==e.role?t.content=P(t.content,e.content):"assistant"==e.role?l.push({role:"assistant",content:e.content}):l.push({role:"user",content:e.content})}const d=r.join("\n\n"),g=[];return d&&g.push({role:"system",content:d}),g.push(...l),g})(t.messages,t.limits));return t.logging&&((e,t)=>{console.log("Local chat call",e.id,t.map(({role:e,content:t})=>({role:e,content:"****",...Array.isArray(t)?{images:t.filter(e=>"image"==e.type).length}:{}})))})(e,r),await $({model:e,logging:t.logging,onReason:t.onReason,onText:t.onText,start:()=>a(r),stopped:o})},W=33554432,z=new Map;let B=!1;const S=e=>{const t=(e.split("/").pop()||"model").replace(/[^\w.-]+/g,"_");let a=0;for(let t=0;t<e.length;t++)a=31*a+e.charCodeAt(t)>>>0;return`${a.toString(16)}-${t}`},q=e=>{const t=/^part-(\d+)$/.exec(e);return t?Number(t[1]):-1},A=async()=>{if(!B)if("undefined"!=typeof navigator&&navigator.storage?.getDirectory)try{return await navigator.storage.getDirectory()}catch{return}else B=!0},O=async(e,t)=>{try{return await e.getDirectoryHandle(t)}catch{return}},N=async e=>{if(e)try{const t=await e.getFile(),a=JSON.parse(await t.text());return{total:Number(a.total)||0,etag:`${a.etag||""}`,complete:!0===a.complete}}catch{return}},U=async(e,t)=>{try{const a=await e.getFileHandle("meta.json",{create:!0}),n=await a.createWritable();await n.write(JSON.stringify(t)),await n.close()}catch{}},F=async function*(e){for await(const t of e)yield t},I=async e=>{try{for await(const[t,a]of F(e))"file"==a.kind&&q(t)>=0&&await e.removeEntry(t).catch(()=>{})}catch{}},D=async(e,t)=>{try{const a=await(await e.getFileHandle(t)).getFile(),n=await N(await e.getFileHandle(`${t}.json`).catch(()=>{}));if(!n)return;return{file:a,size:a.size,total:n.total,etag:n.etag,complete:n.total?a.size>=n.total:!0===n.complete}}catch{return}},H=async(e,t)=>{const a=await O(e,t);if(a){const e=await(async e=>await N(await e.getFileHandle("meta.json").catch(()=>{})))(a),t=await(async e=>{const t=[];try{for await(const[a,n]of F(e)){const e=q(a);if("file"!=n.kind||e<0)continue;const o=await n.getFile();t.push({part:{start:e,size:o.size},file:o})}}catch{return}const a=(e=>{const t=[];let a=0;for(const n of[...e].sort((e,t)=>e.start-t.start))if(n.size){if(n.start!=a)break;t.push(n),a+=n.size}return t})(t.map(e=>e.part)),n=new Set(a.map(e=>e.start)),o=t.filter(e=>n.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:a.reduce((e,t)=>e+t.size,0)}})(a),n=e?.total||0,o=t?.size??0,r=t?.file??new Blob;return{file:n&&o>n?r.slice(0,n):r,size:o,total:n,etag:e?.etag||"",complete:n?o>=n:!0===e?.complete}}return await D(e,t)},Q=(e,t)=>{const a=e.headers.get("content-range"),n=/bytes (\d+)-/.exec(a||""),o=/\/(\d+)\s*$/.exec(a||""),r=e.headers.get("etag")||"";if(206==e.status){const e=n?Number(n[1]):t.size,a=o?Number(o[1]):t.total;if(e!=t.size)return;if(t.total&&a&&t.total!=a)return;if(t.etag&&r&&t.etag!=r)return;return{start:e,total:a||t.total,etag:r||t.etag}}if(!t.etag||!r||t.etag==r)return{start:0,total:M(e)||t.total,etag:r||t.etag}},G=async({dir:e,start:t,skip:a,total:n,body:o,onProgress:r,onChunk:i})=>{let c,l=t,d=0,g=a;const u=async()=>{const t=await e.getFileHandle((e=>`part-${e}`)(l),{create:!0});c=await t.createWritable(),d=0},m=async()=>{const e=c;c=void 0,e&&await e.close().catch(()=>{})};try{const e=o.getReader();for(;;){const{done:a,value:o}=await e.read();if(a)break;i();let f=0;for(g&&(f=Math.min(g,o.byteLength),g-=f);f<o.byteLength;){c||await u();const e=Math.min(W-d,o.byteLength-f);await c.write({type:"write",position:d,data:o.subarray(f,f+e)}),f+=e,d+=e,l+=e,d==W&&await m()}l>t&&r(n?l/n:0,s.download)}await m()}catch(e){throw await m(),e}return l},V=e=>["NotAllowedError","NotSupportedError"].includes(e?.name||""),J=e=>{const t=async()=>{const t=await A();if(t)try{return await t.getDirectoryHandle(e,{create:!0})}catch{return}};return{stored:async e=>{const a=await t();return a?await H(a,S(e)):void 0},download:async({url:a,signal:n,onProgress:o})=>{const r=`${e}|${a}`,i=z.get(r);if(i)return await i;const c=await t();if(!c)return;const l=(async(e,t,a,n)=>{const o=S(t),r=await D(e,o);if(r?.complete)return r;let i=await O(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(a?.aborted)throw new Error("Cancelled");const r=await H(e,o);if(r?.complete)return r;const d=r?.size??0,g=r?.total||0,u=r?.etag||"";if(d>c)c=d,l=0;else if(++l>4)throw new Error("Model download failed");d&&g&&n(d/g,s.download);const m=g?Math.min(d+W,g)-1:d+W-1,f=new AbortController,w=()=>f.abort(),p={size:d,total:g,etag:u};let h,y=setTimeout(w,3e4);a?.addEventListener("abort",w,{once:!0});try{if(h=await fetch(t,{signal:f.signal,headers:{Range:`bytes=${d}-${m}`}}),416==h.status){if(await(h.body?.cancel().catch(()=>{})),g&&d>=g){await U(i,{total:g,etag:u,complete:!0});continue}await I(i),await U(i,{total:0,etag:"",complete:!1});continue}if(!h.ok||!h.body)throw await(h.body?.cancel().catch(()=>{})),new Error("Model download failed");const a=Q(h,p);if(!a){await h.body.cancel().catch(()=>{}),await I(i),await U(i,{total:0,etag:"",complete:!1});continue}await U(i,{total:a.total,etag:a.etag,complete:!1});const r=0==a.start?d:0,s=await G({dir:i,start:a.start+r,skip:r,total:a.total,body:h.body,onProgress:n,onChunk:()=>{clearTimeout(y),y=setTimeout(w,3e4)}});if(!a.total){await U(i,{total:s,etag:a.etag,complete:!0});const t=await H(e,o);if(t)return t}}catch(e){if(a?.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),a?.removeEventListener("abort",w)}}})(c,a,n,o).finally(()=>{z.delete(r)});return z.set(r,l),await l},remove:async e=>{const a=await t();a&&await(async(e,t)=>{await e.removeEntry(t,{recursive:!0}).catch(()=>{}),await e.removeEntry(`${t}.json`).catch(()=>{})})(a,S(e))},purge:async()=>{const t=await A();t&&await t.removeEntry(e,{recursive:!0}).catch(()=>{})}}},K="litertlm-models",X=J("litert-store"),Y={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},Z=async()=>{try{if("undefined"==typeof caches)return;return await caches.open(K)}catch{return}},ee=new Map,te=(e,t,a)=>{let n=0;return e.pipeThrough(new TransformStream({transform:(e,o)=>{n+=e.byteLength,a(t?n/t:0,s.download),o.enqueue(e)}}))},ae=async e=>{const t=[];for(const a of e){if("string"==typeof a.content){t.push({role:a.role,content:a.content});continue}const e=[];for(const t of a.content)e.push("text"==t.type?{type:"text",text:t.text}:{type:"image",data:await h(t.url)});t.push({role:"user",content:e})}return t},ne=async({engine:e,model:t,mod:a,params:n})=>await j({model:t,params:n,stopped:()=>!!n.signal?.aborted,start:async o=>{if(n.signal?.aborted)throw new Error("Cancelled");if(E(o)&&!t.litert?.vision)throw new Error("Model does not accept images");return async function*({engine:e,mod:t,params:a,messages:n}){const o=n[n.length-1],r=x(a.limits,a.reasoning);if("user"!=o?.role)throw new Error("No user turn");const i=await ae(n.slice(0,-1)),s=(await ae([o]))[0];if(!s)throw new Error("Empty prompt");const c=await e.createConversation({sessionConfig:{maxOutputTokens:r.max_tokens,samplerParams:{type:t.SamplerType.TOP_K,temperature:r.temperature,k:40,p:.95}},preface:{...i.length?{messages:i}:{},extra_context:{enable_thinking:a.reasoning}}});let l;const d=()=>{l&&l.cancel().catch(()=>{});try{c.cancel()}catch{}};a.signal?.addEventListener("abort",d,{once:!0});try{if(a.signal?.aborted)return;const e=c.sendMessageStreaming(s).getReader();for(l=e;;){const{done:t,value:a}=await e.read();if(t)break;const n={};let o="string"==typeof a.content?a.content:"";if(Array.isArray(a.content))for(const e of a.content)"text"==e.type&&(o+=e.text);o&&(n.content=o);const r=a.channels?.thought;r&&(n.reasoning=r),(n.content||n.reasoning)&&(yield{choices:[{delta:n}]})}}finally{a.signal?.removeEventListener("abort",d);try{await c.delete()}catch{}}}({engine:e,mod:a,params:n,messages:o})}}),oe={backend:i.gpu,startLLM:async({model:e,limits:t,onProgress:n,signal:o,logging:r})=>{const c=e.litert;if(!c)throw new Error("Model has no LiteRT artifact");const l=await a.e(520).then(()=>a(507));if(await l.getOrLoadGlobalLiteRtLm(w),o?.aborted)throw new Error("Cancelled");const d=_(c.repo,c.file),g=await Z();let u;const m=g?await g.match(d):void 0;if(m?.body)u=m.body;else{const e=await X.stored(d);e?.complete&&(u=e.file.stream())}if(!u){const e=await X.download({url:d,signal:o,onProgress:n}).catch(e=>{if(o?.aborted)throw new Error("Cancelled");if(!V(e))throw e;r&&console.log("Local model store failed",e)});e&&(u=e.file.stream())}if(!u&&g)try{await(async(e,t,a,n)=>{const o=ee.get(e);if(o)return await o;const r=(async()=>{const o=await fetch(e,{signal:a});if(!o.ok||!o.body)throw new Error("Model download failed");const r=M(o),i=te(o.body,r,n);await t.put(e,new Response(i,{headers:{"content-type":"application/octet-stream",...r?{"content-length":`${r}`}:{}}}))})().finally(()=>{ee.delete(e)});return ee.set(e,r),await r})(d,g,o,n);const e=await g.match(d);e?.body&&(u=e.body)}catch(e){if(o?.aborted)throw new Error("Cancelled");r&&console.log("Local model cache failed",e)}if(u||(u=await(async(e,t,a)=>{const n=await fetch(e,{signal:t});if(!n.ok||!n.body)throw new Error("Model download failed");return te(n.body,M(n),a)})(d,o,n)),o?.aborted)throw new Error("Cancelled");n(1,s.load);const f=await l.Engine.create({model:u,mainExecutorSettings:{maxNumTokens:4096,...c.vision?{maxNumImages:Math.max(1,t.maxImages)}:{},backendConfig:Y},...c.vision?{visionBackend:l.Backend.GPU}:{}});if(o?.aborted)throw await f.delete(),new Error("Cancelled");return{model:e,backend:i.gpu,contextSize:4096,chat:t=>ne({engine:f,model:e,mod:l,params:t}),unload:async()=>{await f.delete()}}},cached:async e=>{const t=e.litert;if(!t)return!1;const a=_(t.repo,t.file),[n,o]=await Promise.all([Z(),X.stored(a)]);if(o?.complete)return!0;try{return!(!n||!await n.match(a))}catch{return!1}},hasCache:async()=>{try{for(const e of l){const t=e.litert;if(!t)continue;if(await X.stored(_(t.repo,t.file)))return!0}const e=await Z();return!!e&&(await e.keys()).length>0}catch{return!1}},remove:async e=>{try{const t=e.litert;if(!t)return;const a=_(t.repo,t.file),n=await Z();await X.remove(a),n&&await n.delete(a)}catch{}},purge:async()=>{try{await X.purge(),"undefined"!=typeof caches&&await caches.delete(K)}catch{}}},re=J("wllama-store");let ie,se,ce="",le=!1;const de=e=>`${e.wasm}|${e.compatWasm}|${e.compatWorker}`,ge=e=>{const t={wasm:u,compatWasm:m,compatWorker:f};return{wasm:e?.wasm||t.wasm,compatWasm:e?.compatWasm||t.compatWasm,compatWorker:e?.compatWorker||t.compatWorker}},ue=e=>{const t=e=>(...t)=>{le&&("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)}},me=async()=>{if(ie)return ie;const e=await a.e(924).then(()=>a(577)),t=se||ge(),n=new e.Wllama({default:t.wasm},{logger:ue(e.LoggerWithoutDebug)});return se||(se=t,ce=de(t)),n.setCompat({wasm:t.compatWasm,worker:t.compatWorker}),ie=n,n},fe=async()=>{const e=ie;ie=void 0;try{await(e?.exit())}catch{}},we=(e,t)=>e.name.includes(t)||!!e.metadata?.originalURL?.includes(t),pe=e=>{const t=e.cpu;return t?[{name:t.file,url:_(t.repo,t.file)},...t.mmproj?[{name:t.mmproj.file,url:_(t.repo,t.mmproj.file)}]:[]]:[]},he=async e=>{const t=pe(e);if(!t.length)return!1;for(const e of t){const t=await re.stored(e.url);if(!t?.complete)return!1}return!0},ye=async(e,t)=>{const a=await e.cacheManager.list();return pe(t).every(e=>a.some(t=>we(t,e.name)))},be=async({model:e,signal:t,onProgress:a})=>{const n=pe(e),o=[];for(let e=0;e<n.length;e++){if(t?.aborted)throw new Error("Cancelled");const r=await re.download({url:n[e].url,signal:t,onProgress:(t,o)=>a((e+t)/n.length,o)});if(!r)return;o.push(r.file)}return o.length?o:void 0},ve=async e=>{const t=[];for(const a of e){if("string"==typeof a.content){"system"==a.role?t.push({role:"system",content:a.content}):"assistant"==a.role?t.push({role:"assistant",content:a.content}):t.push({role:"user",content:a.content});continue}const e=[];for(const t of a.content)e.push("text"==t.type?{type:"text",text:t.text}:{type:"image",data:await h(t.url)});t.push({role:"user",content:e})}return t},ke={backend:i.cpu,startLLM:async({model:e,limits:t,onProgress:a,signal:n,logging:o,assets:r})=>{le=!0===o;const c=e.cpu;if(!c)throw new Error("Model has no CPU artifact");const l=ge(r),d=de(l);ie&&d!=ce&&await fe(),se=l,ce=d;const g=(u="undefined"!=typeof navigator&&navigator.hardwareConcurrency||4,m=t.maxThreads,Math.max(1,Math.min(Math.max(2,Math.min(8,(u||4)-1)),m)));var u,m;const f=await me(),w=await he(e),p=c.mmproj?.file,h=_(c.repo,c.file),y=p?_(c.repo,p):"";if(n?.aborted)throw await fe(),new Error("Cancelled");if(!w&&p){const e=await f.cacheManager.list(),t=e.some(e=>we(e,c.file)),a=e.some(e=>we(e,p));t&&!a&&await f.cacheManager.deleteMany(e=>we(e,c.file))}const b={n_ctx:8192,n_threads:g,reasoning_budget_tokens:t.maxThinkingTokens,n_gpu_layers:0,jinja:!0};try{let t=w?await be({model:e,signal:n,onProgress:a}):void 0;t||w||await ye(f,e).catch(()=>!1)||(t=await be({model:e,signal:n,onProgress:a}).catch(e=>{if(n?.aborted)throw new Error("Cancelled");if(!V(e))throw e;o&&console.log("Local model store failed",e)})),t?(a(1,s.load),await f.loadModel(t,b)):await f.loadModelFromUrl(y?{url:h,mmprojUrl:y}:h,{...b,useCache:!0,signal:n,progressCallback:({loaded:e,total:t})=>{const n=t?e/t:0;a(n,n>=1?s.load:s.download)}})}catch(e){throw await fe(),e}if(n?.aborted)throw await fe(),new Error("Cancelled");if(!f.getLoadedContextInfo().n_ctx)throw await fe(),new Error("Failed to create model context");return{model:e,backend:i.cpu,contextSize:8192,chat:t=>(async(e,t,a)=>{if(le=!0===a.logging,e.isModelLoaded())return await j({model:t,params:a,stopped:()=>!!a.signal?.aborted,start:async n=>{if(E(n)&&!e.supportInputModality("image"))throw new Error("Model does not accept images");return await e.createChatCompletion({messages:await ve(n),...x(a.limits,a.reasoning),abortSignal:a.signal,...t.reasoning?{chat_template_kwargs:{enable_thinking:a.reasoning}}:{}})}})})(f,e,t),unload:async()=>{if(ie==f)await fe();else try{await f.exit()}catch{}}}},cached:async e=>{try{return!!e.cpu&&(!!await he(e)||await ye(await me(),e))}catch{return!1}},hasCache:async()=>{try{for(const e of l.flatMap(e=>pe(e)))if(await re.stored(e.url))return!0;const e=await me();return(await e.cacheManager.list()).length>0}catch{return!1}},remove:async e=>{const t=pe(e);try{for(const e of t)await re.remove(e.url)}catch{}try{const e=await me();await e.cacheManager.deleteMany(e=>t.some(t=>we(e,t.name)))}catch{}},purge:async()=>{try{await re.purge()}catch{}try{const e=await me();await e.cacheManager.clear()}catch{}}},xe=((e={})=>{const t=e.engines?.cpu||ke,a=e.engines?.litert||oe;let n,o,c,d,g,u,m,f=i.none,w=r.idle,p=[],h=0,y={percent:0,phase:s.download};const b=n=>{if(e.backend!=i.none)return e.backend==i.cpu?n.cpu?t:void 0:e.backend==i.gpu?n.litert?a:void 0:n.litert&&"undefined"!=typeof navigator&&navigator.gpu&&"undefined"!=typeof WebAssembly?a:!n.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[...p])e.report(y.percent,y.phase)},k=e=>{w=e},x=()=>{h++,u?.abort(),u=void 0,g=void 0,w==r.downloading&&k(r.idle)},_=(e,t)=>{const a=d;return a?new Promise(n=>{let o=!1;const r={report:e,settle:e=>{o||(o=!0,t?.removeEventListener("abort",i),n(e))}},i=()=>{(e=>{p=p.filter(t=>t!=e),queueMicrotask(()=>{!p.length&&d&&x()})})(r),r.settle(!1)};p.push(r),t?.aborted?i():t?.addEventListener("abort",i,{once:!0}),a.then(r.settle,()=>r.settle(!1))}):Promise.resolve(!1)},M=()=>w==r.ready&&!!n&&!!o;return{load:async({model:e,limits:t,assets:a,logging:i,signal:m,onProgress:w})=>{if(m?.aborted)return!1;if(M()&&o?.id==e.id)return!0;if(d&&g==e.id)return w(y.percent,y.phase),await _(w,m);d&&(x(),p=[],await d.catch(()=>!1));const E=++h,C=(async()=>{await(async()=>{try{if("undefined"==typeof navigator)return;await(navigator.storage?.persist?.())}catch{}})();const d=b(e);if(E!=h)return!1;if(!d)return k(r.unsupported),!1;if(await(async(e,t)=>{if(o&&o.id!=e.id){const e=o,t=c,a=n;n=void 0,o=void 0,c=void 0,await(a?.unload()),await(t?.remove(e))}if(await t.hasCache())for(const a of l)a.id!=e.id&&b(a)==t&&await t.remove(a)})(e,d),E!=h)return!1;const g=await d.cached(e);if(E!=h)return!1;k(r.downloading),v(0,s.download),g&&v(1,s.load);const m=new AbortController;u=m;try{const l=await d.startLLM({model:e,limits:t,logging:i,signal:m.signal,assets:a,onProgress:(e,t)=>{E==h&&v(e,t)}});return E!=h||m.signal.aborted?(await l.unload(),!1):(n=l,o=e,c=d,f=d.backend,k(r.ready),v(1,s.load),!0)}catch(e){return E!=h||(n=void 0,o=void 0,c=void 0,i&&console.log("Local model failed",e),k(r.error)),!1}finally{u==m&&(u=void 0)}})().finally(()=>{d==C&&(d=void 0,g=void 0,p=[])});return d=C,g=e.id,await _(w,m)},chat:async e=>{if(!M()||!n)return;for(;m;)await m.catch(()=>{});if(!M()||!n)return;const t=n.chat(e);m=t;try{return await t}finally{m==t&&(m=void 0)}},unload:async()=>{const e=n;x(),n=void 0,o=void 0,c=void 0,k(r.idle),await(e?.unload())},ready:M,loading:()=>!M()&&w==r.downloading,status:()=>w,backend:()=>f,model:()=>o}})(),_e=(e,t,a)=>{const n=e.status(),i=d(t);return{availability:n==r.ready?o.online:n==r.error||n==r.unsupported?o.offline:o.away,status:n,backend:e.backend(),supportsImages:!!i&&g(i),ready:e.ready(),loading:e.loading(),requesting:a.requesting,phase:a.phase,percent:a.percent,...a.error?{error:a.error}:{}}},Me=(e=>{let t,a=0;const o=t=>t.input.onStatus?.(_e(e,t.input.model,t));return i=>{t?.controller.abort();const c={token:++a,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=d(r.model),l=r.messages.some(e=>e.role!=n.system&&"string"==typeof e.content&&!!e.content.trim()),u=r.messages.some(e=>e.role==n.user&&!!e.images?.some(e=>"string"==typeof e&&!!e)),m=l||u;if(!c)return t.error="Unknown model",void o(t);if(!m)return void o(t);if(u&&!l&&!g(c))return t.error="Model does not accept images",void o(t);const f=k(r.limits),w=await e.load({model:c,limits:f,logging:i,signal:t.controller.signal,onProgress:(e,n)=>{t.token==a&&(t.percent=Math.round(100*e),t.phase=n,o(t))}}).catch(e=>(t.token==a&&(t.error=`${e?.message||`${e}`}`),!1));if(t.token!=a)return;if(t.cancelled)return;if(!w)return t.error||(t.error="Model not ready"),void o(t);const p=g(c),h=u&&!p?r.messages.map(({images:e,...t})=>t):r.messages;if(u&&!p&&!l)return t.error="Model does not accept images",void o(t);t.requesting=!0,t.error=void 0,t.percent=100,t.phase=s.load,o(t);let y,b="",v="";try{y=await e.chat({messages:h,limits:f,reasoning:!0===r.reasoning,logging:i,onReason:e=>{t.token!=a||t.cancelled||(b+=e,r.onThinking?.(b))},onText:e=>{t.token!=a||t.cancelled||(v+=e,r.onResponse?.(v))},signal:t.controller.signal})}catch(e){if(t.token!=a)return;return t.requesting=!1,t.error=`${e?.message||`${e}`}`,void o(t)}t.token==a&&(t.requesting=!1,y||t.cancelled||(t.error="No reply"),o(t))})(c),()=>(t=>{t.token==a&&(t.cancelled=!0,t.controller.abort(),e.status()!=r.ready?queueMicrotask(()=>o(t)):e.unload().then(()=>o(t)))})(c)}})(xe),Ee=(Ce=xe,async e=>{const t=d(e.model),a=t=>e.onStatus?.(_e(Ce,e.model,t));if(!t)return a({requesting:!1,phase:s.download,percent:0,error:"Unknown model"}),!1;let n,o=0,r=s.download;const i=Ce.ready()&&Ce.model()?.id==t.id;return await Ce.load({model:t,limits:k(e.limits),logging:!0===e.logging,onProgress:(e,t)=>{o=Math.round(100*e),r=t,a({requesting:!1,phase:r,percent:o})}}).catch(e=>(n=`${e?.message||`${e}`}`,!1))?(i&&a({requesting:!1,phase:s.load,percent:100}),!0):(a({requesting:!1,phase:r,percent:o,error:n||"Model not ready"}),!1)});var Ce;export{c as ChatModelId,o as ChatStatus,i as LlmBackend,s as LlmPhase,n as LlmRole,r as LlmStatus,Me as createChat,Ee 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}}],d=e=>e?l.find(t=>t.id==e):void 0,g=async()=>(await(async()=>{const e="undefined"==typeof navigator?void 0:navigator.gpu;if(!e)return!1;try{return!!await e.requestAdapter({powerPreference:"high-performance"})}catch{return!1}})()?l.find(e=>!!e.litert):l.find(e=>!!e.cpu))?.id,u=async e=>d(e||await g()),m=e=>!!e.cpu?.mmproj||!!e.litert?.vision,f=new URL("./../assets/wllama/wllama.wasm",import.meta.url).href,p=new URL("./../assets/wllama/compat/wllama.wasm",import.meta.url).href,w=new URL("./../assets/wllama/compat/wllama.js",import.meta.url).href,h=new URL("./../assets/litert/litertlm_wasm_internal.js",import.meta.url).href,y=(new URL("./../assets/litert/litertlm_wasm_internal.wasm",import.meta.url).href,288),b=async e=>{if(!e.startsWith("data:"))return await(await fetch(e)).arrayBuffer();const t=e.indexOf(","),n=t>=0?e.slice(0,t):"";if(t<0)throw new Error("Invalid image data");if(!n.includes(";base64"))return await(await fetch(e)).arrayBuffer();const a=atob(e.slice(t+1)),o=new Uint8Array(a.length);for(let e=0;e<a.length;e++)o[e]=a.charCodeAt(e);return o.buffer},v=async e=>{if(!e||"function"!=typeof fetch||"function"!=typeof createImageBitmap)return e;try{const t=await fetch(e),n=await createImageBitmap(await t.blob()),a=Math.min(n.width,n.height),o=Math.floor((n.width-a)/2),r=Math.floor((n.height-a)/2),i=((e,t)=>{if("undefined"!=typeof OffscreenCanvas)return new OffscreenCanvas(e,t);if("undefined"==typeof document)return;const n=document.createElement("canvas");return n.width=e,n.height=t,n})(y,y),s=i?i.getContext("2d"):null;if(!i||!s||a<1)return n.close(),e;if(s.drawImage(n,o,r,a,a,0,0,y,y),n.close(),(e=>"undefined"!=typeof OffscreenCanvas&&e instanceof OffscreenCanvas)(i)){const t=await i.convertToBlob({type:"image/jpeg",quality:.85});return await(e=>new Promise(t=>{if("undefined"==typeof FileReader)return void t("");const n=new FileReader;n.onerror=()=>t(""),n.onload=()=>t(`${n.result}`),n.readAsDataURL(e)}))(t)||e}return i.toDataURL("image/jpeg",.85)}catch{return e}},x={maxReplyTokens:250,maxThinkingTokens:128,maxReasoningTokens:320,maxImages:2,maxContentLength:1e3,promptCharBudget:1e4,maxThreads:6},k=(e,t,n)=>"number"==typeof e&&Number.isFinite(e)&&e>=n?Math.floor(e):t,_=(e={},t=!1)=>{const n=x;return{maxReplyTokens:k(e.maxReplyTokens,n.maxReplyTokens,1),maxThinkingTokens:k(e.maxThinkingTokens,n.maxThinkingTokens,1),maxReasoningTokens:k(e.maxReasoningTokens,t?1200:n.maxReasoningTokens,1),maxImages:k(e.maxImages,n.maxImages,0),maxContentLength:k(e.maxContentLength,n.maxContentLength,1),promptCharBudget:k(e.promptCharBudget,n.promptCharBudget,1),maxThreads:k(e.maxThreads,n.maxThreads,1)}},M=(e,t)=>({stream:!0,temperature:.7,max_tokens:t?e.maxReasoningTokens:e.maxReplyTokens}),T=(e,t)=>`https://huggingface.co/${e}/resolve/main/${t}`,E=e=>Number(e.headers.get("content-length"))||0,C=e=>e.some(e=>Array.isArray(e.content)&&e.content.some(e=>"image"==e.type)),L=["<|channel>thought\n","<|channel>thought","..."],P=["<channel|>","..."],R=(e,t)=>t.length?[...e?[{type:"text",text:e}]:[],...t.map(e=>({type:"image",url:e}))]:e,$=e=>"string"==typeof e?e?[{type:"text",text:e}]:[]:e,j=(e,t)=>{if("string"==typeof e&&"string"==typeof t)return`${e}\n${t}`;const n=[];for(const a of[...$(e),{type:"text",text:"\n"},...$(t)]){const e=n[n.length-1];"text"==a.type&&"text"==e?.type?e.text+=a.text:n.push(a)}return n};class S extends Error{}const W=async(e,t,n)=>{let a;try{return await Promise.race([e,new Promise((e,o)=>{a=setTimeout(()=>o(new S(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,start:o,stopped:r,firstTimeoutMs:i=18e4,idleTimeoutMs:s=3e4,onStall:c})=>{let l="",d="",g="",u=!1;const m=((e,t)=>{let n="",a=!1;const o=(e,t)=>{let n=-1,a="";for(const o of t){const t=e.indexOf(o);t>=0&&(n<0||t<n)&&(n=t,a=o)}return{at:n,tag:a}},r=(e,t)=>{let n="";for(const a of t)for(let t=Math.min(e.length,a.length-1);t>n.length;t--)if(e.endsWith(a.slice(0,t))){n=e.slice(e.length-t);break}return n};return{feed:i=>{let s=n+i;for(n="";s;){if(a){const{at:t,tag:i}=o(s,P);if(t>=0){t&&e(s.slice(0,t)),s=s.slice(t+i.length),a=!1;continue}const c=r(s,P);e(s.slice(0,s.length-c.length)),n=c;break}{const{at:e,tag:i}=o(s,L);if(e>=0){e&&t(s.slice(0,e)),s=s.slice(e+i.length),s.startsWith("\n")&&(s=s.slice(1)),a=!0;continue}const c=r(s,L);t(s.slice(0,s.length-c.length)),n=c;break}}},flush:()=>{n&&(a?e(n):t(n),n="")}}})(e=>{d+=e,n(e)},e=>{l+=e,a(e)});try{const e=(await W(o(),i,"Model did not start"))[Symbol.asyncIterator]();for(;;){if(r()){q(e);break}let a;try{a=await W(e.next(),u?s:i,u?"Model stopped responding":"Model did not reply")}catch(n){if(n instanceof S&&(l||d)){c?.(),q(e),t&&console.log("Local chat stalled",n);break}throw n}if(a.done)break;u=!0;const o=a.value?.choices?.[0],f=o?.delta,p="string"==typeof f?.reasoning_content?f.reasoning_content:"string"==typeof f?.reasoning?f.reasoning:"";o?.finish_reason&&(g=o.finish_reason),p&&(d+=p,n(p)),f?.content&&m.feed(f.content)}m.flush()}catch(e){if(t&&console.log("Local chat failed",e),e instanceof S&&!r())throw e}const f=r()?void 0:l||void 0;return t&&(console.log("Local chat reply",e.id,f?"****":""),console.log("Local chat finish",e.id,g||"none",l.length,d.length)),f},B=async({model:e,params:t,start:n,stopped:o,onStall:r})=>{const i=await(async e=>{const t=[];for(const n of e){if("string"==typeof n.content){t.push(n);continue}const e=[];for(const t of n.content)e.push("image"==t.type?{type:"image",url:await v(t.url)}:t);t.push({role:"user",content:e})}return t})(((e,t=x)=>{const n=t.maxContentLength,o=e.filter(e=>"string"==typeof e.content&&!!e.content.trim()||!(e.role!=a.user||!e.images?.length)),r=[],i=[];let s=0,c=t.maxImages;for(let e=o.length-1;e>=0;e--){const l=o[e],d=("string"==typeof l.content?l.content:"").slice(0,n),g=l.role==a.user?(l.images||[]).filter(e=>"string"==typeof e&&!!e).slice(0,Math.max(0,c)):[];if(d||g.length){if(d&&s+d.length>t.promptCharBudget)break;s+=d.length,c-=g.length,l.role==a.system?r.unshift(d):l.role==a.assistant?i.unshift({role:"assistant",content:d}):i.unshift({role:"user",content:R(d,g)})}}for(;i.length&&"user"!=i[0].role;)i.shift();const l=[];for(const e of i){const t=l[l.length-1];"assistant"==t?.role&&"assistant"==e.role?t.content=`${t.content}\n${e.content}`:"user"==t?.role&&"user"==e.role?t.content=j(t.content,e.content):"assistant"==e.role?l.push({role:"assistant",content:e.content}):l.push({role:"user",content:e.content})}const d=r.join("\n\n"),g=[];return d&&g.push({role:"system",content:d}),g.push(...l),g})(t.messages,t.limits));return t.logging&&((e,t)=>{console.log("Local chat call",e.id,t.map(({role:e,content:t})=>({role:e,content:"****",...Array.isArray(t)?{images:t.filter(e=>"image"==e.type).length}:{}})))})(e,i),await z({model:e,logging:t.logging,onReason:t.onReason,onText:t.onText,start:()=>n(i),stopped:o,onStall:r})},A=33554432,O=new Map;let I=!1;const N=e=>{const t=(e.split("/").pop()||"model").replace(/[^\w.-]+/g,"_");let n=0;for(let t=0;t<e.length;t++)n=31*n+e.charCodeAt(t)>>>0;return`${n.toString(16)}-${t}`},U=e=>{const t=/^part-(\d+)$/.exec(e);return t?Number(t[1]):-1},F=async()=>{if(!I)if("undefined"!=typeof navigator&&navigator.storage?.getDirectory)try{return await navigator.storage.getDirectory()}catch{return}else I=!0},D=async(e,t)=>{try{return await e.getDirectoryHandle(t)}catch{return}},H=async e=>{if(e)try{const t=await e.getFile(),n=JSON.parse(await t.text());return{total:Number(n.total)||0,etag:`${n.etag||""}`,complete:!0===n.complete}}catch{return}},Q=async(e,t)=>{try{const n=await e.getFileHandle("meta.json",{create:!0}),a=await n.createWritable();await a.write(JSON.stringify(t)),await a.close()}catch{}},G=async function*(e){for await(const t of e)yield t},V=async e=>{try{for await(const[t,n]of G(e))"file"==n.kind&&U(t)>=0&&await e.removeEntry(t).catch(()=>{})}catch{}},J=async(e,t)=>{try{const n=await(await e.getFileHandle(t)).getFile(),a=await H(await e.getFileHandle(`${t}.json`).catch(()=>{}));if(!a)return;return{file:n,size:n.size,total:a.total,etag:a.etag,complete:a.total?n.size>=a.total:!0===a.complete}}catch{return}},K=async(e,t)=>{const n=await D(e,t);if(n){const e=await(async e=>await H(await e.getFileHandle("meta.json").catch(()=>{})))(n),t=await(async e=>{const t=[];try{for await(const[n,a]of G(e)){const e=U(n);if("file"!=a.kind||e<0)continue;const o=await a.getFile();t.push({part:{start:e,size:o.size},file:o})}}catch{return}const n=(e=>{const t=[];let n=0;for(const a of[...e].sort((e,t)=>e.start-t.start))if(a.size){if(a.start!=n)break;t.push(a),n+=a.size}return t})(t.map(e=>e.part)),a=new Set(n.map(e=>e.start)),o=t.filter(e=>a.has(e.part.start)).sort((e,t)=>e.part.start-t.part.start).map(e=>e.file);if(o.length)return{file:1==o.length?o[0]:new Blob(o),size:n.reduce((e,t)=>e+t.size,0)}})(n),a=e?.total||0,o=t?.size??0,r=t?.file??new Blob;return{file:a&&o>a?r.slice(0,a):r,size:o,total:a,etag:e?.etag||"",complete:a?o>=a:!0===e?.complete}}return await J(e,t)},X=(e,t)=>{const n=e.headers.get("content-range"),a=/bytes (\d+)-/.exec(n||""),o=/\/(\d+)\s*$/.exec(n||""),r=e.headers.get("etag")||"";if(206==e.status){const e=a?Number(a[1]):t.size,n=o?Number(o[1]):t.total;if(e!=t.size)return;if(t.total&&n&&t.total!=n)return;if(t.etag&&r&&t.etag!=r)return;return{start:e,total:n||t.total,etag:r||t.etag}}if(!t.etag||!r||t.etag==r)return{start:0,total:E(e)||t.total,etag:r||t.etag}},Y=async({dir:e,start:t,skip:n,total:a,body:o,onProgress:r,onChunk:i})=>{let c,l=t,d=0,g=n;const u=async()=>{const t=await e.getFileHandle((e=>`part-${e}`)(l),{create:!0});c=await t.createWritable(),d=0},m=async()=>{const e=c;c=void 0,e&&await e.close().catch(()=>{})};try{const e=o.getReader();for(;;){const{done:n,value:o}=await e.read();if(n)break;i();let f=0;for(g&&(f=Math.min(g,o.byteLength),g-=f);f<o.byteLength;){c||await u();const e=Math.min(A-d,o.byteLength-f);await c.write({type:"write",position:d,data:o.subarray(f,f+e)}),f+=e,d+=e,l+=e,d==A&&await m()}l>t&&r(a?l/a:0,s.download)}await m()}catch(e){throw await m(),e}return l},Z=e=>["NotAllowedError","NotSupportedError"].includes(e?.name||""),ee=e=>{const t=async()=>{const t=await F();if(t)try{return await t.getDirectoryHandle(e,{create:!0})}catch{return}};return{stored:async e=>{const n=await t();return n?await K(n,N(e)):void 0},download:async({url:n,signal:a,onProgress:o})=>{const r=`${e}|${n}`,i=O.get(r);if(i)return await i;const c=await t();if(!c)return;const l=(async(e,t,n,a)=>{const o=N(t),r=await J(e,o);if(r?.complete)return r;let i=await D(e,o);i||(await e.removeEntry(o).catch(()=>{}),await e.removeEntry(`${o}.json`).catch(()=>{}),i=await e.getDirectoryHandle(o,{create:!0}));let c=0,l=0;for(;;){if(n?.aborted)throw new Error("Cancelled");const r=await K(e,o);if(r?.complete)return r;const d=r?.size??0,g=r?.total||0,u=r?.etag||"";if(d>c)c=d,l=0;else if(++l>4)throw new Error("Model download failed");d&&g&&a(d/g,s.download);const m=g?Math.min(d+A,g)-1:d+A-1,f=new AbortController,p=()=>f.abort(),w={size:d,total:g,etag:u};let h,y=setTimeout(p,3e4);n?.addEventListener("abort",p,{once:!0});try{if(h=await fetch(t,{signal:f.signal,headers:{Range:`bytes=${d}-${m}`}}),416==h.status){if(await(h.body?.cancel().catch(()=>{})),g&&d>=g){await Q(i,{total:g,etag:u,complete:!0});continue}await V(i),await Q(i,{total:0,etag:"",complete:!1});continue}if(!h.ok||!h.body)throw await(h.body?.cancel().catch(()=>{})),new Error("Model download failed");const n=X(h,w);if(!n){await h.body.cancel().catch(()=>{}),await V(i),await Q(i,{total:0,etag:"",complete:!1});continue}await Q(i,{total:n.total,etag:n.etag,complete:!1});const r=0==n.start?d:0,s=await Y({dir:i,start:n.start+r,skip:r,total:n.total,body:h.body,onProgress:a,onChunk:()=>{clearTimeout(y),y=setTimeout(p,3e4)}});if(!n.total){await Q(i,{total:s,etag:n.etag,complete:!0});const t=await K(e,o);if(t)return t}}catch(e){if(n?.aborted)throw new Error("Cancelled");if(e instanceof DOMException&&["QuotaExceededError","NotAllowedError","NotSupportedError","InvalidStateError","TypeMismatchError","SecurityError"].includes(e.name))throw e;await new Promise(e=>setTimeout(e,250+500*l))}finally{clearTimeout(y),n?.removeEventListener("abort",p)}}})(c,n,a,o).finally(()=>{O.delete(r)});return O.set(r,l),await l},remove:async e=>{const n=await t();n&&await(async(e,t)=>{await e.removeEntry(t,{recursive:!0}).catch(()=>{}),await e.removeEntry(`${t}.json`).catch(()=>{})})(n,N(e))},purge:async()=>{const t=await F();t&&await t.removeEntry(e,{recursive:!0}).catch(()=>{})}}},te="litertlm-models",ne=ee("litert-store"),ae={num_output_candidates:1,wait_for_weight_uploads:!0,num_decode_steps_per_sync:3,sequence_batch_size:0,supported_lora_ranks:[],max_top_k:40,enable_decode_logits:!1,enable_external_embeddings:!1,use_submodel:!1,use_autosized_ringbuffers:!0},oe=async()=>{try{if("undefined"==typeof caches)return;return await caches.open(te)}catch{return}},re=new Map,ie=(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)}}))},se=async(e,t=!0)=>{const n=[];for(const a of e){if("string"==typeof a.content){n.push({role:a.role,content:a.content});continue}const e=[];for(const n of a.content)e.push("text"==n.type?{type:"text",text:n.text}:t?{type:"image",data:await b(n.url)}:{type:"text",text:"[image]"});n.push({role:a.role,content:e})}return n},ce=(e,t)=>{if(e.role!=t.role)return!1;if("string"==typeof e.content||"string"==typeof t.content)return e.content==t.content;if(e.content.length!=t.content.length)return!1;for(let n=0;n<e.content.length;n++){const a=e.content[n],o=t.content[n];if(a.type!=o.type)return!1;if("text"==a.type&&"text"==o.type){if(a.text!=o.text)return!1}else if("image"==a.type&&"image"==o.type&&a.url!=o.url)return!1}return!0},le=async function*({engine:e,mod:t,params:n,messages:a,session:o}){const r=a[a.length-1],i=M(n.limits,n.reasoning);if("user"!=r?.role)throw new Error("No user turn");const s=a.slice(0,-1),c=(await se([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&&!ce(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(!ce(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 se(s,!1);o.conversation=void 0,o.sent=[],d=await e.createConversation({sessionConfig:{maxOutputTokens:i.max_tokens,samplerParams:{type:t.SamplerType.TOP_K,temperature:i.temperature,k:40,p:.95}},preface:{...a.length?{messages:a}:{},extra_context:{enable_thinking:n.reasoning}},prefillPrefaceOnInit:a.length>0}),o.conversation=d,o.reasoning=n.reasoning,o.maxTokens=i.max_tokens,o.sent=[...s]}o.sent.push(r);let g,u=!1,m="";const f=()=>{g&&g.cancel().catch(()=>{});try{d.cancel()}catch{}};n.signal?.addEventListener("abort",f,{once:!0});try{if(n.signal?.aborted)return;const e=d.sendMessageStreaming(c).getReader();for(g=e;;){const{done:t,value:n}=await e.read();if(t)break;const a={};let o="string"==typeof n.content?n.content:"";if(Array.isArray(n.content))for(const e of n.content)"text"==e.type&&(o+=e.text);o&&(a.content=o,m+=o);const r=n.channels?.thought;r&&(a.reasoning=r),(a.content||a.reasoning)&&(yield{choices:[{delta:a}]})}u=!0}finally{n.signal?.removeEventListener("abort",f),u&&m?o.sent.push({role:"assistant",content:m}):(o.conversation=void 0,o.sent=[])}},de={backend:i.gpu,startLLM:async({model:e,limits:t,onProgress:a,signal:o,logging:r})=>{const c=e.litert;if(!c)throw new Error("Model has no LiteRT artifact");const l=await n.e(520).then(()=>n(507));if(await l.getOrLoadGlobalLiteRtLm(h),o?.aborted)throw new Error("Cancelled");const d=T(c.repo,c.file),g=await oe();let u;const m=g?await g.match(d):void 0;if(m?.body)u=m.body;else{const e=await ne.stored(d);e?.complete&&(u=e.file.stream())}if(!u){const e=await ne.download({url:d,signal:o,onProgress:a}).catch(e=>{if(o?.aborted)throw new Error("Cancelled");if(!Z(e))throw e;r&&console.log("Local model store failed",e)});e&&(u=e.file.stream())}if(!u&&g)try{await(async(e,t,n,a)=>{const o=re.get(e);if(o)return await o;const r=(async()=>{const o=await fetch(e,{signal:n});if(!o.ok||!o.body)throw new Error("Model download failed");const r=E(o),i=ie(o.body,r,a);await t.put(e,new Response(i,{headers:{"content-type":"application/octet-stream",...r?{"content-length":`${r}`}:{}}}))})().finally(()=>{re.delete(e)});return re.set(e,r),await r})(d,g,o,a);const e=await g.match(d);e?.body&&(u=e.body)}catch(e){if(o?.aborted)throw new Error("Cancelled");r&&console.log("Local model cache failed",e)}if(u||(u=await(async(e,t,n)=>{const a=await fetch(e,{signal:t});if(!a.ok||!a.body)throw new Error("Model download failed");return ie(a.body,E(a),n)})(d,o,a)),o?.aborted)throw new Error("Cancelled");a(1,s.load);const f=await l.Engine.create({model:u,mainExecutorSettings:{maxNumTokens:4096,...c.vision?{maxNumImages:Math.max(1,t.maxImages)}:{},backendConfig:ae},...c.vision?{visionBackend:l.Backend.GPU}:{}});if(o?.aborted)throw await f.delete(),new Error("Cancelled");const p={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})=>{const r=()=>{try{o.conversation?.cancel()}catch{}o.conversation=void 0,o.sent=[]};try{return await B({model:t,params:a,stopped:()=>!!a.signal?.aborted,onStall:r,start:async r=>{if(a.signal?.aborted)throw new Error("Cancelled");if(C(r)&&!t.litert?.vision)throw new Error("Model does not accept images");return le({engine:e,mod:n,params:a,messages:r,session:o})}})}catch(e){throw r(),e}})({engine:f,model:e,mod:l,params:t,session:p}),unload:async()=>{p.conversation=void 0,p.sent=[],await f.delete()}}},cached:async e=>{const t=e.litert;if(!t)return!1;const n=T(t.repo,t.file),[a,o]=await Promise.all([oe(),ne.stored(n)]);if(o?.complete)return!0;try{return!(!a||!await a.match(n))}catch{return!1}},hasCache:async()=>{try{for(const e of l){const t=e.litert;if(!t)continue;if(await ne.stored(T(t.repo,t.file)))return!0}const e=await oe();return!!e&&(await e.keys()).length>0}catch{return!1}},remove:async e=>{try{const t=e.litert;if(!t)return;const n=T(t.repo,t.file),a=await oe();await ne.remove(n),a&&await a.delete(n)}catch{}},purge:async()=>{try{await ne.purge(),"undefined"!=typeof caches&&await caches.delete(te)}catch{}}},ge=ee("wllama-store");let ue,me,fe="",pe=!1;const we=e=>`${e.wasm}|${e.compatWasm}|${e.compatWorker}`,he=e=>{const t={wasm:f,compatWasm:p,compatWorker:w};return{wasm:e?.wasm||t.wasm,compatWasm:e?.compatWasm||t.compatWasm,compatWorker:e?.compatWorker||t.compatWorker}},ye=e=>{const t=e=>(...t)=>{pe&&("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)}},be=async()=>{if(ue)return ue;const e=await n.e(924).then(()=>n(577)),t=me||he(),a=new e.Wllama({default:t.wasm},{logger:ye(e.LoggerWithoutDebug)});return me||(me=t,fe=we(t)),a.setCompat({wasm:t.compatWasm,worker:t.compatWorker}),ue=a,a},ve=async()=>{const e=ue;ue=void 0;try{await(e?.exit())}catch{}},xe=(e,t)=>e.name.includes(t)||!!e.metadata?.originalURL?.includes(t),ke=e=>{const t=e.cpu;return t?[{name:t.file,url:T(t.repo,t.file)},...t.mmproj?[{name:t.mmproj.file,url:T(t.repo,t.mmproj.file)}]:[]]:[]},_e=async e=>{const t=ke(e);if(!t.length)return!1;for(const e of t){const t=await ge.stored(e.url);if(!t?.complete)return!1}return!0},Me=async(e,t)=>{const n=await e.cacheManager.list();return ke(t).every(e=>n.some(t=>xe(t,e.name)))},Te=async({model:e,signal:t,onProgress:n})=>{const a=ke(e),o=[];for(let e=0;e<a.length;e++){if(t?.aborted)throw new Error("Cancelled");const r=await ge.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},Ee=async e=>{const t=[];for(const n of e){if("string"==typeof n.content){"system"==n.role?t.push({role:"system",content:n.content}):"assistant"==n.role?t.push({role:"assistant",content:n.content}):t.push({role:"user",content:n.content});continue}const e=[];for(const t of n.content)e.push("text"==t.type?{type:"text",text:t.text}:{type:"image",data:await b(t.url)});t.push({role:"user",content:e})}return t},Ce={backend:i.cpu,startLLM:async({model:e,limits:t,onProgress:n,signal:a,logging:o,assets:r})=>{pe=!0===o;const c=e.cpu;if(!c)throw new Error("Model has no CPU artifact");const l=he(r),d=we(l);ue&&d!=fe&&await ve(),me=l,fe=d;const g=(u="undefined"!=typeof navigator&&navigator.hardwareConcurrency||4,m=t.maxThreads,Math.max(1,Math.min(Math.max(2,Math.min(8,(u||4)-1)),m)));var u,m;const f=await be(),p=await _e(e),w=c.mmproj?.file,h=T(c.repo,c.file),y=w?T(c.repo,w):"";if(a?.aborted)throw await ve(),new Error("Cancelled");if(!p&&w){const e=await f.cacheManager.list(),t=e.some(e=>xe(e,c.file)),n=e.some(e=>xe(e,w));t&&!n&&await f.cacheManager.deleteMany(e=>xe(e,c.file))}const b={n_ctx:8192,n_threads:g,reasoning_budget_tokens:t.maxThinkingTokens,n_gpu_layers:0,jinja:!0};try{let t=p?await Te({model:e,signal:a,onProgress:n}):void 0;t||p||await Me(f,e).catch(()=>!1)||(t=await Te({model:e,signal:a,onProgress:n}).catch(e=>{if(a?.aborted)throw new Error("Cancelled");if(!Z(e))throw e;o&&console.log("Local model store failed",e)})),t?(n(1,s.load),await f.loadModel(t,b)):await f.loadModelFromUrl(y?{url:h,mmprojUrl:y}:h,{...b,useCache:!0,signal:a,progressCallback:({loaded:e,total:t})=>{const a=t?e/t:0;n(a,a>=1?s.load:s.download)}})}catch(e){throw await ve(),e}if(a?.aborted)throw await ve(),new Error("Cancelled");if(!f.getLoadedContextInfo().n_ctx)throw await ve(),new Error("Failed to create model context");return{model:e,backend:i.cpu,contextSize:8192,chat:t=>(async(e,t,n)=>{if(pe=!0===n.logging,e.isModelLoaded())return await B({model:t,params:n,stopped:()=>!!n.signal?.aborted,start:async a=>{if(C(a)&&!e.supportInputModality("image"))throw new Error("Model does not accept images");return await e.createChatCompletion({messages:await Ee(a),...M(n.limits,n.reasoning),abortSignal:n.signal,...t.reasoning?{chat_template_kwargs:{enable_thinking:n.reasoning}}:{}})}})})(f,e,t),unload:async()=>{if(ue==f)await ve();else try{await f.exit()}catch{}}}},cached:async e=>{try{return!!e.cpu&&(!!await _e(e)||await Me(await be(),e))}catch{return!1}},hasCache:async()=>{try{for(const e of l.flatMap(e=>ke(e)))if(await ge.stored(e.url))return!0;const e=await be();return(await e.cacheManager.list()).length>0}catch{return!1}},remove:async e=>{const t=ke(e);try{for(const e of t)await ge.remove(e.url)}catch{}try{const e=await be();await e.cacheManager.deleteMany(e=>t.some(t=>xe(e,t.name)))}catch{}},purge:async()=>{try{await ge.purge()}catch{}try{const e=await be();await e.cacheManager.clear()}catch{}}},Le=((e={})=>{const t=e.engines?.cpu||Ce,n=e.engines?.litert||de;let a,o,c,d,g,u,m,f=i.none,p=r.idle,w=[],h=0,y={percent:0,phase:s.download};const b=a=>{if(e.backend!=i.none)return e.backend==i.cpu?a.cpu?t:void 0:e.backend==i.gpu?a.litert?n:void 0:a.litert&&"undefined"!=typeof navigator&&navigator.gpu&&"undefined"!=typeof WebAssembly?n:!a.cpu||"undefined"==typeof window&&"undefined"==typeof self&&"undefined"==typeof Worker||"undefined"==typeof WebAssembly?void 0:t},v=(e,t)=>{y={percent:Math.max(0,Math.min(1,e)),phase:t||s.download};for(const e of[...w])e.report(y.percent,y.phase)},x=e=>{p=e},k=()=>{h++,u?.abort(),u=void 0,g=void 0,p==r.downloading&&x(r.idle)},_=(e,t)=>{const n=d;return n?new Promise(a=>{let o=!1;const r={report:e,settle:e=>{o||(o=!0,t?.removeEventListener("abort",i),a(e))}},i=()=>{(e=>{w=w.filter(t=>t!=e),queueMicrotask(()=>{!w.length&&d&&k()})})(r),r.settle(!1)};w.push(r),t?.aborted?i():t?.addEventListener("abort",i,{once:!0}),n.then(r.settle,()=>r.settle(!1))}):Promise.resolve(!1)},M=()=>p==r.ready&&!!a&&!!o;return{load:async({model:e,limits:t,assets:n,logging:i,signal:m,onProgress:p})=>{if(m?.aborted)return!1;if(M()&&o?.id==e.id)return!0;if(d&&g==e.id)return p(y.percent,y.phase),await _(p,m);d&&(k(),w=[],await d.catch(()=>!1));const T=++h,E=(async()=>{await(async()=>{try{if("undefined"==typeof navigator)return;await(navigator.storage?.persist?.())}catch{}})();const d=b(e);if(T!=h)return!1;if(!d)return x(r.unsupported),!1;if(await(async(e,t)=>{if(o&&o.id!=e.id){const e=o,t=c,n=a;a=void 0,o=void 0,c=void 0,await(n?.unload()),await(t?.remove(e))}if(await t.hasCache())for(const n of l)n.id!=e.id&&b(n)==t&&await t.remove(n)})(e,d),T!=h)return!1;const g=await d.cached(e);if(T!=h)return!1;x(r.downloading),v(0,s.download),g&&v(1,s.load);const m=new AbortController;u=m;try{const l=await d.startLLM({model:e,limits:t,logging:i,signal:m.signal,assets:n,onProgress:(e,t)=>{T==h&&v(e,t)}});return T!=h||m.signal.aborted?(await l.unload(),!1):(a=l,o=e,c=d,f=d.backend,x(r.ready),v(1,s.load),!0)}catch(e){return T!=h||(a=void 0,o=void 0,c=void 0,i&&console.log("Local model failed",e),x(r.error)),!1}finally{u==m&&(u=void 0)}})().finally(()=>{d==E&&(d=void 0,g=void 0,w=[])});return d=E,g=e.id,await _(p,m)},chat:async e=>{if(!M()||!a)return;for(;m;)await m.catch(()=>{});if(!M()||!a)return;const t=a.chat(e);m=t;try{return await t}finally{m==t&&(m=void 0)}},unload:async()=>{const e=a;k(),a=void 0,o=void 0,c=void 0,x(r.idle),await(e?.unload())},ready:M,loading:()=>!M()&&p==r.downloading,status:()=>p,backend:()=>f,model:()=>o}})(),Pe=(e,t,n)=>{const a=e.status(),i=d(t);return{availability:a==r.ready?o.online:a==r.error||a==r.unsupported?o.offline:o.away,status:a,backend:e.backend(),supportsImages:!!i&&m(i),ready:e.ready(),loading:e.loading(),requesting:n.requesting,phase:n.phase,percent:n.percent,...n.error?{error:n.error}:{}}},Re=(e=>{let t,n=0;const o=t=>t.input.onStatus?.(Pe(e,t.model??t.input.model,t));return i=>{t?.controller.abort();const c={token:++n,input:i,controller:new AbortController,percent:e.ready()?100:0,phase:e.ready()?s.load:s.download,requesting:!1,cancelled:!1};return t=c,o(c),(async t=>{const{input:r}=t,i=!0===r.logging,c=await u(r.model);if(t.model=c?.id,t.token!=n)return;const l=r.messages.some(e=>e.role!=a.system&&"string"==typeof e.content&&!!e.content.trim()),d=r.messages.some(e=>e.role==a.user&&!!e.images?.some(e=>"string"==typeof e&&!!e)),g=l||d;if(!c)return t.error="Unknown model",void o(t);if(!g)return void o(t);if(d&&!l&&!m(c))return t.error="Model does not accept images",void o(t);const f=_(r.limits,!!c.litert),p=await e.load({model:c,limits:f,logging:i,signal:t.controller.signal,onProgress:(e,a)=>{t.token==n&&(t.percent=Math.round(100*e),t.phase=a,o(t))}}).catch(e=>(t.token==n&&(t.error=`${e?.message||`${e}`}`),!1));if(t.token!=n)return;if(t.cancelled)return;if(!p)return t.error||(t.error="Model not ready"),void o(t);const w=m(c),h=d&&!w?r.messages.map(({images:e,...t})=>t):r.messages;if(d&&!w&&!l)return t.error="Model does not accept images",void o(t);t.requesting=!0,t.error=void 0,t.percent=100,t.phase=s.load,o(t);let y,b="",v="";try{y=await e.chat({messages:h,limits:f,reasoning:!0===r.reasoning,logging:i,onReason:e=>{t.token!=n||t.cancelled||(b+=e,r.onThinking?.(b))},onText:e=>{t.token!=n||t.cancelled||(v+=e,r.onResponse?.(v))},signal:t.controller.signal})}catch(e){if(t.token!=n)return;return t.requesting=!1,t.error=`${e?.message||`${e}`}`,void o(t)}t.token==n&&(t.requesting=!1,y||t.cancelled||(t.error="No reply"),o(t))})(c),()=>(t=>{t.token==n&&(t.cancelled=!0,t.controller.abort(),e.status()!=r.ready?queueMicrotask(()=>o(t)):e.unload().then(()=>o(t)))})(c)}})(Le),$e=(je=Le,async(e={})=>{const t=await u(e.model),n=n=>e.onStatus?.(Pe(je,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=je.ready()&&je.model()?.id==t.id;return await je.load({model:t,limits:_(e.limits,!!t.litert),logging:!0===e.logging,onProgress:(e,t)=>{o=Math.round(100*e),r=t,n({requesting:!1,phase:r,percent:o})}}).catch(e=>(a=`${e?.message||`${e}`}`,!1))?(i&&n({requesting:!1,phase:s.load,percent:100}),!0):(n({requesting:!1,phase:r,percent:o,error:a||"Model not ready"}),!1)});var je;export{c as ChatModelId,o as ChatStatus,i as LlmBackend,s as LlmPhase,a as LlmRole,r as LlmStatus,Re as createChat,$e as preloadModel};
package/dist/chat.d.ts CHANGED
@@ -2,7 +2,7 @@ import type { CancelChat, ChatInput, ChatPreloadInput, Runtime } from './types';
2
2
  /** Build a one-shot chat function over one runtime */
3
3
  declare const createChatOn: (service: Runtime) => ((input: ChatInput) => CancelChat);
4
4
  /** Build a model preload over one runtime */
5
- declare const createPreloadOn: (service: Runtime) => (input: ChatPreloadInput) => Promise<boolean>;
5
+ declare const createPreloadOn: (service: Runtime) => (input?: ChatPreloadInput) => Promise<boolean>;
6
6
  declare const createChat: (input: ChatInput) => CancelChat;
7
- declare const preloadModel: (input: ChatPreloadInput) => Promise<boolean>;
7
+ declare const preloadModel: (input?: ChatPreloadInput) => Promise<boolean>;
8
8
  export { createChat, createChatOn, createPreloadOn, preloadModel };
@@ -1,8 +1,8 @@
1
1
  import type { ChatMessage, ChatModel, EngineMessage, EngineRunParams, LlmLimits, StreamReplyParams } from '../types';
2
- /** Default token, prompt and thread budgets */
2
+ /** Default token, prompt and thread budgets of the CPU engine */
3
3
  declare const defaultLlmLimits: LlmLimits;
4
- /** Merge developer budgets over the defaults */
5
- declare const resolveLlmLimits: (overrides?: Partial<LlmLimits>) => LlmLimits;
4
+ /** Merge developer budgets over the backend defaults */
5
+ declare const resolveLlmLimits: (overrides?: Partial<LlmLimits>, gpu?: boolean) => LlmLimits;
6
6
  /** Sampling options shared by every engine stream */
7
7
  declare const replyOptions: (limits: LlmLimits, reasoning: boolean) => {
8
8
  stream: true;
@@ -28,8 +28,8 @@ declare const
28
28
  /** Log an engine chat call with all message text censored */
29
29
  logChatCall: (model: ChatModel, messages: EngineMessage[]) => void,
30
30
  /** Stream one reply, logging it and returning undefined when stopped */
31
- streamReply: ({ model, logging, onReason, onText, start, stopped, }: StreamReplyParams) => Promise<string | undefined>;
31
+ streamReply: ({ model, logging, onReason, onText, start, stopped, firstTimeoutMs, idleTimeoutMs, onStall, }: StreamReplyParams) => Promise<string | undefined>;
32
32
  declare const
33
33
  /** Build the prompt and stream one reply through an engine */
34
- runEngineChat: ({ model, params, start, stopped, }: EngineRunParams) => Promise<string | undefined>;
34
+ runEngineChat: ({ model, params, start, stopped, onStall, }: EngineRunParams) => Promise<string | undefined>;
35
35
  export { buildMessages, createThinkSplitter, logChatCall, runEngineChat, streamReply, replyOptions, defaultLlmLimits, resolveLlmLimits, hasImages, modelUrl, contentLength, };
@@ -1,7 +1,11 @@
1
- import type { EngineMessage, LitertMessage, LlmEngine, StartLlmParams } from '../types';
1
+ import type { EngineChunk, EngineMessage, LitertMessage, LlmEngine, StartLlmParams, StreamLitertParams } from '../types';
2
2
  /** Fetch one model file into the cache, once per URL */
3
3
  declare const cacheModel: (url: string, cache: Cache, signal: AbortSignal | undefined, onProgress: StartLlmParams[`onProgress`]) => Promise<void>;
4
4
  /** Map built messages to LiteRT turns */
5
- declare const toLitertMessages: (messages: EngineMessage[]) => Promise<LitertMessage[]>;
5
+ declare const toLitertMessages: (messages: EngineMessage[], keepImages?: boolean) => Promise<LitertMessage[]>;
6
+ /** Whether the live conversation already holds the incoming history */
7
+ declare const continuesHistory: (sent: EngineMessage[], history: EngineMessage[]) => boolean;
8
+ /** Stream one LiteRT reply as engine chunks */
9
+ declare const streamLitert: ({ engine, mod, params, messages, session, }: StreamLitertParams) => AsyncGenerator<EngineChunk>;
6
10
  declare const litertEngine: LlmEngine;
7
- export { cacheModel, litertEngine, toLitertMessages, };
11
+ export { cacheModel, continuesHistory, litertEngine, streamLitert, toLitertMessages, };
package/dist/models.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { ChatModelId } from './enums';
1
2
  import type { ChatModel } from './types';
2
3
  declare const llmModels: ChatModel[],
3
4
  /** Context window requested from the CPU engine, a 32k KV cache needs 3.5 GB */
@@ -6,6 +7,12 @@ cpuContextWindow = 8192,
6
7
  litertContextWindow = 4096,
7
8
  /** Look up a catalog entry by id */
8
9
  findLlmModel: (id?: string) => ChatModel | undefined,
10
+ /** Whether this browser provides a WebGPU adapter */
11
+ gpuAdapter: () => Promise<boolean>,
12
+ /** Default model, the GPU one when a WebGPU adapter exists */
13
+ defaultLlmModel: () => Promise<ChatModelId | undefined>,
14
+ /** Resolve the requested model or the default of this device */
15
+ resolveLlmModel: (id?: ChatModelId) => Promise<ChatModel | undefined>,
9
16
  /** Whether a model accepts images */
10
17
  supportsImages: (model: ChatModel) => boolean;
11
- export { llmModels, cpuContextWindow, litertContextWindow, findLlmModel, supportsImages, };
18
+ export { llmModels, cpuContextWindow, litertContextWindow, findLlmModel, gpuAdapter, defaultLlmModel, resolveLlmModel, supportsImages, };
@@ -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 a(n){const o=t[n];if(void 0!==o)return o.exports;const r=t[n]={exports:{}};return e[n](r,r.exports,a),r.exports}a.m=e,a.d=(e,t)=>{for(var n in t)a.o(t,n)&&!a.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},a.f={},a.e=e=>{const t=[];return a.f.require(e,t),Promise.all(t)},a.u=e=>({520:"engine-litert",924:"engine-wllama"}[e]+".node.min.js"),a.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),a.r=e=>{Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{const e={792:1};a.f.require=(t,n)=>{if(!e[t]){const n=require("./"+a.u(t));e[t]||(t=>{const n=t.modules,o=t.ids,r=t.runtime;for(var i in n)a.o(n,i)&&(a.m[i]=n[i]);r&&r(a);for(var s=0;s<o.length;s++)e[o[s]]=1})(n)}}})();let n={};var o,r,i,s,c,l;a.r(n),a.d(n,{ChatModelId:()=>l,ChatStatus:()=>r,LlmBackend:()=>s,LlmPhase:()=>c,LlmRole:()=>o,LlmStatus:()=>i,createChat:()=>Me,preloadModel:()=>Ee}),function(e){e.system="system",e.user="user",e.assistant="assistant"}(o||(o={})),function(e){e[e.online=0]="online",e[e.away=1]="away",e[e.offline=2]="offline"}(r||(r={})),function(e){e[e.idle=0]="idle",e[e.downloading=1]="downloading",e[e.ready=2]="ready",e[e.error=3]="error",e[e.unsupported=4]="unsupported"}(i||(i={})),function(e){e.cpu="cpu",e.gpu="gpu",e.none="none"}(s||(s={})),function(e){e.download="download",e.load="load"}(c||(c={})),function(e){e.qwen3_vl_2b="qwen3-vl-2b",e.gemma4_e2b="gemma4-e2b"}(l||(l={}));const d=[{id:l.qwen3_vl_2b,reasoning:!0,cpu:{repo:"Qwen/Qwen3-VL-2B-Thinking-GGUF",file:"Qwen3VL-2B-Thinking-Q4_K_M.gguf",mmproj:{file:"mmproj-Qwen3VL-2B-Thinking-Q8_0.gguf"}}},{id:l.gemma4_e2b,reasoning:!0,litert:{repo:"litert-community/gemma-4-E2B-it-litert-lm",file:"gemma-4-E2B-it.litertlm",vision:!0}}],g=e=>e?d.find(t=>t.id==e):void 0,u=e=>!!e.cpu?.mmproj||!!e.litert?.vision,m=require("node:path"),f=()=>{return e=(0,m.join)(__dirname,"../assets/wllama"),{wasm:(0,m.join)(e,"wllama.wasm"),compatWasm:(0,m.join)(e,"compat/wllama.wasm"),compatWorker:(0,m.join)(e,"compat/wllama.js")};var e},w=()=>{return e=(0,m.join)(__dirname,"../assets/litert"),{script:(0,m.join)(e,"litertlm_wasm_internal.js"),wasm:(0,m.join)(e,"litertlm_wasm_internal.wasm")};var e},p=288,h=async e=>{if(!e.startsWith("data:"))return await(await fetch(e)).arrayBuffer();const t=e.indexOf(","),a=t>=0?e.slice(0,t):"";if(t<0)throw new Error("Invalid image data");if(!a.includes(";base64"))return await(await fetch(e)).arrayBuffer();const n=atob(e.slice(t+1)),o=new Uint8Array(n.length);for(let e=0;e<n.length;e++)o[e]=n.charCodeAt(e);return o.buffer},y=async e=>{if(!e||"function"!=typeof fetch||"function"!=typeof createImageBitmap)return e;try{const t=await fetch(e),a=await createImageBitmap(await t.blob()),n=Math.min(a.width,a.height),o=Math.floor((a.width-n)/2),r=Math.floor((a.height-n)/2),i=((e,t)=>{if("undefined"!=typeof OffscreenCanvas)return new OffscreenCanvas(e,t);if("undefined"==typeof document)return;const a=document.createElement("canvas");return a.width=e,a.height=t,a})(p,p),s=i?i.getContext("2d"):null;if(!i||!s||n<1)return a.close(),e;if(s.drawImage(a,o,r,n,n,0,0,p,p),a.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 a=new FileReader;a.onerror=()=>t(""),a.onload=()=>t(`${a.result}`),a.readAsDataURL(e)}))(t)||e}return i.toDataURL("image/jpeg",.85)}catch{return e}},b={maxReplyTokens:200,maxThinkingTokens:64,maxReasoningTokens:150,maxImages:2,maxContentLength:1e3,promptCharBudget:1e4,maxThreads:2},v=(e,t,a)=>"number"==typeof e&&Number.isFinite(e)&&e>=a?Math.floor(e):t,x=(e={})=>{const t=b;return{maxReplyTokens:v(e.maxReplyTokens,t.maxReplyTokens,1),maxThinkingTokens:v(e.maxThinkingTokens,t.maxThinkingTokens,1),maxReasoningTokens:v(e.maxReasoningTokens,t.maxReasoningTokens,1),maxImages:v(e.maxImages,t.maxImages,0),maxContentLength:v(e.maxContentLength,t.maxContentLength,1),promptCharBudget:v(e.promptCharBudget,t.promptCharBudget,1),maxThreads:v(e.maxThreads,t.maxThreads,1)}},k=(e,t)=>({stream:!0,temperature:.7,max_tokens:t?e.maxReasoningTokens:e.maxReplyTokens}),_=(e,t)=>`https://huggingface.co/${e}/resolve/main/${t}`,M=e=>Number(e.headers.get("content-length"))||0,E=e=>e.some(e=>Array.isArray(e.content)&&e.content.some(e=>"image"==e.type)),C=["<|channel>thought\n","<|channel>thought","..."],T=["<channel|>","..."],L=(e,t)=>t.length?[...e?[{type:"text",text:e}]:[],...t.map(e=>({type:"image",url:e}))]:e,j=e=>"string"==typeof e?e?[{type:"text",text:e}]:[]:e,$=(e,t)=>{if("string"==typeof e&&"string"==typeof t)return`${e}\n${t}`;const a=[];for(const n of[...j(e),{type:"text",text:"\n"},...j(t)]){const e=a[a.length-1];"text"==n.type&&"text"==e?.type?e.text+=n.text:a.push(n)}return a},P=async({model:e,logging:t,onReason:a,onText:n,start:o,stopped:r})=>{let i="",s="",c="";const l=((e,t)=>{let a="",n=!1;const o=(e,t)=>{let a=-1,n="";for(const o of t){const t=e.indexOf(o);t>=0&&(a<0||t<a)&&(a=t,n=o)}return{at:a,tag:n}},r=(e,t)=>{let a="";for(const n of t)for(let t=Math.min(e.length,n.length-1);t>a.length;t--)if(e.endsWith(n.slice(0,t))){a=e.slice(e.length-t);break}return a};return{feed:i=>{let s=a+i;for(a="";s;){if(n){const{at:t,tag:i}=o(s,T);if(t>=0){t&&e(s.slice(0,t)),s=s.slice(t+i.length),n=!1;continue}const c=r(s,T);e(s.slice(0,s.length-c.length)),a=c;break}{const{at:e,tag:i}=o(s,C);if(e>=0){e&&t(s.slice(0,e)),s=s.slice(e+i.length),s.startsWith("\n")&&(s=s.slice(1)),n=!0;continue}const c=r(s,C);t(s.slice(0,s.length-c.length)),a=c;break}}},flush:()=>{a&&(n?e(a):t(a),a="")}}})(e=>{s+=e,a(e)},e=>{i+=e,n(e)});try{const e=await o();for await(const t of e){if(r())break;const e=t?.choices?.[0],n=e?.delta,o="string"==typeof n?.reasoning_content?n.reasoning_content:"string"==typeof n?.reasoning?n.reasoning:"";e?.finish_reason&&(c=e.finish_reason),o&&(s+=o,a(o)),n?.content&&l.feed(n.content)}l.flush()}catch(e){t&&console.log("Local chat failed",e)}const d=r()?void 0:i||void 0;return t&&(console.log("Local chat reply",e.id,d?"****":""),console.log("Local chat finish",e.id,c||"none",i.length,s.length)),d},R=async({model:e,params:t,start:a,stopped:n})=>{const r=await(async e=>{const t=[];for(const a of e){if("string"==typeof a.content){t.push(a);continue}const e=[];for(const t of a.content)e.push("image"==t.type?{type:"image",url:await y(t.url)}:t);t.push({role:"user",content:e})}return t})(((e,t=b)=>{const a=t.maxContentLength,n=e.filter(e=>"string"==typeof e.content&&!!e.content.trim()||!(e.role!=o.user||!e.images?.length)),r=[],i=[];let s=0,c=t.maxImages;for(let e=n.length-1;e>=0;e--){const l=n[e],d=("string"==typeof l.content?l.content:"").slice(0,a),g=l.role==o.user?(l.images||[]).filter(e=>"string"==typeof e&&!!e).slice(0,Math.max(0,c)):[];if(d||g.length){if(d&&s+d.length>t.promptCharBudget)break;s+=d.length,c-=g.length,l.role==o.system?r.unshift(d):l.role==o.assistant?i.unshift({role:"assistant",content:d}):i.unshift({role:"user",content:L(d,g)})}}for(;i.length&&"user"!=i[0].role;)i.shift();const l=[];for(const e of i){const t=l[l.length-1];"assistant"==t?.role&&"assistant"==e.role?t.content=`${t.content}\n${e.content}`:"user"==t?.role&&"user"==e.role?t.content=$(t.content,e.content):"assistant"==e.role?l.push({role:"assistant",content:e.content}):l.push({role:"user",content:e.content})}const d=r.join("\n\n"),g=[];return d&&g.push({role:"system",content:d}),g.push(...l),g})(t.messages,t.limits));return t.logging&&((e,t)=>{console.log("Local chat call",e.id,t.map(({role:e,content:t})=>({role:e,content:"****",...Array.isArray(t)?{images:t.filter(e=>"image"==e.type).length}:{}})))})(e,r),await P({model:e,logging:t.logging,onReason:t.onReason,onText:t.onText,start:()=>a(r),stopped:n})},q=33554432,W=new Map;let S=!1;const B=e=>{const t=(e.split("/").pop()||"model").replace(/[^\w.-]+/g,"_");let a=0;for(let t=0;t<e.length;t++)a=31*a+e.charCodeAt(t)>>>0;return`${a.toString(16)}-${t}`},z=e=>{const t=/^part-(\d+)$/.exec(e);return t?Number(t[1]):-1},A=async()=>{if(!S)if("undefined"!=typeof navigator&&navigator.storage?.getDirectory)try{return await navigator.storage.getDirectory()}catch{return}else S=!0},O=async(e,t)=>{try{return await e.getDirectoryHandle(t)}catch{return}},I=async e=>{if(e)try{const t=await e.getFile(),a=JSON.parse(await t.text());return{total:Number(a.total)||0,etag:`${a.etag||""}`,complete:!0===a.complete}}catch{return}},N=async(e,t)=>{try{const a=await e.getFileHandle("meta.json",{create:!0}),n=await a.createWritable();await n.write(JSON.stringify(t)),await n.close()}catch{}},F=async function*(e){for await(const t of e)yield t},U=async e=>{try{for await(const[t,a]of F(e))"file"==a.kind&&z(t)>=0&&await e.removeEntry(t).catch(()=>{})}catch{}},D=async(e,t)=>{try{const a=await(await e.getFileHandle(t)).getFile(),n=await I(await e.getFileHandle(`${t}.json`).catch(()=>{}));if(!n)return;return{file:a,size:a.size,total:n.total,etag:n.etag,complete:n.total?a.size>=n.total:!0===n.complete}}catch{return}},H=async(e,t)=>{const a=await O(e,t);if(a){const e=await(async e=>await I(await e.getFileHandle("meta.json").catch(()=>{})))(a),t=await(async e=>{const t=[];try{for await(const[a,n]of F(e)){const e=z(a);if("file"!=n.kind||e<0)continue;const o=await n.getFile();t.push({part:{start:e,size:o.size},file:o})}}catch{return}const a=(e=>{const t=[];let a=0;for(const n of[...e].sort((e,t)=>e.start-t.start))if(n.size){if(n.start!=a)break;t.push(n),a+=n.size}return t})(t.map(e=>e.part)),n=new Set(a.map(e=>e.start)),o=t.filter(e=>n.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:a.reduce((e,t)=>e+t.size,0)}})(a),n=e?.total||0,o=t?.size??0,r=t?.file??new Blob;return{file:n&&o>n?r.slice(0,n):r,size:o,total:n,etag:e?.etag||"",complete:n?o>=n:!0===e?.complete}}return await D(e,t)},Q=(e,t)=>{const a=e.headers.get("content-range"),n=/bytes (\d+)-/.exec(a||""),o=/\/(\d+)\s*$/.exec(a||""),r=e.headers.get("etag")||"";if(206==e.status){const e=n?Number(n[1]):t.size,a=o?Number(o[1]):t.total;if(e!=t.size)return;if(t.total&&a&&t.total!=a)return;if(t.etag&&r&&t.etag!=r)return;return{start:e,total:a||t.total,etag:r||t.etag}}if(!t.etag||!r||t.etag==r)return{start:0,total:M(e)||t.total,etag:r||t.etag}},G=async({dir:e,start:t,skip:a,total:n,body:o,onProgress:r,onChunk:i})=>{let s,l=t,d=0,g=a;const u=async()=>{const t=await e.getFileHandle((e=>`part-${e}`)(l),{create:!0});s=await t.createWritable(),d=0},m=async()=>{const e=s;s=void 0,e&&await e.close().catch(()=>{})};try{const e=o.getReader();for(;;){const{done:a,value:o}=await e.read();if(a)break;i();let f=0;for(g&&(f=Math.min(g,o.byteLength),g-=f);f<o.byteLength;){s||await u();const e=Math.min(q-d,o.byteLength-f);await s.write({type:"write",position:d,data:o.subarray(f,f+e)}),f+=e,d+=e,l+=e,d==q&&await m()}l>t&&r(n?l/n:0,c.download)}await m()}catch(e){throw await m(),e}return l},V=e=>["NotAllowedError","NotSupportedError"].includes(e?.name||""),J=e=>{const t=async()=>{const t=await A();if(t)try{return await t.getDirectoryHandle(e,{create:!0})}catch{return}};return{stored:async e=>{const a=await t();return a?await H(a,B(e)):void 0},download:async({url:a,signal:n,onProgress:o})=>{const r=`${e}|${a}`,i=W.get(r);if(i)return await i;const s=await t();if(!s)return;const l=(async(e,t,a,n)=>{const o=B(t),r=await D(e,o);if(r?.complete)return r;let i=await O(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(a?.aborted)throw new Error("Cancelled");const r=await H(e,o);if(r?.complete)return r;const d=r?.size??0,g=r?.total||0,u=r?.etag||"";if(d>s)s=d,l=0;else if(++l>4)throw new Error("Model download failed");d&&g&&n(d/g,c.download);const m=g?Math.min(d+q,g)-1:d+q-1,f=new AbortController,w=()=>f.abort(),p={size:d,total:g,etag:u};let h,y=setTimeout(w,3e4);a?.addEventListener("abort",w,{once:!0});try{if(h=await fetch(t,{signal:f.signal,headers:{Range:`bytes=${d}-${m}`}}),416==h.status){if(await(h.body?.cancel().catch(()=>{})),g&&d>=g){await N(i,{total:g,etag:u,complete:!0});continue}await U(i),await N(i,{total:0,etag:"",complete:!1});continue}if(!h.ok||!h.body)throw await(h.body?.cancel().catch(()=>{})),new Error("Model download failed");const a=Q(h,p);if(!a){await h.body.cancel().catch(()=>{}),await U(i),await N(i,{total:0,etag:"",complete:!1});continue}await N(i,{total:a.total,etag:a.etag,complete:!1});const r=0==a.start?d:0,s=await G({dir:i,start:a.start+r,skip:r,total:a.total,body:h.body,onProgress:n,onChunk:()=>{clearTimeout(y),y=setTimeout(w,3e4)}});if(!a.total){await N(i,{total:s,etag:a.etag,complete:!0});const t=await H(e,o);if(t)return t}}catch(e){if(a?.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),a?.removeEventListener("abort",w)}}})(s,a,n,o).finally(()=>{W.delete(r)});return W.set(r,l),await l},remove:async e=>{const a=await t();a&&await(async(e,t)=>{await e.removeEntry(t,{recursive:!0}).catch(()=>{}),await e.removeEntry(`${t}.json`).catch(()=>{})})(a,B(e))},purge:async()=>{const t=await A();t&&await t.removeEntry(e,{recursive:!0}).catch(()=>{})}}},K="litertlm-models",X=J("litert-store"),Y={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},Z=async()=>{try{if("undefined"==typeof caches)return;return await caches.open(K)}catch{return}},ee=new Map,te=(e,t,a)=>{let n=0;return e.pipeThrough(new TransformStream({transform:(e,o)=>{n+=e.byteLength,a(t?n/t:0,c.download),o.enqueue(e)}}))},ae=async e=>{const t=[];for(const a of e){if("string"==typeof a.content){t.push({role:a.role,content:a.content});continue}const e=[];for(const t of a.content)e.push("text"==t.type?{type:"text",text:t.text}:{type:"image",data:await h(t.url)});t.push({role:"user",content:e})}return t},ne=async({engine:e,model:t,mod:a,params:n})=>await R({model:t,params:n,stopped:()=>!!n.signal?.aborted,start:async o=>{if(n.signal?.aborted)throw new Error("Cancelled");if(E(o)&&!t.litert?.vision)throw new Error("Model does not accept images");return async function*({engine:e,mod:t,params:a,messages:n}){const o=n[n.length-1],r=k(a.limits,a.reasoning);if("user"!=o?.role)throw new Error("No user turn");const i=await ae(n.slice(0,-1)),s=(await ae([o]))[0];if(!s)throw new Error("Empty prompt");const c=await e.createConversation({sessionConfig:{maxOutputTokens:r.max_tokens,samplerParams:{type:t.SamplerType.TOP_K,temperature:r.temperature,k:40,p:.95}},preface:{...i.length?{messages:i}:{},extra_context:{enable_thinking:a.reasoning}}});let l;const d=()=>{l&&l.cancel().catch(()=>{});try{c.cancel()}catch{}};a.signal?.addEventListener("abort",d,{once:!0});try{if(a.signal?.aborted)return;const e=c.sendMessageStreaming(s).getReader();for(l=e;;){const{done:t,value:a}=await e.read();if(t)break;const n={};let o="string"==typeof a.content?a.content:"";if(Array.isArray(a.content))for(const e of a.content)"text"==e.type&&(o+=e.text);o&&(n.content=o);const r=a.channels?.thought;r&&(n.reasoning=r),(n.content||n.reasoning)&&(yield{choices:[{delta:n}]})}}finally{a.signal?.removeEventListener("abort",d);try{await c.delete()}catch{}}}({engine:e,mod:a,params:n,messages:o})}}),oe={backend:s.gpu,startLLM:async({model:e,limits:t,onProgress:n,signal:o,logging:r})=>{const i=e.litert;if(!i)throw new Error("Model has no LiteRT artifact");const l=await a.e(520).then(()=>a(507));if(await l.getOrLoadGlobalLiteRtLm(w().script),o?.aborted)throw new Error("Cancelled");const d=_(i.repo,i.file),g=await Z();let u;const m=g?await g.match(d):void 0;if(m?.body)u=m.body;else{const e=await X.stored(d);e?.complete&&(u=e.file.stream())}if(!u){const e=await X.download({url:d,signal:o,onProgress:n}).catch(e=>{if(o?.aborted)throw new Error("Cancelled");if(!V(e))throw e;r&&console.log("Local model store failed",e)});e&&(u=e.file.stream())}if(!u&&g)try{await(async(e,t,a,n)=>{const o=ee.get(e);if(o)return await o;const r=(async()=>{const o=await fetch(e,{signal:a});if(!o.ok||!o.body)throw new Error("Model download failed");const r=M(o),i=te(o.body,r,n);await t.put(e,new Response(i,{headers:{"content-type":"application/octet-stream",...r?{"content-length":`${r}`}:{}}}))})().finally(()=>{ee.delete(e)});return ee.set(e,r),await r})(d,g,o,n);const e=await g.match(d);e?.body&&(u=e.body)}catch(e){if(o?.aborted)throw new Error("Cancelled");r&&console.log("Local model cache failed",e)}if(u||(u=await(async(e,t,a)=>{const n=await fetch(e,{signal:t});if(!n.ok||!n.body)throw new Error("Model download failed");return te(n.body,M(n),a)})(d,o,n)),o?.aborted)throw new Error("Cancelled");n(1,c.load);const f=await l.Engine.create({model:u,mainExecutorSettings:{maxNumTokens:4096,...i.vision?{maxNumImages:Math.max(1,t.maxImages)}:{},backendConfig:Y},...i.vision?{visionBackend:l.Backend.GPU}:{}});if(o?.aborted)throw await f.delete(),new Error("Cancelled");return{model:e,backend:s.gpu,contextSize:4096,chat:t=>ne({engine:f,model:e,mod:l,params:t}),unload:async()=>{await f.delete()}}},cached:async e=>{const t=e.litert;if(!t)return!1;const a=_(t.repo,t.file),[n,o]=await Promise.all([Z(),X.stored(a)]);if(o?.complete)return!0;try{return!(!n||!await n.match(a))}catch{return!1}},hasCache:async()=>{try{for(const e of d){const t=e.litert;if(!t)continue;if(await X.stored(_(t.repo,t.file)))return!0}const e=await Z();return!!e&&(await e.keys()).length>0}catch{return!1}},remove:async e=>{try{const t=e.litert;if(!t)return;const a=_(t.repo,t.file),n=await Z();await X.remove(a),n&&await n.delete(a)}catch{}},purge:async()=>{try{await X.purge(),"undefined"!=typeof caches&&await caches.delete(K)}catch{}}},re=J("wllama-store");let ie,se,ce="",le=!1;const de=e=>`${e.wasm}|${e.compatWasm}|${e.compatWorker}`,ge=e=>{const t=f();return{wasm:e?.wasm||t.wasm,compatWasm:e?.compatWasm||t.compatWasm,compatWorker:e?.compatWorker||t.compatWorker}},ue=e=>{const t=e=>(...t)=>{le&&("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)}},me=async()=>{if(ie)return ie;const e=await a.e(924).then(()=>a(577)),t=se||ge(),n=new e.Wllama({default:t.wasm},{logger:ue(e.LoggerWithoutDebug)});return se||(se=t,ce=de(t)),n.setCompat({wasm:t.compatWasm,worker:t.compatWorker}),ie=n,n},fe=async()=>{const e=ie;ie=void 0;try{await(e?.exit())}catch{}},we=(e,t)=>e.name.includes(t)||!!e.metadata?.originalURL?.includes(t),pe=e=>{const t=e.cpu;return t?[{name:t.file,url:_(t.repo,t.file)},...t.mmproj?[{name:t.mmproj.file,url:_(t.repo,t.mmproj.file)}]:[]]:[]},he=async e=>{const t=pe(e);if(!t.length)return!1;for(const e of t){const t=await re.stored(e.url);if(!t?.complete)return!1}return!0},ye=async(e,t)=>{const a=await e.cacheManager.list();return pe(t).every(e=>a.some(t=>we(t,e.name)))},be=async({model:e,signal:t,onProgress:a})=>{const n=pe(e),o=[];for(let e=0;e<n.length;e++){if(t?.aborted)throw new Error("Cancelled");const r=await re.download({url:n[e].url,signal:t,onProgress:(t,o)=>a((e+t)/n.length,o)});if(!r)return;o.push(r.file)}return o.length?o:void 0},ve=async e=>{const t=[];for(const a of e){if("string"==typeof a.content){"system"==a.role?t.push({role:"system",content:a.content}):"assistant"==a.role?t.push({role:"assistant",content:a.content}):t.push({role:"user",content:a.content});continue}const e=[];for(const t of a.content)e.push("text"==t.type?{type:"text",text:t.text}:{type:"image",data:await h(t.url)});t.push({role:"user",content:e})}return t},xe={backend:s.cpu,startLLM:async({model:e,limits:t,onProgress:a,signal:n,logging:o,assets:r})=>{le=!0===o;const i=e.cpu;if(!i)throw new Error("Model has no CPU artifact");const l=ge(r),d=de(l);ie&&d!=ce&&await fe(),se=l,ce=d;const g=(u="undefined"!=typeof navigator&&navigator.hardwareConcurrency||4,m=t.maxThreads,Math.max(1,Math.min(Math.max(2,Math.min(8,(u||4)-1)),m)));var u,m;const f=await me(),w=await he(e),p=i.mmproj?.file,h=_(i.repo,i.file),y=p?_(i.repo,p):"";if(n?.aborted)throw await fe(),new Error("Cancelled");if(!w&&p){const e=await f.cacheManager.list(),t=e.some(e=>we(e,i.file)),a=e.some(e=>we(e,p));t&&!a&&await f.cacheManager.deleteMany(e=>we(e,i.file))}const b={n_ctx:8192,n_threads:g,reasoning_budget_tokens:t.maxThinkingTokens,n_gpu_layers:0,jinja:!0};try{let t=w?await be({model:e,signal:n,onProgress:a}):void 0;t||w||await ye(f,e).catch(()=>!1)||(t=await be({model:e,signal:n,onProgress:a}).catch(e=>{if(n?.aborted)throw new Error("Cancelled");if(!V(e))throw e;o&&console.log("Local model store failed",e)})),t?(a(1,c.load),await f.loadModel(t,b)):await f.loadModelFromUrl(y?{url:h,mmprojUrl:y}:h,{...b,useCache:!0,signal:n,progressCallback:({loaded:e,total:t})=>{const n=t?e/t:0;a(n,n>=1?c.load:c.download)}})}catch(e){throw await fe(),e}if(n?.aborted)throw await fe(),new Error("Cancelled");if(!f.getLoadedContextInfo().n_ctx)throw await fe(),new Error("Failed to create model context");return{model:e,backend:s.cpu,contextSize:8192,chat:t=>(async(e,t,a)=>{if(le=!0===a.logging,e.isModelLoaded())return await R({model:t,params:a,stopped:()=>!!a.signal?.aborted,start:async n=>{if(E(n)&&!e.supportInputModality("image"))throw new Error("Model does not accept images");return await e.createChatCompletion({messages:await ve(n),...k(a.limits,a.reasoning),abortSignal:a.signal,...t.reasoning?{chat_template_kwargs:{enable_thinking:a.reasoning}}:{}})}})})(f,e,t),unload:async()=>{if(ie==f)await fe();else try{await f.exit()}catch{}}}},cached:async e=>{try{return!!e.cpu&&(!!await he(e)||await ye(await me(),e))}catch{return!1}},hasCache:async()=>{try{for(const e of d.flatMap(e=>pe(e)))if(await re.stored(e.url))return!0;const e=await me();return(await e.cacheManager.list()).length>0}catch{return!1}},remove:async e=>{const t=pe(e);try{for(const e of t)await re.remove(e.url)}catch{}try{const e=await me();await e.cacheManager.deleteMany(e=>t.some(t=>we(e,t.name)))}catch{}},purge:async()=>{try{await re.purge()}catch{}try{const e=await me();await e.cacheManager.clear()}catch{}}},ke=((e={})=>{const t=e.engines?.cpu||xe,a=e.engines?.litert||oe;let n,o,r,l,g,u,m,f=s.none,w=i.idle,p=[],h=0,y={percent:0,phase:c.download};const b=n=>{if(e.backend!=s.none)return e.backend==s.cpu?n.cpu?t:void 0:e.backend==s.gpu?n.litert?a:void 0:n.litert&&"undefined"!=typeof navigator&&navigator.gpu&&"undefined"!=typeof WebAssembly?a:!n.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[...p])e.report(y.percent,y.phase)},x=e=>{w=e},k=()=>{h++,u?.abort(),u=void 0,g=void 0,w==i.downloading&&x(i.idle)},_=(e,t)=>{const a=l;return a?new Promise(n=>{let o=!1;const r={report:e,settle:e=>{o||(o=!0,t?.removeEventListener("abort",i),n(e))}},i=()=>{(e=>{p=p.filter(t=>t!=e),queueMicrotask(()=>{!p.length&&l&&k()})})(r),r.settle(!1)};p.push(r),t?.aborted?i():t?.addEventListener("abort",i,{once:!0}),a.then(r.settle,()=>r.settle(!1))}):Promise.resolve(!1)},M=()=>w==i.ready&&!!n&&!!o;return{load:async({model:e,limits:t,assets:a,logging:s,signal:m,onProgress:w})=>{if(m?.aborted)return!1;if(M()&&o?.id==e.id)return!0;if(l&&g==e.id)return w(y.percent,y.phase),await _(w,m);l&&(k(),p=[],await l.catch(()=>!1));const E=++h,C=(async()=>{await(async()=>{try{if("undefined"==typeof navigator)return;await(navigator.storage?.persist?.())}catch{}})();const l=b(e);if(E!=h)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,a=n;n=void 0,o=void 0,r=void 0,await(a?.unload()),await(t?.remove(e))}if(await t.hasCache())for(const a of d)a.id!=e.id&&b(a)==t&&await t.remove(a)})(e,l),E!=h)return!1;const g=await l.cached(e);if(E!=h)return!1;x(i.downloading),v(0,c.download),g&&v(1,c.load);const m=new AbortController;u=m;try{const d=await l.startLLM({model:e,limits:t,logging:s,signal:m.signal,assets:a,onProgress:(e,t)=>{E==h&&v(e,t)}});return E!=h||m.signal.aborted?(await d.unload(),!1):(n=d,o=e,r=l,f=l.backend,x(i.ready),v(1,c.load),!0)}catch(e){return E!=h||(n=void 0,o=void 0,r=void 0,s&&console.log("Local model failed",e),x(i.error)),!1}finally{u==m&&(u=void 0)}})().finally(()=>{l==C&&(l=void 0,g=void 0,p=[])});return l=C,g=e.id,await _(w,m)},chat:async e=>{if(!M()||!n)return;for(;m;)await m.catch(()=>{});if(!M()||!n)return;const t=n.chat(e);m=t;try{return await t}finally{m==t&&(m=void 0)}},unload:async()=>{const e=n;k(),n=void 0,o=void 0,r=void 0,x(i.idle),await(e?.unload())},ready:M,loading:()=>!M()&&w==i.downloading,status:()=>w,backend:()=>f,model:()=>o}})(),_e=(e,t,a)=>{const n=e.status(),o=g(t);return{availability:n==i.ready?r.online:n==i.error||n==i.unsupported?r.offline:r.away,status:n,backend:e.backend(),supportsImages:!!o&&u(o),ready:e.ready(),loading:e.loading(),requesting:a.requesting,phase:a.phase,percent:a.percent,...a.error?{error:a.error}:{}}},Me=(e=>{let t,a=0;const n=t=>t.input.onStatus?.(_e(e,t.input.model,t));return r=>{t?.controller.abort();const s={token:++a,input:r,controller:new AbortController,percent:e.ready()?100:0,phase:e.ready()?c.load:c.download,requesting:!1,cancelled:!1};return t=s,n(s),(async t=>{const{input:r}=t,i=!0===r.logging,s=g(r.model),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)),m=l||d;if(!s)return t.error="Unknown model",void n(t);if(!m)return void n(t);if(d&&!l&&!u(s))return t.error="Model does not accept images",void n(t);const f=x(r.limits),w=await e.load({model:s,limits:f,logging:i,signal:t.controller.signal,onProgress:(e,o)=>{t.token==a&&(t.percent=Math.round(100*e),t.phase=o,n(t))}}).catch(e=>(t.token==a&&(t.error=`${e?.message||`${e}`}`),!1));if(t.token!=a)return;if(t.cancelled)return;if(!w)return t.error||(t.error="Model not ready"),void n(t);const p=u(s),h=d&&!p?r.messages.map(({images:e,...t})=>t):r.messages;if(d&&!p&&!l)return t.error="Model does not accept images",void n(t);t.requesting=!0,t.error=void 0,t.percent=100,t.phase=c.load,n(t);let y,b="",v="";try{y=await e.chat({messages:h,limits:f,reasoning:!0===r.reasoning,logging:i,onReason:e=>{t.token!=a||t.cancelled||(b+=e,r.onThinking?.(b))},onText:e=>{t.token!=a||t.cancelled||(v+=e,r.onResponse?.(v))},signal:t.controller.signal})}catch(e){if(t.token!=a)return;return t.requesting=!1,t.error=`${e?.message||`${e}`}`,void n(t)}t.token==a&&(t.requesting=!1,y||t.cancelled||(t.error="No reply"),n(t))})(s),()=>(t=>{t.token==a&&(t.cancelled=!0,t.controller.abort(),e.status()!=i.ready?queueMicrotask(()=>n(t)):e.unload().then(()=>n(t)))})(s)}})(ke),Ee=(Ce=ke,async e=>{const t=g(e.model),a=t=>e.onStatus?.(_e(Ce,e.model,t));if(!t)return a({requesting:!1,phase:c.download,percent:0,error:"Unknown model"}),!1;let n,o=0,r=c.download;const i=Ce.ready()&&Ce.model()?.id==t.id;return await Ce.load({model:t,limits:x(e.limits),logging:!0===e.logging,onProgress:(e,t)=>{o=Math.round(100*e),r=t,a({requesting:!1,phase:r,percent:o})}}).catch(e=>(n=`${e?.message||`${e}`}`,!1))?(i&&a({requesting:!1,phase:c.load,percent:100}),!0):(a({requesting:!1,phase:r,percent:o,error:n||"Model not ready"}),!1)});var Ce;module.exports=n})();
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:()=>je,preloadModel:()=>$e}),function(e){e.system="system",e.user="user",e.assistant="assistant"}(o||(o={})),function(e){e[e.online=0]="online",e[e.away=1]="away",e[e.offline=2]="offline"}(r||(r={})),function(e){e[e.idle=0]="idle",e[e.downloading=1]="downloading",e[e.ready=2]="ready",e[e.error=3]="error",e[e.unsupported=4]="unsupported"}(i||(i={})),function(e){e.cpu="cpu",e.gpu="gpu",e.none="none"}(s||(s={})),function(e){e.download="download",e.load="load"}(c||(c={})),function(e){e.qwen3_vl_2b="qwen3-vl-2b",e.gemma4_e2b="gemma4-e2b"}(l||(l={}));const d=[{id:l.qwen3_vl_2b,reasoning:!0,cpu:{repo:"Qwen/Qwen3-VL-2B-Thinking-GGUF",file:"Qwen3VL-2B-Thinking-Q4_K_M.gguf",mmproj:{file:"mmproj-Qwen3VL-2B-Thinking-Q8_0.gguf"}}},{id:l.gemma4_e2b,reasoning:!0,litert:{repo:"litert-community/gemma-4-E2B-it-litert-lm",file:"gemma-4-E2B-it.litertlm",vision:!0}}],g=e=>e?d.find(t=>t.id==e):void 0,u=async()=>(await(async()=>{const e="undefined"==typeof navigator?void 0:navigator.gpu;if(!e)return!1;try{return!!await e.requestAdapter({powerPreference:"high-performance"})}catch{return!1}})()?d.find(e=>!!e.litert):d.find(e=>!!e.cpu))?.id,m=async e=>g(e||await u()),f=e=>!!e.cpu?.mmproj||!!e.litert?.vision,p=require("node:path"),h=()=>{return e=(0,p.join)(__dirname,"../assets/wllama"),{wasm:(0,p.join)(e,"wllama.wasm"),compatWasm:(0,p.join)(e,"compat/wllama.wasm"),compatWorker:(0,p.join)(e,"compat/wllama.js")};var e},w=()=>{return e=(0,p.join)(__dirname,"../assets/litert"),{script:(0,p.join)(e,"litertlm_wasm_internal.js"),wasm:(0,p.join)(e,"litertlm_wasm_internal.wasm")};var e},y=288,b=async e=>{if(!e.startsWith("data:"))return await(await fetch(e)).arrayBuffer();const t=e.indexOf(","),n=t>=0?e.slice(0,t):"";if(t<0)throw new Error("Invalid image data");if(!n.includes(";base64"))return await(await fetch(e)).arrayBuffer();const a=atob(e.slice(t+1)),o=new Uint8Array(a.length);for(let e=0;e<a.length;e++)o[e]=a.charCodeAt(e);return o.buffer},v=async e=>{if(!e||"function"!=typeof fetch||"function"!=typeof createImageBitmap)return e;try{const t=await fetch(e),n=await createImageBitmap(await t.blob()),a=Math.min(n.width,n.height),o=Math.floor((n.width-a)/2),r=Math.floor((n.height-a)/2),i=((e,t)=>{if("undefined"!=typeof OffscreenCanvas)return new OffscreenCanvas(e,t);if("undefined"==typeof document)return;const n=document.createElement("canvas");return n.width=e,n.height=t,n})(y,y),s=i?i.getContext("2d"):null;if(!i||!s||a<1)return n.close(),e;if(s.drawImage(n,o,r,a,a,0,0,y,y),n.close(),(e=>"undefined"!=typeof OffscreenCanvas&&e instanceof OffscreenCanvas)(i)){const t=await i.convertToBlob({type:"image/jpeg",quality:.85});return await(e=>new Promise(t=>{if("undefined"==typeof FileReader)return void t("");const n=new FileReader;n.onerror=()=>t(""),n.onload=()=>t(`${n.result}`),n.readAsDataURL(e)}))(t)||e}return i.toDataURL("image/jpeg",.85)}catch{return e}},x={maxReplyTokens:250,maxThinkingTokens:128,maxReasoningTokens:320,maxImages:2,maxContentLength:1e3,promptCharBudget:1e4,maxThreads:6},k=(e,t,n)=>"number"==typeof e&&Number.isFinite(e)&&e>=n?Math.floor(e):t,_=(e={},t=!1)=>{const n=x;return{maxReplyTokens:k(e.maxReplyTokens,n.maxReplyTokens,1),maxThinkingTokens:k(e.maxThinkingTokens,n.maxThinkingTokens,1),maxReasoningTokens:k(e.maxReasoningTokens,t?1200:n.maxReasoningTokens,1),maxImages:k(e.maxImages,n.maxImages,0),maxContentLength:k(e.maxContentLength,n.maxContentLength,1),promptCharBudget:k(e.promptCharBudget,n.promptCharBudget,1),maxThreads:k(e.maxThreads,n.maxThreads,1)}},M=(e,t)=>({stream:!0,temperature:.7,max_tokens:t?e.maxReasoningTokens:e.maxReplyTokens}),T=(e,t)=>`https://huggingface.co/${e}/resolve/main/${t}`,E=e=>Number(e.headers.get("content-length"))||0,C=e=>e.some(e=>Array.isArray(e.content)&&e.content.some(e=>"image"==e.type)),L=["<|channel>thought\n","<|channel>thought","..."],P=["<channel|>","..."],j=(e,t)=>t.length?[...e?[{type:"text",text:e}]:[],...t.map(e=>({type:"image",url:e}))]:e,$=e=>"string"==typeof e?e?[{type:"text",text:e}]:[]:e,R=(e,t)=>{if("string"==typeof e&&"string"==typeof t)return`${e}\n${t}`;const n=[];for(const a of[...$(e),{type:"text",text:"\n"},...$(t)]){const e=n[n.length-1];"text"==a.type&&"text"==e?.type?e.text+=a.text:n.push(a)}return n};class S extends Error{}const q=async(e,t,n)=>{let a;try{return await Promise.race([e,new Promise((e,o)=>{a=setTimeout(()=>o(new S(n)),t)})])}finally{a&&clearTimeout(a)}},W=e=>{try{Promise.resolve(e.return?.()).catch(()=>{})}catch{}},B=async({model:e,logging:t,onReason:n,onText:a,start:o,stopped:r,firstTimeoutMs:i=18e4,idleTimeoutMs:s=3e4,onStall:c})=>{let l="",d="",g="",u=!1;const m=((e,t)=>{let n="",a=!1;const o=(e,t)=>{let n=-1,a="";for(const o of t){const t=e.indexOf(o);t>=0&&(n<0||t<n)&&(n=t,a=o)}return{at:n,tag:a}},r=(e,t)=>{let n="";for(const a of t)for(let t=Math.min(e.length,a.length-1);t>n.length;t--)if(e.endsWith(a.slice(0,t))){n=e.slice(e.length-t);break}return n};return{feed:i=>{let s=n+i;for(n="";s;){if(a){const{at:t,tag:i}=o(s,P);if(t>=0){t&&e(s.slice(0,t)),s=s.slice(t+i.length),a=!1;continue}const c=r(s,P);e(s.slice(0,s.length-c.length)),n=c;break}{const{at:e,tag:i}=o(s,L);if(e>=0){e&&t(s.slice(0,e)),s=s.slice(e+i.length),s.startsWith("\n")&&(s=s.slice(1)),a=!0;continue}const c=r(s,L);t(s.slice(0,s.length-c.length)),n=c;break}}},flush:()=>{n&&(a?e(n):t(n),n="")}}})(e=>{d+=e,n(e)},e=>{l+=e,a(e)});try{const e=(await q(o(),i,"Model did not start"))[Symbol.asyncIterator]();for(;;){if(r()){W(e);break}let a;try{a=await q(e.next(),u?s:i,u?"Model stopped responding":"Model did not reply")}catch(n){if(n instanceof S&&(l||d)){c?.(),W(e),t&&console.log("Local chat stalled",n);break}throw n}if(a.done)break;u=!0;const o=a.value?.choices?.[0],f=o?.delta,p="string"==typeof f?.reasoning_content?f.reasoning_content:"string"==typeof f?.reasoning?f.reasoning:"";o?.finish_reason&&(g=o.finish_reason),p&&(d+=p,n(p)),f?.content&&m.feed(f.content)}m.flush()}catch(e){if(t&&console.log("Local chat failed",e),e instanceof S&&!r())throw e}const f=r()?void 0:l||void 0;return t&&(console.log("Local chat reply",e.id,f?"****":""),console.log("Local chat finish",e.id,g||"none",l.length,d.length)),f},z=async({model:e,params:t,start:n,stopped:a,onStall:r})=>{const i=await(async e=>{const t=[];for(const n of e){if("string"==typeof n.content){t.push(n);continue}const e=[];for(const t of n.content)e.push("image"==t.type?{type:"image",url:await v(t.url)}:t);t.push({role:"user",content:e})}return t})(((e,t=x)=>{const n=t.maxContentLength,a=e.filter(e=>"string"==typeof e.content&&!!e.content.trim()||!(e.role!=o.user||!e.images?.length)),r=[],i=[];let s=0,c=t.maxImages;for(let e=a.length-1;e>=0;e--){const l=a[e],d=("string"==typeof l.content?l.content:"").slice(0,n),g=l.role==o.user?(l.images||[]).filter(e=>"string"==typeof e&&!!e).slice(0,Math.max(0,c)):[];if(d||g.length){if(d&&s+d.length>t.promptCharBudget)break;s+=d.length,c-=g.length,l.role==o.system?r.unshift(d):l.role==o.assistant?i.unshift({role:"assistant",content:d}):i.unshift({role:"user",content:j(d,g)})}}for(;i.length&&"user"!=i[0].role;)i.shift();const l=[];for(const e of i){const t=l[l.length-1];"assistant"==t?.role&&"assistant"==e.role?t.content=`${t.content}\n${e.content}`:"user"==t?.role&&"user"==e.role?t.content=R(t.content,e.content):"assistant"==e.role?l.push({role:"assistant",content:e.content}):l.push({role:"user",content:e.content})}const d=r.join("\n\n"),g=[];return d&&g.push({role:"system",content:d}),g.push(...l),g})(t.messages,t.limits));return t.logging&&((e,t)=>{console.log("Local chat call",e.id,t.map(({role:e,content:t})=>({role:e,content:"****",...Array.isArray(t)?{images:t.filter(e=>"image"==e.type).length}:{}})))})(e,i),await B({model:e,logging:t.logging,onReason:t.onReason,onText:t.onText,start:()=>n(i),stopped:a,onStall:r})},A=33554432,I=new Map;let O=!1;const N=e=>{const t=(e.split("/").pop()||"model").replace(/[^\w.-]+/g,"_");let n=0;for(let t=0;t<e.length;t++)n=31*n+e.charCodeAt(t)>>>0;return`${n.toString(16)}-${t}`},F=e=>{const t=/^part-(\d+)$/.exec(e);return t?Number(t[1]):-1},U=async()=>{if(!O)if("undefined"!=typeof navigator&&navigator.storage?.getDirectory)try{return await navigator.storage.getDirectory()}catch{return}else O=!0},D=async(e,t)=>{try{return await e.getDirectoryHandle(t)}catch{return}},H=async e=>{if(e)try{const t=await e.getFile(),n=JSON.parse(await t.text());return{total:Number(n.total)||0,etag:`${n.etag||""}`,complete:!0===n.complete}}catch{return}},Q=async(e,t)=>{try{const n=await e.getFileHandle("meta.json",{create:!0}),a=await n.createWritable();await a.write(JSON.stringify(t)),await a.close()}catch{}},G=async function*(e){for await(const t of e)yield t},V=async e=>{try{for await(const[t,n]of G(e))"file"==n.kind&&F(t)>=0&&await e.removeEntry(t).catch(()=>{})}catch{}},J=async(e,t)=>{try{const n=await(await e.getFileHandle(t)).getFile(),a=await H(await e.getFileHandle(`${t}.json`).catch(()=>{}));if(!a)return;return{file:n,size:n.size,total:a.total,etag:a.etag,complete:a.total?n.size>=a.total:!0===a.complete}}catch{return}},K=async(e,t)=>{const n=await D(e,t);if(n){const e=await(async e=>await H(await e.getFileHandle("meta.json").catch(()=>{})))(n),t=await(async e=>{const t=[];try{for await(const[n,a]of G(e)){const e=F(n);if("file"!=a.kind||e<0)continue;const o=await a.getFile();t.push({part:{start:e,size:o.size},file:o})}}catch{return}const n=(e=>{const t=[];let n=0;for(const a of[...e].sort((e,t)=>e.start-t.start))if(a.size){if(a.start!=n)break;t.push(a),n+=a.size}return t})(t.map(e=>e.part)),a=new Set(n.map(e=>e.start)),o=t.filter(e=>a.has(e.part.start)).sort((e,t)=>e.part.start-t.part.start).map(e=>e.file);if(o.length)return{file:1==o.length?o[0]:new Blob(o),size:n.reduce((e,t)=>e+t.size,0)}})(n),a=e?.total||0,o=t?.size??0,r=t?.file??new Blob;return{file:a&&o>a?r.slice(0,a):r,size:o,total:a,etag:e?.etag||"",complete:a?o>=a:!0===e?.complete}}return await J(e,t)},X=(e,t)=>{const n=e.headers.get("content-range"),a=/bytes (\d+)-/.exec(n||""),o=/\/(\d+)\s*$/.exec(n||""),r=e.headers.get("etag")||"";if(206==e.status){const e=a?Number(a[1]):t.size,n=o?Number(o[1]):t.total;if(e!=t.size)return;if(t.total&&n&&t.total!=n)return;if(t.etag&&r&&t.etag!=r)return;return{start:e,total:n||t.total,etag:r||t.etag}}if(!t.etag||!r||t.etag==r)return{start:0,total:E(e)||t.total,etag:r||t.etag}},Y=async({dir:e,start:t,skip:n,total:a,body:o,onProgress:r,onChunk:i})=>{let s,l=t,d=0,g=n;const u=async()=>{const t=await e.getFileHandle((e=>`part-${e}`)(l),{create:!0});s=await t.createWritable(),d=0},m=async()=>{const e=s;s=void 0,e&&await e.close().catch(()=>{})};try{const e=o.getReader();for(;;){const{done:n,value:o}=await e.read();if(n)break;i();let f=0;for(g&&(f=Math.min(g,o.byteLength),g-=f);f<o.byteLength;){s||await u();const e=Math.min(A-d,o.byteLength-f);await s.write({type:"write",position:d,data:o.subarray(f,f+e)}),f+=e,d+=e,l+=e,d==A&&await m()}l>t&&r(a?l/a:0,c.download)}await m()}catch(e){throw await m(),e}return l},Z=e=>["NotAllowedError","NotSupportedError"].includes(e?.name||""),ee=e=>{const t=async()=>{const t=await U();if(t)try{return await t.getDirectoryHandle(e,{create:!0})}catch{return}};return{stored:async e=>{const n=await t();return n?await K(n,N(e)):void 0},download:async({url:n,signal:a,onProgress:o})=>{const r=`${e}|${n}`,i=I.get(r);if(i)return await i;const s=await t();if(!s)return;const l=(async(e,t,n,a)=>{const o=N(t),r=await J(e,o);if(r?.complete)return r;let i=await D(e,o);i||(await e.removeEntry(o).catch(()=>{}),await e.removeEntry(`${o}.json`).catch(()=>{}),i=await e.getDirectoryHandle(o,{create:!0}));let s=0,l=0;for(;;){if(n?.aborted)throw new Error("Cancelled");const r=await K(e,o);if(r?.complete)return r;const d=r?.size??0,g=r?.total||0,u=r?.etag||"";if(d>s)s=d,l=0;else if(++l>4)throw new Error("Model download failed");d&&g&&a(d/g,c.download);const m=g?Math.min(d+A,g)-1:d+A-1,f=new AbortController,p=()=>f.abort(),h={size:d,total:g,etag:u};let w,y=setTimeout(p,3e4);n?.addEventListener("abort",p,{once:!0});try{if(w=await fetch(t,{signal:f.signal,headers:{Range:`bytes=${d}-${m}`}}),416==w.status){if(await(w.body?.cancel().catch(()=>{})),g&&d>=g){await Q(i,{total:g,etag:u,complete:!0});continue}await V(i),await Q(i,{total:0,etag:"",complete:!1});continue}if(!w.ok||!w.body)throw await(w.body?.cancel().catch(()=>{})),new Error("Model download failed");const n=X(w,h);if(!n){await w.body.cancel().catch(()=>{}),await V(i),await Q(i,{total:0,etag:"",complete:!1});continue}await Q(i,{total:n.total,etag:n.etag,complete:!1});const r=0==n.start?d:0,s=await Y({dir:i,start:n.start+r,skip:r,total:n.total,body:w.body,onProgress:a,onChunk:()=>{clearTimeout(y),y=setTimeout(p,3e4)}});if(!n.total){await Q(i,{total:s,etag:n.etag,complete:!0});const t=await K(e,o);if(t)return t}}catch(e){if(n?.aborted)throw new Error("Cancelled");if(e instanceof DOMException&&["QuotaExceededError","NotAllowedError","NotSupportedError","InvalidStateError","TypeMismatchError","SecurityError"].includes(e.name))throw e;await new Promise(e=>setTimeout(e,250+500*l))}finally{clearTimeout(y),n?.removeEventListener("abort",p)}}})(s,n,a,o).finally(()=>{I.delete(r)});return I.set(r,l),await l},remove:async e=>{const n=await t();n&&await(async(e,t)=>{await e.removeEntry(t,{recursive:!0}).catch(()=>{}),await e.removeEntry(`${t}.json`).catch(()=>{})})(n,N(e))},purge:async()=>{const t=await U();t&&await t.removeEntry(e,{recursive:!0}).catch(()=>{})}}},te="litertlm-models",ne=ee("litert-store"),ae={num_output_candidates:1,wait_for_weight_uploads:!0,num_decode_steps_per_sync:3,sequence_batch_size:0,supported_lora_ranks:[],max_top_k:40,enable_decode_logits:!1,enable_external_embeddings:!1,use_submodel:!1,use_autosized_ringbuffers:!0},oe=async()=>{try{if("undefined"==typeof caches)return;return await caches.open(te)}catch{return}},re=new Map,ie=(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)}}))},se=async(e,t=!0)=>{const n=[];for(const a of e){if("string"==typeof a.content){n.push({role:a.role,content:a.content});continue}const e=[];for(const n of a.content)e.push("text"==n.type?{type:"text",text:n.text}:t?{type:"image",data:await b(n.url)}:{type:"text",text:"[image]"});n.push({role:a.role,content:e})}return n},ce=(e,t)=>{if(e.role!=t.role)return!1;if("string"==typeof e.content||"string"==typeof t.content)return e.content==t.content;if(e.content.length!=t.content.length)return!1;for(let n=0;n<e.content.length;n++){const a=e.content[n],o=t.content[n];if(a.type!=o.type)return!1;if("text"==a.type&&"text"==o.type){if(a.text!=o.text)return!1}else if("image"==a.type&&"image"==o.type&&a.url!=o.url)return!1}return!0},le=async function*({engine:e,mod:t,params:n,messages:a,session:o}){const r=a[a.length-1],i=M(n.limits,n.reasoning);if("user"!=r?.role)throw new Error("No user turn");const s=a.slice(0,-1),c=(await se([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&&!ce(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(!ce(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 se(s,!1);o.conversation=void 0,o.sent=[],d=await e.createConversation({sessionConfig:{maxOutputTokens:i.max_tokens,samplerParams:{type:t.SamplerType.TOP_K,temperature:i.temperature,k:40,p:.95}},preface:{...a.length?{messages:a}:{},extra_context:{enable_thinking:n.reasoning}},prefillPrefaceOnInit:a.length>0}),o.conversation=d,o.reasoning=n.reasoning,o.maxTokens=i.max_tokens,o.sent=[...s]}o.sent.push(r);let g,u=!1,m="";const f=()=>{g&&g.cancel().catch(()=>{});try{d.cancel()}catch{}};n.signal?.addEventListener("abort",f,{once:!0});try{if(n.signal?.aborted)return;const e=d.sendMessageStreaming(c).getReader();for(g=e;;){const{done:t,value:n}=await e.read();if(t)break;const a={};let o="string"==typeof n.content?n.content:"";if(Array.isArray(n.content))for(const e of n.content)"text"==e.type&&(o+=e.text);o&&(a.content=o,m+=o);const r=n.channels?.thought;r&&(a.reasoning=r),(a.content||a.reasoning)&&(yield{choices:[{delta:a}]})}u=!0}finally{n.signal?.removeEventListener("abort",f),u&&m?o.sent.push({role:"assistant",content:m}):(o.conversation=void 0,o.sent=[])}},de={backend:s.gpu,startLLM:async({model:e,limits:t,onProgress:a,signal:o,logging:r})=>{const i=e.litert;if(!i)throw new Error("Model has no LiteRT artifact");const l=await n.e(520).then(()=>n(507));if(await l.getOrLoadGlobalLiteRtLm(w().script),o?.aborted)throw new Error("Cancelled");const d=T(i.repo,i.file),g=await oe();let u;const m=g?await g.match(d):void 0;if(m?.body)u=m.body;else{const e=await ne.stored(d);e?.complete&&(u=e.file.stream())}if(!u){const e=await ne.download({url:d,signal:o,onProgress:a}).catch(e=>{if(o?.aborted)throw new Error("Cancelled");if(!Z(e))throw e;r&&console.log("Local model store failed",e)});e&&(u=e.file.stream())}if(!u&&g)try{await(async(e,t,n,a)=>{const o=re.get(e);if(o)return await o;const r=(async()=>{const o=await fetch(e,{signal:n});if(!o.ok||!o.body)throw new Error("Model download failed");const r=E(o),i=ie(o.body,r,a);await t.put(e,new Response(i,{headers:{"content-type":"application/octet-stream",...r?{"content-length":`${r}`}:{}}}))})().finally(()=>{re.delete(e)});return re.set(e,r),await r})(d,g,o,a);const e=await g.match(d);e?.body&&(u=e.body)}catch(e){if(o?.aborted)throw new Error("Cancelled");r&&console.log("Local model cache failed",e)}if(u||(u=await(async(e,t,n)=>{const a=await fetch(e,{signal:t});if(!a.ok||!a.body)throw new Error("Model download failed");return ie(a.body,E(a),n)})(d,o,a)),o?.aborted)throw new Error("Cancelled");a(1,c.load);const f=await l.Engine.create({model:u,mainExecutorSettings:{maxNumTokens:4096,...i.vision?{maxNumImages:Math.max(1,t.maxImages)}:{},backendConfig:ae},...i.vision?{visionBackend:l.Backend.GPU}:{}});if(o?.aborted)throw await f.delete(),new Error("Cancelled");const p={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})=>{const r=()=>{try{o.conversation?.cancel()}catch{}o.conversation=void 0,o.sent=[]};try{return await z({model:t,params:a,stopped:()=>!!a.signal?.aborted,onStall:r,start:async r=>{if(a.signal?.aborted)throw new Error("Cancelled");if(C(r)&&!t.litert?.vision)throw new Error("Model does not accept images");return le({engine:e,mod:n,params:a,messages:r,session:o})}})}catch(e){throw r(),e}})({engine:f,model:e,mod:l,params:t,session:p}),unload:async()=>{p.conversation=void 0,p.sent=[],await f.delete()}}},cached:async e=>{const t=e.litert;if(!t)return!1;const n=T(t.repo,t.file),[a,o]=await Promise.all([oe(),ne.stored(n)]);if(o?.complete)return!0;try{return!(!a||!await a.match(n))}catch{return!1}},hasCache:async()=>{try{for(const e of d){const t=e.litert;if(!t)continue;if(await ne.stored(T(t.repo,t.file)))return!0}const e=await oe();return!!e&&(await e.keys()).length>0}catch{return!1}},remove:async e=>{try{const t=e.litert;if(!t)return;const n=T(t.repo,t.file),a=await oe();await ne.remove(n),a&&await a.delete(n)}catch{}},purge:async()=>{try{await ne.purge(),"undefined"!=typeof caches&&await caches.delete(te)}catch{}}},ge=ee("wllama-store");let ue,me,fe="",pe=!1;const he=e=>`${e.wasm}|${e.compatWasm}|${e.compatWorker}`,we=e=>{const t=h();return{wasm:e?.wasm||t.wasm,compatWasm:e?.compatWasm||t.compatWasm,compatWorker:e?.compatWorker||t.compatWorker}},ye=e=>{const t=e=>(...t)=>{pe&&("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)}},be=async()=>{if(ue)return ue;const e=await n.e(924).then(()=>n(577)),t=me||we(),a=new e.Wllama({default:t.wasm},{logger:ye(e.LoggerWithoutDebug)});return me||(me=t,fe=he(t)),a.setCompat({wasm:t.compatWasm,worker:t.compatWorker}),ue=a,a},ve=async()=>{const e=ue;ue=void 0;try{await(e?.exit())}catch{}},xe=(e,t)=>e.name.includes(t)||!!e.metadata?.originalURL?.includes(t),ke=e=>{const t=e.cpu;return t?[{name:t.file,url:T(t.repo,t.file)},...t.mmproj?[{name:t.mmproj.file,url:T(t.repo,t.mmproj.file)}]:[]]:[]},_e=async e=>{const t=ke(e);if(!t.length)return!1;for(const e of t){const t=await ge.stored(e.url);if(!t?.complete)return!1}return!0},Me=async(e,t)=>{const n=await e.cacheManager.list();return ke(t).every(e=>n.some(t=>xe(t,e.name)))},Te=async({model:e,signal:t,onProgress:n})=>{const a=ke(e),o=[];for(let e=0;e<a.length;e++){if(t?.aborted)throw new Error("Cancelled");const r=await ge.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},Ee=async e=>{const t=[];for(const n of e){if("string"==typeof n.content){"system"==n.role?t.push({role:"system",content:n.content}):"assistant"==n.role?t.push({role:"assistant",content:n.content}):t.push({role:"user",content:n.content});continue}const e=[];for(const t of n.content)e.push("text"==t.type?{type:"text",text:t.text}:{type:"image",data:await b(t.url)});t.push({role:"user",content:e})}return t},Ce={backend:s.cpu,startLLM:async({model:e,limits:t,onProgress:n,signal:a,logging:o,assets:r})=>{pe=!0===o;const i=e.cpu;if(!i)throw new Error("Model has no CPU artifact");const l=we(r),d=he(l);ue&&d!=fe&&await ve(),me=l,fe=d;const g=(u="undefined"!=typeof navigator&&navigator.hardwareConcurrency||4,m=t.maxThreads,Math.max(1,Math.min(Math.max(2,Math.min(8,(u||4)-1)),m)));var u,m;const f=await be(),p=await _e(e),h=i.mmproj?.file,w=T(i.repo,i.file),y=h?T(i.repo,h):"";if(a?.aborted)throw await ve(),new Error("Cancelled");if(!p&&h){const e=await f.cacheManager.list(),t=e.some(e=>xe(e,i.file)),n=e.some(e=>xe(e,h));t&&!n&&await f.cacheManager.deleteMany(e=>xe(e,i.file))}const b={n_ctx:8192,n_threads:g,reasoning_budget_tokens:t.maxThinkingTokens,n_gpu_layers:0,jinja:!0};try{let t=p?await Te({model:e,signal:a,onProgress:n}):void 0;t||p||await Me(f,e).catch(()=>!1)||(t=await Te({model:e,signal:a,onProgress:n}).catch(e=>{if(a?.aborted)throw new Error("Cancelled");if(!Z(e))throw e;o&&console.log("Local model store failed",e)})),t?(n(1,c.load),await f.loadModel(t,b)):await f.loadModelFromUrl(y?{url:w,mmprojUrl:y}:w,{...b,useCache:!0,signal:a,progressCallback:({loaded:e,total:t})=>{const a=t?e/t:0;n(a,a>=1?c.load:c.download)}})}catch(e){throw await ve(),e}if(a?.aborted)throw await ve(),new Error("Cancelled");if(!f.getLoadedContextInfo().n_ctx)throw await ve(),new Error("Failed to create model context");return{model:e,backend:s.cpu,contextSize:8192,chat:t=>(async(e,t,n)=>{if(pe=!0===n.logging,e.isModelLoaded())return await z({model:t,params:n,stopped:()=>!!n.signal?.aborted,start:async a=>{if(C(a)&&!e.supportInputModality("image"))throw new Error("Model does not accept images");return await e.createChatCompletion({messages:await Ee(a),...M(n.limits,n.reasoning),abortSignal:n.signal,...t.reasoning?{chat_template_kwargs:{enable_thinking:n.reasoning}}:{}})}})})(f,e,t),unload:async()=>{if(ue==f)await ve();else try{await f.exit()}catch{}}}},cached:async e=>{try{return!!e.cpu&&(!!await _e(e)||await Me(await be(),e))}catch{return!1}},hasCache:async()=>{try{for(const e of d.flatMap(e=>ke(e)))if(await ge.stored(e.url))return!0;const e=await be();return(await e.cacheManager.list()).length>0}catch{return!1}},remove:async e=>{const t=ke(e);try{for(const e of t)await ge.remove(e.url)}catch{}try{const e=await be();await e.cacheManager.deleteMany(e=>t.some(t=>xe(e,t.name)))}catch{}},purge:async()=>{try{await ge.purge()}catch{}try{const e=await be();await e.cacheManager.clear()}catch{}}},Le=((e={})=>{const t=e.engines?.cpu||Ce,n=e.engines?.litert||de;let a,o,r,l,g,u,m,f=s.none,p=i.idle,h=[],w=0,y={percent:0,phase:c.download};const b=a=>{if(e.backend!=s.none)return e.backend==s.cpu?a.cpu?t:void 0:e.backend==s.gpu?a.litert?n:void 0:a.litert&&"undefined"!=typeof navigator&&navigator.gpu&&"undefined"!=typeof WebAssembly?n:!a.cpu||"undefined"==typeof window&&"undefined"==typeof self&&"undefined"==typeof Worker||"undefined"==typeof WebAssembly?void 0:t},v=(e,t)=>{y={percent:Math.max(0,Math.min(1,e)),phase:t||c.download};for(const e of[...h])e.report(y.percent,y.phase)},x=e=>{p=e},k=()=>{w++,u?.abort(),u=void 0,g=void 0,p==i.downloading&&x(i.idle)},_=(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)},M=()=>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(M()&&o?.id==e.id)return!0;if(l&&g==e.id)return p(y.percent,y.phase),await _(p,m);l&&(k(),h=[],await l.catch(()=>!1));const T=++w,E=(async()=>{await(async()=>{try{if("undefined"==typeof navigator)return;await(navigator.storage?.persist?.())}catch{}})();const l=b(e);if(T!=w)return!1;if(!l)return x(i.unsupported),!1;if(await(async(e,t)=>{if(o&&o.id!=e.id){const e=o,t=r,n=a;a=void 0,o=void 0,r=void 0,await(n?.unload()),await(t?.remove(e))}if(await t.hasCache())for(const n of d)n.id!=e.id&&b(n)==t&&await t.remove(n)})(e,l),T!=w)return!1;const g=await l.cached(e);if(T!=w)return!1;x(i.downloading),v(0,c.download),g&&v(1,c.load);const m=new AbortController;u=m;try{const d=await l.startLLM({model:e,limits:t,logging:s,signal:m.signal,assets:n,onProgress:(e,t)=>{T==w&&v(e,t)}});return T!=w||m.signal.aborted?(await d.unload(),!1):(a=d,o=e,r=l,f=l.backend,x(i.ready),v(1,c.load),!0)}catch(e){return T!=w||(a=void 0,o=void 0,r=void 0,s&&console.log("Local model failed",e),x(i.error)),!1}finally{u==m&&(u=void 0)}})().finally(()=>{l==E&&(l=void 0,g=void 0,h=[])});return l=E,g=e.id,await _(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(i.idle),await(e?.unload())},ready:M,loading:()=>!M()&&p==i.downloading,status:()=>p,backend:()=>f,model:()=>o}})(),Pe=(e,t,n)=>{const a=e.status(),o=g(t);return{availability:a==i.ready?r.online:a==i.error||a==i.unsupported?r.offline:r.away,status:a,backend:e.backend(),supportsImages:!!o&&f(o),ready:e.ready(),loading:e.loading(),requesting:n.requesting,phase:n.phase,percent:n.percent,...n.error?{error:n.error}:{}}},je=(e=>{let t,n=0;const a=t=>t.input.onStatus?.(Pe(e,t.model??t.input.model,t));return r=>{t?.controller.abort();const s={token:++n,input:r,controller:new AbortController,percent:e.ready()?100:0,phase:e.ready()?c.load:c.download,requesting:!1,cancelled:!1};return t=s,a(s),(async t=>{const{input:r}=t,i=!0===r.logging,s=await m(r.model);if(t.model=s?.id,t.token!=n)return;const l=r.messages.some(e=>e.role!=o.system&&"string"==typeof e.content&&!!e.content.trim()),d=r.messages.some(e=>e.role==o.user&&!!e.images?.some(e=>"string"==typeof e&&!!e)),g=l||d;if(!s)return t.error="Unknown model",void a(t);if(!g)return void a(t);if(d&&!l&&!f(s))return t.error="Model does not accept images",void a(t);const u=_(r.limits,!!s.litert),p=await e.load({model:s,limits:u,logging:i,signal:t.controller.signal,onProgress:(e,o)=>{t.token==n&&(t.percent=Math.round(100*e),t.phase=o,a(t))}}).catch(e=>(t.token==n&&(t.error=`${e?.message||`${e}`}`),!1));if(t.token!=n)return;if(t.cancelled)return;if(!p)return t.error||(t.error="Model not ready"),void a(t);const h=f(s),w=d&&!h?r.messages.map(({images:e,...t})=>t):r.messages;if(d&&!h&&!l)return t.error="Model does not accept images",void a(t);t.requesting=!0,t.error=void 0,t.percent=100,t.phase=c.load,a(t);let y,b="",v="";try{y=await e.chat({messages:w,limits:u,reasoning:!0===r.reasoning,logging:i,onReason:e=>{t.token!=n||t.cancelled||(b+=e,r.onThinking?.(b))},onText:e=>{t.token!=n||t.cancelled||(v+=e,r.onResponse?.(v))},signal:t.controller.signal})}catch(e){if(t.token!=n)return;return t.requesting=!1,t.error=`${e?.message||`${e}`}`,void a(t)}t.token==n&&(t.requesting=!1,y||t.cancelled||(t.error="No reply"),a(t))})(s),()=>(t=>{t.token==n&&(t.cancelled=!0,t.controller.abort(),e.status()!=i.ready?queueMicrotask(()=>a(t)):e.unload().then(()=>a(t)))})(s)}})(Le),$e=(Re=Le,async(e={})=>{const t=await m(e.model),n=n=>e.onStatus?.(Pe(Re,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=Re.ready()&&Re.model()?.id==t.id;return await Re.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 Re;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 a}from"node:url";var n={};const o={};function r(e){const t=o[e];if(void 0!==t)return t.exports;const a=o[e]={exports:{}};return n[e](a,a.exports,r),a.exports}var s,i,c,l,d,g;r.m=n,r.d=(e,t)=>{for(var a in t)r.o(t,a)&&!r.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:t[a]})},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},a=e=>{let{__webpack_esm_ids__:a,__webpack_esm_modules__:n,__webpack_esm_runtime__:o}=e;var s,i,c=0;for(s in n)r.o(n,s)&&(r.m[s]=n[s]);for(o&&o(r);c<a.length;c++)i=a[c],r.o(t,i)&&t[i]&&t[i][0](),t[i]=0};r.f.j=(n,o)=>{let s=r.o(t,n)?t[n]:void 0;if(0!==s)if(s)o.push(s[1]);else{let r=e[n]().then(a,e=>{throw 0!==t[n]&&(t[n]=void 0),e});r=Promise.race([r,new Promise(e=>s=t[n]=[e])]),o.push(s[1]=r)}}})(),function(e){e.system="system",e.user="user",e.assistant="assistant"}(s||(s={})),function(e){e[e.online=0]="online",e[e.away=1]="away",e[e.offline=2]="offline"}(i||(i={})),function(e){e[e.idle=0]="idle",e[e.downloading=1]="downloading",e[e.ready=2]="ready",e[e.error=3]="error",e[e.unsupported=4]="unsupported"}(c||(c={})),function(e){e.cpu="cpu",e.gpu="gpu",e.none="none"}(l||(l={})),function(e){e.download="download",e.load="load"}(d||(d={})),function(e){e.qwen3_vl_2b="qwen3-vl-2b",e.gemma4_e2b="gemma4-e2b"}(g||(g={}));const u=[{id:g.qwen3_vl_2b,reasoning:!0,cpu:{repo:"Qwen/Qwen3-VL-2B-Thinking-GGUF",file:"Qwen3VL-2B-Thinking-Q4_K_M.gguf",mmproj:{file:"mmproj-Qwen3VL-2B-Thinking-Q8_0.gguf"}}},{id:g.gemma4_e2b,reasoning:!0,litert:{repo:"litert-community/gemma-4-E2B-it-litert-lm",file:"gemma-4-E2B-it.litertlm",vision:!0}}],m=e=>e?u.find(t=>t.id==e):void 0,f=e=>!!e.cpu?.mmproj||!!e.litert?.vision,p=()=>{return n=t(e(a("file:///media/x/store/code/DegreeSign/ds_chat/src/assets/nodeEsm.js")),"../assets/wllama"),{wasm:t(n,"wllama.wasm"),compatWasm:t(n,"compat/wllama.wasm"),compatWorker:t(n,"compat/wllama.js")};var n},w=()=>{return n=t(e(a("file:///media/x/store/code/DegreeSign/ds_chat/src/assets/nodeEsm.js")),"../assets/litert"),{script:t(n,"litertlm_wasm_internal.js"),wasm:t(n,"litertlm_wasm_internal.wasm")};var n},h=288,y=async e=>{if(!e.startsWith("data:"))return await(await fetch(e)).arrayBuffer();const t=e.indexOf(","),a=t>=0?e.slice(0,t):"";if(t<0)throw new Error("Invalid image data");if(!a.includes(";base64"))return await(await fetch(e)).arrayBuffer();const n=atob(e.slice(t+1)),o=new Uint8Array(n.length);for(let e=0;e<n.length;e++)o[e]=n.charCodeAt(e);return o.buffer},b=async e=>{if(!e||"function"!=typeof fetch||"function"!=typeof createImageBitmap)return e;try{const t=await fetch(e),a=await createImageBitmap(await t.blob()),n=Math.min(a.width,a.height),o=Math.floor((a.width-n)/2),r=Math.floor((a.height-n)/2),s=((e,t)=>{if("undefined"!=typeof OffscreenCanvas)return new OffscreenCanvas(e,t);if("undefined"==typeof document)return;const a=document.createElement("canvas");return a.width=e,a.height=t,a})(h,h),i=s?s.getContext("2d"):null;if(!s||!i||n<1)return a.close(),e;if(i.drawImage(a,o,r,n,n,0,0,h,h),a.close(),(e=>"undefined"!=typeof OffscreenCanvas&&e instanceof OffscreenCanvas)(s)){const t=await s.convertToBlob({type:"image/jpeg",quality:.85});return await(e=>new Promise(t=>{if("undefined"==typeof FileReader)return void t("");const a=new FileReader;a.onerror=()=>t(""),a.onload=()=>t(`${a.result}`),a.readAsDataURL(e)}))(t)||e}return s.toDataURL("image/jpeg",.85)}catch{return e}},v={maxReplyTokens:200,maxThinkingTokens:64,maxReasoningTokens:150,maxImages:2,maxContentLength:1e3,promptCharBudget:1e4,maxThreads:2},x=(e,t,a)=>"number"==typeof e&&Number.isFinite(e)&&e>=a?Math.floor(e):t,k=(e={})=>{const t=v;return{maxReplyTokens:x(e.maxReplyTokens,t.maxReplyTokens,1),maxThinkingTokens:x(e.maxThinkingTokens,t.maxThinkingTokens,1),maxReasoningTokens:x(e.maxReasoningTokens,t.maxReasoningTokens,1),maxImages:x(e.maxImages,t.maxImages,0),maxContentLength:x(e.maxContentLength,t.maxContentLength,1),promptCharBudget:x(e.promptCharBudget,t.promptCharBudget,1),maxThreads:x(e.maxThreads,t.maxThreads,1)}},_=(e,t)=>({stream:!0,temperature:.7,max_tokens:t?e.maxReasoningTokens:e.maxReplyTokens}),M=(e,t)=>`https://huggingface.co/${e}/resolve/main/${t}`,E=e=>Number(e.headers.get("content-length"))||0,T=e=>e.some(e=>Array.isArray(e.content)&&e.content.some(e=>"image"==e.type)),C=["<|channel>thought\n","<|channel>thought","..."],L=["<channel|>","..."],j=(e,t)=>t.length?[...e?[{type:"text",text:e}]:[],...t.map(e=>({type:"image",url:e}))]:e,P=e=>"string"==typeof e?e?[{type:"text",text:e}]:[]:e,$=(e,t)=>{if("string"==typeof e&&"string"==typeof t)return`${e}\n${t}`;const a=[];for(const n of[...P(e),{type:"text",text:"\n"},...P(t)]){const e=a[a.length-1];"text"==n.type&&"text"==e?.type?e.text+=n.text:a.push(n)}return a},R=async({model:e,logging:t,onReason:a,onText:n,start:o,stopped:r})=>{let s="",i="",c="";const l=((e,t)=>{let a="",n=!1;const o=(e,t)=>{let a=-1,n="";for(const o of t){const t=e.indexOf(o);t>=0&&(a<0||t<a)&&(a=t,n=o)}return{at:a,tag:n}},r=(e,t)=>{let a="";for(const n of t)for(let t=Math.min(e.length,n.length-1);t>a.length;t--)if(e.endsWith(n.slice(0,t))){a=e.slice(e.length-t);break}return a};return{feed:s=>{let i=a+s;for(a="";i;){if(n){const{at:t,tag:s}=o(i,L);if(t>=0){t&&e(i.slice(0,t)),i=i.slice(t+s.length),n=!1;continue}const c=r(i,L);e(i.slice(0,i.length-c.length)),a=c;break}{const{at:e,tag:s}=o(i,C);if(e>=0){e&&t(i.slice(0,e)),i=i.slice(e+s.length),i.startsWith("\n")&&(i=i.slice(1)),n=!0;continue}const c=r(i,C);t(i.slice(0,i.length-c.length)),a=c;break}}},flush:()=>{a&&(n?e(a):t(a),a="")}}})(e=>{i+=e,a(e)},e=>{s+=e,n(e)});try{const e=await o();for await(const t of e){if(r())break;const e=t?.choices?.[0],n=e?.delta,o="string"==typeof n?.reasoning_content?n.reasoning_content:"string"==typeof n?.reasoning?n.reasoning:"";e?.finish_reason&&(c=e.finish_reason),o&&(i+=o,a(o)),n?.content&&l.feed(n.content)}l.flush()}catch(e){t&&console.log("Local chat failed",e)}const d=r()?void 0:s||void 0;return t&&(console.log("Local chat reply",e.id,d?"****":""),console.log("Local chat finish",e.id,c||"none",s.length,i.length)),d},W=async({model:e,params:t,start:a,stopped:n})=>{const o=await(async e=>{const t=[];for(const a of e){if("string"==typeof a.content){t.push(a);continue}const e=[];for(const t of a.content)e.push("image"==t.type?{type:"image",url:await b(t.url)}:t);t.push({role:"user",content:e})}return t})(((e,t=v)=>{const a=t.maxContentLength,n=e.filter(e=>"string"==typeof e.content&&!!e.content.trim()||!(e.role!=s.user||!e.images?.length)),o=[],r=[];let i=0,c=t.maxImages;for(let e=n.length-1;e>=0;e--){const l=n[e],d=("string"==typeof l.content?l.content:"").slice(0,a),g=l.role==s.user?(l.images||[]).filter(e=>"string"==typeof e&&!!e).slice(0,Math.max(0,c)):[];if(d||g.length){if(d&&i+d.length>t.promptCharBudget)break;i+=d.length,c-=g.length,l.role==s.system?o.unshift(d):l.role==s.assistant?r.unshift({role:"assistant",content:d}):r.unshift({role:"user",content:j(d,g)})}}for(;r.length&&"user"!=r[0].role;)r.shift();const l=[];for(const e of r){const t=l[l.length-1];"assistant"==t?.role&&"assistant"==e.role?t.content=`${t.content}\n${e.content}`:"user"==t?.role&&"user"==e.role?t.content=$(t.content,e.content):"assistant"==e.role?l.push({role:"assistant",content:e.content}):l.push({role:"user",content:e.content})}const d=o.join("\n\n"),g=[];return d&&g.push({role:"system",content:d}),g.push(...l),g})(t.messages,t.limits));return t.logging&&((e,t)=>{console.log("Local chat call",e.id,t.map(({role:e,content:t})=>({role:e,content:"****",...Array.isArray(t)?{images:t.filter(e=>"image"==e.type).length}:{}})))})(e,o),await R({model:e,logging:t.logging,onReason:t.onReason,onText:t.onText,start:()=>a(o),stopped:n})},S=33554432,z=new Map;let B=!1;const q=e=>{const t=(e.split("/").pop()||"model").replace(/[^\w.-]+/g,"_");let a=0;for(let t=0;t<e.length;t++)a=31*a+e.charCodeAt(t)>>>0;return`${a.toString(16)}-${t}`},A=e=>{const t=/^part-(\d+)$/.exec(e);return t?Number(t[1]):-1},O=async()=>{if(!B)if("undefined"!=typeof navigator&&navigator.storage?.getDirectory)try{return await navigator.storage.getDirectory()}catch{return}else B=!0},N=async(e,t)=>{try{return await e.getDirectoryHandle(t)}catch{return}},F=async e=>{if(e)try{const t=await e.getFile(),a=JSON.parse(await t.text());return{total:Number(a.total)||0,etag:`${a.etag||""}`,complete:!0===a.complete}}catch{return}},I=async(e,t)=>{try{const a=await e.getFileHandle("meta.json",{create:!0}),n=await a.createWritable();await n.write(JSON.stringify(t)),await n.close()}catch{}},U=async function*(e){for await(const t of e)yield t},D=async e=>{try{for await(const[t,a]of U(e))"file"==a.kind&&A(t)>=0&&await e.removeEntry(t).catch(()=>{})}catch{}},H=async(e,t)=>{try{const a=await(await e.getFileHandle(t)).getFile(),n=await F(await e.getFileHandle(`${t}.json`).catch(()=>{}));if(!n)return;return{file:a,size:a.size,total:n.total,etag:n.etag,complete:n.total?a.size>=n.total:!0===n.complete}}catch{return}},Q=async(e,t)=>{const a=await N(e,t);if(a){const e=await(async e=>await F(await e.getFileHandle("meta.json").catch(()=>{})))(a),t=await(async e=>{const t=[];try{for await(const[a,n]of U(e)){const e=A(a);if("file"!=n.kind||e<0)continue;const o=await n.getFile();t.push({part:{start:e,size:o.size},file:o})}}catch{return}const a=(e=>{const t=[];let a=0;for(const n of[...e].sort((e,t)=>e.start-t.start))if(n.size){if(n.start!=a)break;t.push(n),a+=n.size}return t})(t.map(e=>e.part)),n=new Set(a.map(e=>e.start)),o=t.filter(e=>n.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:a.reduce((e,t)=>e+t.size,0)}})(a),n=e?.total||0,o=t?.size??0,r=t?.file??new Blob;return{file:n&&o>n?r.slice(0,n):r,size:o,total:n,etag:e?.etag||"",complete:n?o>=n:!0===e?.complete}}return await H(e,t)},G=(e,t)=>{const a=e.headers.get("content-range"),n=/bytes (\d+)-/.exec(a||""),o=/\/(\d+)\s*$/.exec(a||""),r=e.headers.get("etag")||"";if(206==e.status){const e=n?Number(n[1]):t.size,a=o?Number(o[1]):t.total;if(e!=t.size)return;if(t.total&&a&&t.total!=a)return;if(t.etag&&r&&t.etag!=r)return;return{start:e,total:a||t.total,etag:r||t.etag}}if(!t.etag||!r||t.etag==r)return{start:0,total:E(e)||t.total,etag:r||t.etag}},V=async({dir:e,start:t,skip:a,total:n,body:o,onProgress:r,onChunk:s})=>{let i,c=t,l=0,g=a;const u=async()=>{const t=await e.getFileHandle((e=>`part-${e}`)(c),{create:!0});i=await t.createWritable(),l=0},m=async()=>{const e=i;i=void 0,e&&await e.close().catch(()=>{})};try{const e=o.getReader();for(;;){const{done:a,value:o}=await e.read();if(a)break;s();let f=0;for(g&&(f=Math.min(g,o.byteLength),g-=f);f<o.byteLength;){i||await u();const e=Math.min(S-l,o.byteLength-f);await i.write({type:"write",position:l,data:o.subarray(f,f+e)}),f+=e,l+=e,c+=e,l==S&&await m()}c>t&&r(n?c/n:0,d.download)}await m()}catch(e){throw await m(),e}return c},J=e=>["NotAllowedError","NotSupportedError"].includes(e?.name||""),K=e=>{const t=async()=>{const t=await O();if(t)try{return await t.getDirectoryHandle(e,{create:!0})}catch{return}};return{stored:async e=>{const a=await t();return a?await Q(a,q(e)):void 0},download:async({url:a,signal:n,onProgress:o})=>{const r=`${e}|${a}`,s=z.get(r);if(s)return await s;const i=await t();if(!i)return;const c=(async(e,t,a,n)=>{const o=q(t),r=await H(e,o);if(r?.complete)return r;let s=await N(e,o);s||(await e.removeEntry(o).catch(()=>{}),await e.removeEntry(`${o}.json`).catch(()=>{}),s=await e.getDirectoryHandle(o,{create:!0}));let i=0,c=0;for(;;){if(a?.aborted)throw new Error("Cancelled");const r=await Q(e,o);if(r?.complete)return r;const l=r?.size??0,g=r?.total||0,u=r?.etag||"";if(l>i)i=l,c=0;else if(++c>4)throw new Error("Model download failed");l&&g&&n(l/g,d.download);const m=g?Math.min(l+S,g)-1:l+S-1,f=new AbortController,p=()=>f.abort(),w={size:l,total:g,etag:u};let h,y=setTimeout(p,3e4);a?.addEventListener("abort",p,{once:!0});try{if(h=await fetch(t,{signal:f.signal,headers:{Range:`bytes=${l}-${m}`}}),416==h.status){if(await(h.body?.cancel().catch(()=>{})),g&&l>=g){await I(s,{total:g,etag:u,complete:!0});continue}await D(s),await I(s,{total:0,etag:"",complete:!1});continue}if(!h.ok||!h.body)throw await(h.body?.cancel().catch(()=>{})),new Error("Model download failed");const a=G(h,w);if(!a){await h.body.cancel().catch(()=>{}),await D(s),await I(s,{total:0,etag:"",complete:!1});continue}await I(s,{total:a.total,etag:a.etag,complete:!1});const r=0==a.start?l:0,i=await V({dir:s,start:a.start+r,skip:r,total:a.total,body:h.body,onProgress:n,onChunk:()=>{clearTimeout(y),y=setTimeout(p,3e4)}});if(!a.total){await I(s,{total:i,etag:a.etag,complete:!0});const t=await Q(e,o);if(t)return t}}catch(e){if(a?.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),a?.removeEventListener("abort",p)}}})(i,a,n,o).finally(()=>{z.delete(r)});return z.set(r,c),await c},remove:async e=>{const a=await t();a&&await(async(e,t)=>{await e.removeEntry(t,{recursive:!0}).catch(()=>{}),await e.removeEntry(`${t}.json`).catch(()=>{})})(a,q(e))},purge:async()=>{const t=await O();t&&await t.removeEntry(e,{recursive:!0}).catch(()=>{})}}},X="litertlm-models",Y=K("litert-store"),Z={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},ee=async()=>{try{if("undefined"==typeof caches)return;return await caches.open(X)}catch{return}},te=new Map,ae=(e,t,a)=>{let n=0;return e.pipeThrough(new TransformStream({transform:(e,o)=>{n+=e.byteLength,a(t?n/t:0,d.download),o.enqueue(e)}}))},ne=async e=>{const t=[];for(const a of e){if("string"==typeof a.content){t.push({role:a.role,content:a.content});continue}const e=[];for(const t of a.content)e.push("text"==t.type?{type:"text",text:t.text}:{type:"image",data:await y(t.url)});t.push({role:"user",content:e})}return t},oe=async({engine:e,model:t,mod:a,params:n})=>await W({model:t,params:n,stopped:()=>!!n.signal?.aborted,start:async o=>{if(n.signal?.aborted)throw new Error("Cancelled");if(T(o)&&!t.litert?.vision)throw new Error("Model does not accept images");return async function*({engine:e,mod:t,params:a,messages:n}){const o=n[n.length-1],r=_(a.limits,a.reasoning);if("user"!=o?.role)throw new Error("No user turn");const s=await ne(n.slice(0,-1)),i=(await ne([o]))[0];if(!i)throw new Error("Empty prompt");const c=await e.createConversation({sessionConfig:{maxOutputTokens:r.max_tokens,samplerParams:{type:t.SamplerType.TOP_K,temperature:r.temperature,k:40,p:.95}},preface:{...s.length?{messages:s}:{},extra_context:{enable_thinking:a.reasoning}}});let l;const d=()=>{l&&l.cancel().catch(()=>{});try{c.cancel()}catch{}};a.signal?.addEventListener("abort",d,{once:!0});try{if(a.signal?.aborted)return;const e=c.sendMessageStreaming(i).getReader();for(l=e;;){const{done:t,value:a}=await e.read();if(t)break;const n={};let o="string"==typeof a.content?a.content:"";if(Array.isArray(a.content))for(const e of a.content)"text"==e.type&&(o+=e.text);o&&(n.content=o);const r=a.channels?.thought;r&&(n.reasoning=r),(n.content||n.reasoning)&&(yield{choices:[{delta:n}]})}}finally{a.signal?.removeEventListener("abort",d);try{await c.delete()}catch{}}}({engine:e,mod:a,params:n,messages:o})}}),re={backend:l.gpu,startLLM:async({model:e,limits:t,onProgress:a,signal:n,logging:o})=>{const s=e.litert;if(!s)throw new Error("Model has no LiteRT artifact");const i=await r.e(520).then(()=>r(507));if(await i.getOrLoadGlobalLiteRtLm(w().script),n?.aborted)throw new Error("Cancelled");const c=M(s.repo,s.file),g=await ee();let u;const m=g?await g.match(c):void 0;if(m?.body)u=m.body;else{const e=await Y.stored(c);e?.complete&&(u=e.file.stream())}if(!u){const e=await Y.download({url:c,signal:n,onProgress:a}).catch(e=>{if(n?.aborted)throw new Error("Cancelled");if(!J(e))throw e;o&&console.log("Local model store failed",e)});e&&(u=e.file.stream())}if(!u&&g)try{await(async(e,t,a,n)=>{const o=te.get(e);if(o)return await o;const r=(async()=>{const o=await fetch(e,{signal:a});if(!o.ok||!o.body)throw new Error("Model download failed");const r=E(o),s=ae(o.body,r,n);await t.put(e,new Response(s,{headers:{"content-type":"application/octet-stream",...r?{"content-length":`${r}`}:{}}}))})().finally(()=>{te.delete(e)});return te.set(e,r),await r})(c,g,n,a);const e=await g.match(c);e?.body&&(u=e.body)}catch(e){if(n?.aborted)throw new Error("Cancelled");o&&console.log("Local model cache failed",e)}if(u||(u=await(async(e,t,a)=>{const n=await fetch(e,{signal:t});if(!n.ok||!n.body)throw new Error("Model download failed");return ae(n.body,E(n),a)})(c,n,a)),n?.aborted)throw new Error("Cancelled");a(1,d.load);const f=await i.Engine.create({model:u,mainExecutorSettings:{maxNumTokens:4096,...s.vision?{maxNumImages:Math.max(1,t.maxImages)}:{},backendConfig:Z},...s.vision?{visionBackend:i.Backend.GPU}:{}});if(n?.aborted)throw await f.delete(),new Error("Cancelled");return{model:e,backend:l.gpu,contextSize:4096,chat:t=>oe({engine:f,model:e,mod:i,params:t}),unload:async()=>{await f.delete()}}},cached:async e=>{const t=e.litert;if(!t)return!1;const a=M(t.repo,t.file),[n,o]=await Promise.all([ee(),Y.stored(a)]);if(o?.complete)return!0;try{return!(!n||!await n.match(a))}catch{return!1}},hasCache:async()=>{try{for(const e of u){const t=e.litert;if(!t)continue;if(await Y.stored(M(t.repo,t.file)))return!0}const e=await ee();return!!e&&(await e.keys()).length>0}catch{return!1}},remove:async e=>{try{const t=e.litert;if(!t)return;const a=M(t.repo,t.file),n=await ee();await Y.remove(a),n&&await n.delete(a)}catch{}},purge:async()=>{try{await Y.purge(),"undefined"!=typeof caches&&await caches.delete(X)}catch{}}},se=K("wllama-store");let ie,ce,le="",de=!1;const ge=e=>`${e.wasm}|${e.compatWasm}|${e.compatWorker}`,ue=e=>{const t=p();return{wasm:e?.wasm||t.wasm,compatWasm:e?.compatWasm||t.compatWasm,compatWorker:e?.compatWorker||t.compatWorker}},me=e=>{const t=e=>(...t)=>{de&&("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)}},fe=async()=>{if(ie)return ie;const e=await r.e(924).then(()=>r(577)),t=ce||ue(),a=new e.Wllama({default:t.wasm},{logger:me(e.LoggerWithoutDebug)});return ce||(ce=t,le=ge(t)),a.setCompat({wasm:t.compatWasm,worker:t.compatWorker}),ie=a,a},pe=async()=>{const e=ie;ie=void 0;try{await(e?.exit())}catch{}},we=(e,t)=>e.name.includes(t)||!!e.metadata?.originalURL?.includes(t),he=e=>{const t=e.cpu;return t?[{name:t.file,url:M(t.repo,t.file)},...t.mmproj?[{name:t.mmproj.file,url:M(t.repo,t.mmproj.file)}]:[]]:[]},ye=async e=>{const t=he(e);if(!t.length)return!1;for(const e of t){const t=await se.stored(e.url);if(!t?.complete)return!1}return!0},be=async(e,t)=>{const a=await e.cacheManager.list();return he(t).every(e=>a.some(t=>we(t,e.name)))},ve=async({model:e,signal:t,onProgress:a})=>{const n=he(e),o=[];for(let e=0;e<n.length;e++){if(t?.aborted)throw new Error("Cancelled");const r=await se.download({url:n[e].url,signal:t,onProgress:(t,o)=>a((e+t)/n.length,o)});if(!r)return;o.push(r.file)}return o.length?o:void 0},xe=async e=>{const t=[];for(const a of e){if("string"==typeof a.content){"system"==a.role?t.push({role:"system",content:a.content}):"assistant"==a.role?t.push({role:"assistant",content:a.content}):t.push({role:"user",content:a.content});continue}const e=[];for(const t of a.content)e.push("text"==t.type?{type:"text",text:t.text}:{type:"image",data:await y(t.url)});t.push({role:"user",content:e})}return t},ke={backend:l.cpu,startLLM:async({model:e,limits:t,onProgress:a,signal:n,logging:o,assets:r})=>{de=!0===o;const s=e.cpu;if(!s)throw new Error("Model has no CPU artifact");const i=ue(r),c=ge(i);ie&&c!=le&&await pe(),ce=i,le=c;const g=(u="undefined"!=typeof navigator&&navigator.hardwareConcurrency||4,m=t.maxThreads,Math.max(1,Math.min(Math.max(2,Math.min(8,(u||4)-1)),m)));var u,m;const f=await fe(),p=await ye(e),w=s.mmproj?.file,h=M(s.repo,s.file),y=w?M(s.repo,w):"";if(n?.aborted)throw await pe(),new Error("Cancelled");if(!p&&w){const e=await f.cacheManager.list(),t=e.some(e=>we(e,s.file)),a=e.some(e=>we(e,w));t&&!a&&await f.cacheManager.deleteMany(e=>we(e,s.file))}const b={n_ctx:8192,n_threads:g,reasoning_budget_tokens:t.maxThinkingTokens,n_gpu_layers:0,jinja:!0};try{let t=p?await ve({model:e,signal:n,onProgress:a}):void 0;t||p||await be(f,e).catch(()=>!1)||(t=await ve({model:e,signal:n,onProgress:a}).catch(e=>{if(n?.aborted)throw new Error("Cancelled");if(!J(e))throw e;o&&console.log("Local model store failed",e)})),t?(a(1,d.load),await f.loadModel(t,b)):await f.loadModelFromUrl(y?{url:h,mmprojUrl:y}:h,{...b,useCache:!0,signal:n,progressCallback:({loaded:e,total:t})=>{const n=t?e/t:0;a(n,n>=1?d.load:d.download)}})}catch(e){throw await pe(),e}if(n?.aborted)throw await pe(),new Error("Cancelled");if(!f.getLoadedContextInfo().n_ctx)throw await pe(),new Error("Failed to create model context");return{model:e,backend:l.cpu,contextSize:8192,chat:t=>(async(e,t,a)=>{if(de=!0===a.logging,e.isModelLoaded())return await W({model:t,params:a,stopped:()=>!!a.signal?.aborted,start:async n=>{if(T(n)&&!e.supportInputModality("image"))throw new Error("Model does not accept images");return await e.createChatCompletion({messages:await xe(n),..._(a.limits,a.reasoning),abortSignal:a.signal,...t.reasoning?{chat_template_kwargs:{enable_thinking:a.reasoning}}:{}})}})})(f,e,t),unload:async()=>{if(ie==f)await pe();else try{await f.exit()}catch{}}}},cached:async e=>{try{return!!e.cpu&&(!!await ye(e)||await be(await fe(),e))}catch{return!1}},hasCache:async()=>{try{for(const e of u.flatMap(e=>he(e)))if(await se.stored(e.url))return!0;const e=await fe();return(await e.cacheManager.list()).length>0}catch{return!1}},remove:async e=>{const t=he(e);try{for(const e of t)await se.remove(e.url)}catch{}try{const e=await fe();await e.cacheManager.deleteMany(e=>t.some(t=>we(e,t.name)))}catch{}},purge:async()=>{try{await se.purge()}catch{}try{const e=await fe();await e.cacheManager.clear()}catch{}}},_e=((e={})=>{const t=e.engines?.cpu||ke,a=e.engines?.litert||re;let n,o,r,s,i,g,m,f=l.none,p=c.idle,w=[],h=0,y={percent:0,phase:d.download};const b=n=>{if(e.backend!=l.none)return e.backend==l.cpu?n.cpu?t:void 0:e.backend==l.gpu?n.litert?a:void 0:n.litert&&"undefined"!=typeof navigator&&navigator.gpu&&"undefined"!=typeof WebAssembly?a:!n.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[...w])e.report(y.percent,y.phase)},x=e=>{p=e},k=()=>{h++,g?.abort(),g=void 0,i=void 0,p==c.downloading&&x(c.idle)},_=(e,t)=>{const a=s;return a?new Promise(n=>{let o=!1;const r={report:e,settle:e=>{o||(o=!0,t?.removeEventListener("abort",i),n(e))}},i=()=>{(e=>{w=w.filter(t=>t!=e),queueMicrotask(()=>{!w.length&&s&&k()})})(r),r.settle(!1)};w.push(r),t?.aborted?i():t?.addEventListener("abort",i,{once:!0}),a.then(r.settle,()=>r.settle(!1))}):Promise.resolve(!1)},M=()=>p==c.ready&&!!n&&!!o;return{load:async({model:e,limits:t,assets:a,logging:l,signal:m,onProgress:p})=>{if(m?.aborted)return!1;if(M()&&o?.id==e.id)return!0;if(s&&i==e.id)return p(y.percent,y.phase),await _(p,m);s&&(k(),w=[],await s.catch(()=>!1));const E=++h,T=(async()=>{await(async()=>{try{if("undefined"==typeof navigator)return;await(navigator.storage?.persist?.())}catch{}})();const s=b(e);if(E!=h)return!1;if(!s)return x(c.unsupported),!1;if(await(async(e,t)=>{if(o&&o.id!=e.id){const e=o,t=r,a=n;n=void 0,o=void 0,r=void 0,await(a?.unload()),await(t?.remove(e))}if(await t.hasCache())for(const a of u)a.id!=e.id&&b(a)==t&&await t.remove(a)})(e,s),E!=h)return!1;const i=await s.cached(e);if(E!=h)return!1;x(c.downloading),v(0,d.download),i&&v(1,d.load);const m=new AbortController;g=m;try{const i=await s.startLLM({model:e,limits:t,logging:l,signal:m.signal,assets:a,onProgress:(e,t)=>{E==h&&v(e,t)}});return E!=h||m.signal.aborted?(await i.unload(),!1):(n=i,o=e,r=s,f=s.backend,x(c.ready),v(1,d.load),!0)}catch(e){return E!=h||(n=void 0,o=void 0,r=void 0,l&&console.log("Local model failed",e),x(c.error)),!1}finally{g==m&&(g=void 0)}})().finally(()=>{s==T&&(s=void 0,i=void 0,w=[])});return s=T,i=e.id,await _(p,m)},chat:async e=>{if(!M()||!n)return;for(;m;)await m.catch(()=>{});if(!M()||!n)return;const t=n.chat(e);m=t;try{return await t}finally{m==t&&(m=void 0)}},unload:async()=>{const e=n;k(),n=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}})(),Me=(e,t,a)=>{const n=e.status(),o=m(t);return{availability:n==c.ready?i.online:n==c.error||n==c.unsupported?i.offline:i.away,status:n,backend:e.backend(),supportsImages:!!o&&f(o),ready:e.ready(),loading:e.loading(),requesting:a.requesting,phase:a.phase,percent:a.percent,...a.error?{error:a.error}:{}}},Ee=(e=>{let t,a=0;const n=t=>t.input.onStatus?.(Me(e,t.input.model,t));return o=>{t?.controller.abort();const r={token:++a,input:o,controller:new AbortController,percent:e.ready()?100:0,phase:e.ready()?d.load:d.download,requesting:!1,cancelled:!1};return t=r,n(r),(async t=>{const{input:o}=t,r=!0===o.logging,i=m(o.model),c=o.messages.some(e=>e.role!=s.system&&"string"==typeof e.content&&!!e.content.trim()),l=o.messages.some(e=>e.role==s.user&&!!e.images?.some(e=>"string"==typeof e&&!!e)),g=c||l;if(!i)return t.error="Unknown model",void n(t);if(!g)return void n(t);if(l&&!c&&!f(i))return t.error="Model does not accept images",void n(t);const u=k(o.limits),p=await e.load({model:i,limits:u,logging:r,signal:t.controller.signal,onProgress:(e,o)=>{t.token==a&&(t.percent=Math.round(100*e),t.phase=o,n(t))}}).catch(e=>(t.token==a&&(t.error=`${e?.message||`${e}`}`),!1));if(t.token!=a)return;if(t.cancelled)return;if(!p)return t.error||(t.error="Model not ready"),void n(t);const w=f(i),h=l&&!w?o.messages.map(({images:e,...t})=>t):o.messages;if(l&&!w&&!c)return t.error="Model does not accept images",void n(t);t.requesting=!0,t.error=void 0,t.percent=100,t.phase=d.load,n(t);let y,b="",v="";try{y=await e.chat({messages:h,limits:u,reasoning:!0===o.reasoning,logging:r,onReason:e=>{t.token!=a||t.cancelled||(b+=e,o.onThinking?.(b))},onText:e=>{t.token!=a||t.cancelled||(v+=e,o.onResponse?.(v))},signal:t.controller.signal})}catch(e){if(t.token!=a)return;return t.requesting=!1,t.error=`${e?.message||`${e}`}`,void n(t)}t.token==a&&(t.requesting=!1,y||t.cancelled||(t.error="No reply"),n(t))})(r),()=>(t=>{t.token==a&&(t.cancelled=!0,t.controller.abort(),e.status()!=c.ready?queueMicrotask(()=>n(t)):e.unload().then(()=>n(t)))})(r)}})(_e),Te=(Ce=_e,async e=>{const t=m(e.model),a=t=>e.onStatus?.(Me(Ce,e.model,t));if(!t)return a({requesting:!1,phase:d.download,percent:0,error:"Unknown model"}),!1;let n,o=0,r=d.download;const s=Ce.ready()&&Ce.model()?.id==t.id;return await Ce.load({model:t,limits:k(e.limits),logging:!0===e.logging,onProgress:(e,t)=>{o=Math.round(100*e),r=t,a({requesting:!1,phase:r,percent:o})}}).catch(e=>(n=`${e?.message||`${e}`}`,!1))?(s&&a({requesting:!1,phase:d.load,percent:100}),!0):(a({requesting:!1,phase:r,percent:o,error:n||"Model not ready"}),!1)});var Ce;export{g as ChatModelId,i as ChatStatus,l as LlmBackend,d as LlmPhase,s as LlmRole,c as LlmStatus,Ee as createChat,Te 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 s,i,c,l,d,g;r.m=a,r.d=(e,t)=>{for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.f={},r.e=e=>{const t=[];return r.f.j(e,t),Promise.all(t)},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r.r=e=>{Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{const e={520:()=>import("./engine-litert.node.min.mjs"),924:()=>import("./engine-wllama.node.min.mjs")},t={792:0},n=e=>{let{__webpack_esm_ids__:n,__webpack_esm_modules__:a,__webpack_esm_runtime__:o}=e;var s,i,c=0;for(s in a)r.o(a,s)&&(r.m[s]=a[s]);for(o&&o(r);c<n.length;c++)i=n[c],r.o(t,i)&&t[i]&&t[i][0](),t[i]=0};r.f.j=(a,o)=>{let s=r.o(t,a)?t[a]:void 0;if(0!==s)if(s)o.push(s[1]);else{let r=e[a]().then(n,e=>{throw 0!==t[a]&&(t[a]=void 0),e});r=Promise.race([r,new Promise(e=>s=t[a]=[e])]),o.push(s[1]=r)}}})(),function(e){e.system="system",e.user="user",e.assistant="assistant"}(s||(s={})),function(e){e[e.online=0]="online",e[e.away=1]="away",e[e.offline=2]="offline"}(i||(i={})),function(e){e[e.idle=0]="idle",e[e.downloading=1]="downloading",e[e.ready=2]="ready",e[e.error=3]="error",e[e.unsupported=4]="unsupported"}(c||(c={})),function(e){e.cpu="cpu",e.gpu="gpu",e.none="none"}(l||(l={})),function(e){e.download="download",e.load="load"}(d||(d={})),function(e){e.qwen3_vl_2b="qwen3-vl-2b",e.gemma4_e2b="gemma4-e2b"}(g||(g={}));const u=[{id:g.qwen3_vl_2b,reasoning:!0,cpu:{repo:"Qwen/Qwen3-VL-2B-Thinking-GGUF",file:"Qwen3VL-2B-Thinking-Q4_K_M.gguf",mmproj:{file:"mmproj-Qwen3VL-2B-Thinking-Q8_0.gguf"}}},{id:g.gemma4_e2b,reasoning:!0,litert:{repo:"litert-community/gemma-4-E2B-it-litert-lm",file:"gemma-4-E2B-it.litertlm",vision:!0}}],m=e=>e?u.find(t=>t.id==e):void 0,f=async()=>(await(async()=>{const e="undefined"==typeof navigator?void 0:navigator.gpu;if(!e)return!1;try{return!!await e.requestAdapter({powerPreference:"high-performance"})}catch{return!1}})()?u.find(e=>!!e.litert):u.find(e=>!!e.cpu))?.id,p=async e=>m(e||await f()),h=e=>!!e.cpu?.mmproj||!!e.litert?.vision,w=()=>{return a=t(e(n("file:///media/x/store/code/DegreeSign/ds_chat/src/assets/nodeEsm.js")),"../assets/wllama"),{wasm:t(a,"wllama.wasm"),compatWasm:t(a,"compat/wllama.wasm"),compatWorker:t(a,"compat/wllama.js")};var a},y=()=>{return a=t(e(n("file:///media/x/store/code/DegreeSign/ds_chat/src/assets/nodeEsm.js")),"../assets/litert"),{script:t(a,"litertlm_wasm_internal.js"),wasm:t(a,"litertlm_wasm_internal.wasm")};var a},b=288,v=async e=>{if(!e.startsWith("data:"))return await(await fetch(e)).arrayBuffer();const t=e.indexOf(","),n=t>=0?e.slice(0,t):"";if(t<0)throw new Error("Invalid image data");if(!n.includes(";base64"))return await(await fetch(e)).arrayBuffer();const a=atob(e.slice(t+1)),o=new Uint8Array(a.length);for(let e=0;e<a.length;e++)o[e]=a.charCodeAt(e);return o.buffer},x=async e=>{if(!e||"function"!=typeof fetch||"function"!=typeof createImageBitmap)return e;try{const t=await fetch(e),n=await createImageBitmap(await t.blob()),a=Math.min(n.width,n.height),o=Math.floor((n.width-a)/2),r=Math.floor((n.height-a)/2),s=((e,t)=>{if("undefined"!=typeof OffscreenCanvas)return new OffscreenCanvas(e,t);if("undefined"==typeof document)return;const n=document.createElement("canvas");return n.width=e,n.height=t,n})(b,b),i=s?s.getContext("2d"):null;if(!s||!i||a<1)return n.close(),e;if(i.drawImage(n,o,r,a,a,0,0,b,b),n.close(),(e=>"undefined"!=typeof OffscreenCanvas&&e instanceof OffscreenCanvas)(s)){const t=await s.convertToBlob({type:"image/jpeg",quality:.85});return await(e=>new Promise(t=>{if("undefined"==typeof FileReader)return void t("");const n=new FileReader;n.onerror=()=>t(""),n.onload=()=>t(`${n.result}`),n.readAsDataURL(e)}))(t)||e}return s.toDataURL("image/jpeg",.85)}catch{return e}},k={maxReplyTokens:250,maxThinkingTokens:128,maxReasoningTokens:320,maxImages:2,maxContentLength:1e3,promptCharBudget:1e4,maxThreads:6},_=(e,t,n)=>"number"==typeof e&&Number.isFinite(e)&&e>=n?Math.floor(e):t,M=(e={},t=!1)=>{const n=k;return{maxReplyTokens:_(e.maxReplyTokens,n.maxReplyTokens,1),maxThinkingTokens:_(e.maxThinkingTokens,n.maxThinkingTokens,1),maxReasoningTokens:_(e.maxReasoningTokens,t?1200:n.maxReasoningTokens,1),maxImages:_(e.maxImages,n.maxImages,0),maxContentLength:_(e.maxContentLength,n.maxContentLength,1),promptCharBudget:_(e.promptCharBudget,n.promptCharBudget,1),maxThreads:_(e.maxThreads,n.maxThreads,1)}},T=(e,t)=>({stream:!0,temperature:.7,max_tokens:t?e.maxReasoningTokens:e.maxReplyTokens}),E=(e,t)=>`https://huggingface.co/${e}/resolve/main/${t}`,C=e=>Number(e.headers.get("content-length"))||0,L=e=>e.some(e=>Array.isArray(e.content)&&e.content.some(e=>"image"==e.type)),P=["<|channel>thought\n","<|channel>thought","..."],j=["<channel|>","..."],$=(e,t)=>t.length?[...e?[{type:"text",text:e}]:[],...t.map(e=>({type:"image",url:e}))]:e,R=e=>"string"==typeof e?e?[{type:"text",text:e}]:[]:e,S=(e,t)=>{if("string"==typeof e&&"string"==typeof t)return`${e}\n${t}`;const n=[];for(const a of[...R(e),{type:"text",text:"\n"},...R(t)]){const e=n[n.length-1];"text"==a.type&&"text"==e?.type?e.text+=a.text:n.push(a)}return n};class W extends Error{}const q=async(e,t,n)=>{let a;try{return await Promise.race([e,new Promise((e,o)=>{a=setTimeout(()=>o(new W(n)),t)})])}finally{a&&clearTimeout(a)}},z=e=>{try{Promise.resolve(e.return?.()).catch(()=>{})}catch{}},B=async({model:e,logging:t,onReason:n,onText:a,start:o,stopped:r,firstTimeoutMs:s=18e4,idleTimeoutMs:i=3e4,onStall:c})=>{let l="",d="",g="",u=!1;const m=((e,t)=>{let n="",a=!1;const o=(e,t)=>{let n=-1,a="";for(const o of t){const t=e.indexOf(o);t>=0&&(n<0||t<n)&&(n=t,a=o)}return{at:n,tag:a}},r=(e,t)=>{let n="";for(const a of t)for(let t=Math.min(e.length,a.length-1);t>n.length;t--)if(e.endsWith(a.slice(0,t))){n=e.slice(e.length-t);break}return n};return{feed:s=>{let i=n+s;for(n="";i;){if(a){const{at:t,tag:s}=o(i,j);if(t>=0){t&&e(i.slice(0,t)),i=i.slice(t+s.length),a=!1;continue}const c=r(i,j);e(i.slice(0,i.length-c.length)),n=c;break}{const{at:e,tag:s}=o(i,P);if(e>=0){e&&t(i.slice(0,e)),i=i.slice(e+s.length),i.startsWith("\n")&&(i=i.slice(1)),a=!0;continue}const c=r(i,P);t(i.slice(0,i.length-c.length)),n=c;break}}},flush:()=>{n&&(a?e(n):t(n),n="")}}})(e=>{d+=e,n(e)},e=>{l+=e,a(e)});try{const e=(await q(o(),s,"Model did not start"))[Symbol.asyncIterator]();for(;;){if(r()){z(e);break}let a;try{a=await q(e.next(),u?i:s,u?"Model stopped responding":"Model did not reply")}catch(n){if(n instanceof W&&(l||d)){c?.(),z(e),t&&console.log("Local chat stalled",n);break}throw n}if(a.done)break;u=!0;const o=a.value?.choices?.[0],f=o?.delta,p="string"==typeof f?.reasoning_content?f.reasoning_content:"string"==typeof f?.reasoning?f.reasoning:"";o?.finish_reason&&(g=o.finish_reason),p&&(d+=p,n(p)),f?.content&&m.feed(f.content)}m.flush()}catch(e){if(t&&console.log("Local chat failed",e),e instanceof W&&!r())throw e}const f=r()?void 0:l||void 0;return t&&(console.log("Local chat reply",e.id,f?"****":""),console.log("Local chat finish",e.id,g||"none",l.length,d.length)),f},A=async({model:e,params:t,start:n,stopped:a,onStall:o})=>{const r=await(async e=>{const t=[];for(const n of e){if("string"==typeof n.content){t.push(n);continue}const e=[];for(const t of n.content)e.push("image"==t.type?{type:"image",url:await x(t.url)}:t);t.push({role:"user",content:e})}return t})(((e,t=k)=>{const n=t.maxContentLength,a=e.filter(e=>"string"==typeof e.content&&!!e.content.trim()||!(e.role!=s.user||!e.images?.length)),o=[],r=[];let i=0,c=t.maxImages;for(let e=a.length-1;e>=0;e--){const l=a[e],d=("string"==typeof l.content?l.content:"").slice(0,n),g=l.role==s.user?(l.images||[]).filter(e=>"string"==typeof e&&!!e).slice(0,Math.max(0,c)):[];if(d||g.length){if(d&&i+d.length>t.promptCharBudget)break;i+=d.length,c-=g.length,l.role==s.system?o.unshift(d):l.role==s.assistant?r.unshift({role:"assistant",content:d}):r.unshift({role:"user",content:$(d,g)})}}for(;r.length&&"user"!=r[0].role;)r.shift();const l=[];for(const e of r){const t=l[l.length-1];"assistant"==t?.role&&"assistant"==e.role?t.content=`${t.content}\n${e.content}`:"user"==t?.role&&"user"==e.role?t.content=S(t.content,e.content):"assistant"==e.role?l.push({role:"assistant",content:e.content}):l.push({role:"user",content:e.content})}const d=o.join("\n\n"),g=[];return d&&g.push({role:"system",content:d}),g.push(...l),g})(t.messages,t.limits));return t.logging&&((e,t)=>{console.log("Local chat call",e.id,t.map(({role:e,content:t})=>({role:e,content:"****",...Array.isArray(t)?{images:t.filter(e=>"image"==e.type).length}:{}})))})(e,r),await B({model:e,logging:t.logging,onReason:t.onReason,onText:t.onText,start:()=>n(r),stopped:a,onStall:o})},O=33554432,I=new Map;let N=!1;const F=e=>{const t=(e.split("/").pop()||"model").replace(/[^\w.-]+/g,"_");let n=0;for(let t=0;t<e.length;t++)n=31*n+e.charCodeAt(t)>>>0;return`${n.toString(16)}-${t}`},U=e=>{const t=/^part-(\d+)$/.exec(e);return t?Number(t[1]):-1},D=async()=>{if(!N)if("undefined"!=typeof navigator&&navigator.storage?.getDirectory)try{return await navigator.storage.getDirectory()}catch{return}else N=!0},H=async(e,t)=>{try{return await e.getDirectoryHandle(t)}catch{return}},Q=async e=>{if(e)try{const t=await e.getFile(),n=JSON.parse(await t.text());return{total:Number(n.total)||0,etag:`${n.etag||""}`,complete:!0===n.complete}}catch{return}},G=async(e,t)=>{try{const n=await e.getFileHandle("meta.json",{create:!0}),a=await n.createWritable();await a.write(JSON.stringify(t)),await a.close()}catch{}},V=async function*(e){for await(const t of e)yield t},J=async e=>{try{for await(const[t,n]of V(e))"file"==n.kind&&U(t)>=0&&await e.removeEntry(t).catch(()=>{})}catch{}},K=async(e,t)=>{try{const n=await(await e.getFileHandle(t)).getFile(),a=await Q(await e.getFileHandle(`${t}.json`).catch(()=>{}));if(!a)return;return{file:n,size:n.size,total:a.total,etag:a.etag,complete:a.total?n.size>=a.total:!0===a.complete}}catch{return}},X=async(e,t)=>{const n=await H(e,t);if(n){const e=await(async e=>await Q(await e.getFileHandle("meta.json").catch(()=>{})))(n),t=await(async e=>{const t=[];try{for await(const[n,a]of V(e)){const e=U(n);if("file"!=a.kind||e<0)continue;const o=await a.getFile();t.push({part:{start:e,size:o.size},file:o})}}catch{return}const n=(e=>{const t=[];let n=0;for(const a of[...e].sort((e,t)=>e.start-t.start))if(a.size){if(a.start!=n)break;t.push(a),n+=a.size}return t})(t.map(e=>e.part)),a=new Set(n.map(e=>e.start)),o=t.filter(e=>a.has(e.part.start)).sort((e,t)=>e.part.start-t.part.start).map(e=>e.file);if(o.length)return{file:1==o.length?o[0]:new Blob(o),size:n.reduce((e,t)=>e+t.size,0)}})(n),a=e?.total||0,o=t?.size??0,r=t?.file??new Blob;return{file:a&&o>a?r.slice(0,a):r,size:o,total:a,etag:e?.etag||"",complete:a?o>=a:!0===e?.complete}}return await K(e,t)},Y=(e,t)=>{const n=e.headers.get("content-range"),a=/bytes (\d+)-/.exec(n||""),o=/\/(\d+)\s*$/.exec(n||""),r=e.headers.get("etag")||"";if(206==e.status){const e=a?Number(a[1]):t.size,n=o?Number(o[1]):t.total;if(e!=t.size)return;if(t.total&&n&&t.total!=n)return;if(t.etag&&r&&t.etag!=r)return;return{start:e,total:n||t.total,etag:r||t.etag}}if(!t.etag||!r||t.etag==r)return{start:0,total:C(e)||t.total,etag:r||t.etag}},Z=async({dir:e,start:t,skip:n,total:a,body:o,onProgress:r,onChunk:s})=>{let i,c=t,l=0,g=n;const u=async()=>{const t=await e.getFileHandle((e=>`part-${e}`)(c),{create:!0});i=await t.createWritable(),l=0},m=async()=>{const e=i;i=void 0,e&&await e.close().catch(()=>{})};try{const e=o.getReader();for(;;){const{done:n,value:o}=await e.read();if(n)break;s();let f=0;for(g&&(f=Math.min(g,o.byteLength),g-=f);f<o.byteLength;){i||await u();const e=Math.min(O-l,o.byteLength-f);await i.write({type:"write",position:l,data:o.subarray(f,f+e)}),f+=e,l+=e,c+=e,l==O&&await m()}c>t&&r(a?c/a:0,d.download)}await m()}catch(e){throw await m(),e}return c},ee=e=>["NotAllowedError","NotSupportedError"].includes(e?.name||""),te=e=>{const t=async()=>{const t=await D();if(t)try{return await t.getDirectoryHandle(e,{create:!0})}catch{return}};return{stored:async e=>{const n=await t();return n?await X(n,F(e)):void 0},download:async({url:n,signal:a,onProgress:o})=>{const r=`${e}|${n}`,s=I.get(r);if(s)return await s;const i=await t();if(!i)return;const c=(async(e,t,n,a)=>{const o=F(t),r=await K(e,o);if(r?.complete)return r;let s=await H(e,o);s||(await e.removeEntry(o).catch(()=>{}),await e.removeEntry(`${o}.json`).catch(()=>{}),s=await e.getDirectoryHandle(o,{create:!0}));let i=0,c=0;for(;;){if(n?.aborted)throw new Error("Cancelled");const r=await X(e,o);if(r?.complete)return r;const l=r?.size??0,g=r?.total||0,u=r?.etag||"";if(l>i)i=l,c=0;else if(++c>4)throw new Error("Model download failed");l&&g&&a(l/g,d.download);const m=g?Math.min(l+O,g)-1:l+O-1,f=new AbortController,p=()=>f.abort(),h={size:l,total:g,etag:u};let w,y=setTimeout(p,3e4);n?.addEventListener("abort",p,{once:!0});try{if(w=await fetch(t,{signal:f.signal,headers:{Range:`bytes=${l}-${m}`}}),416==w.status){if(await(w.body?.cancel().catch(()=>{})),g&&l>=g){await G(s,{total:g,etag:u,complete:!0});continue}await J(s),await G(s,{total:0,etag:"",complete:!1});continue}if(!w.ok||!w.body)throw await(w.body?.cancel().catch(()=>{})),new Error("Model download failed");const n=Y(w,h);if(!n){await w.body.cancel().catch(()=>{}),await J(s),await G(s,{total:0,etag:"",complete:!1});continue}await G(s,{total:n.total,etag:n.etag,complete:!1});const r=0==n.start?l:0,i=await Z({dir:s,start:n.start+r,skip:r,total:n.total,body:w.body,onProgress:a,onChunk:()=>{clearTimeout(y),y=setTimeout(p,3e4)}});if(!n.total){await G(s,{total:i,etag:n.etag,complete:!0});const t=await X(e,o);if(t)return t}}catch(e){if(n?.aborted)throw new Error("Cancelled");if(e instanceof DOMException&&["QuotaExceededError","NotAllowedError","NotSupportedError","InvalidStateError","TypeMismatchError","SecurityError"].includes(e.name))throw e;await new Promise(e=>setTimeout(e,250+500*c))}finally{clearTimeout(y),n?.removeEventListener("abort",p)}}})(i,n,a,o).finally(()=>{I.delete(r)});return I.set(r,c),await c},remove:async e=>{const n=await t();n&&await(async(e,t)=>{await e.removeEntry(t,{recursive:!0}).catch(()=>{}),await e.removeEntry(`${t}.json`).catch(()=>{})})(n,F(e))},purge:async()=>{const t=await D();t&&await t.removeEntry(e,{recursive:!0}).catch(()=>{})}}},ne="litertlm-models",ae=te("litert-store"),oe={num_output_candidates:1,wait_for_weight_uploads:!0,num_decode_steps_per_sync:3,sequence_batch_size:0,supported_lora_ranks:[],max_top_k:40,enable_decode_logits:!1,enable_external_embeddings:!1,use_submodel:!1,use_autosized_ringbuffers:!0},re=async()=>{try{if("undefined"==typeof caches)return;return await caches.open(ne)}catch{return}},se=new Map,ie=(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)}}))},ce=async(e,t=!0)=>{const n=[];for(const a of e){if("string"==typeof a.content){n.push({role:a.role,content:a.content});continue}const e=[];for(const n of a.content)e.push("text"==n.type?{type:"text",text:n.text}:t?{type:"image",data:await v(n.url)}:{type:"text",text:"[image]"});n.push({role:a.role,content:e})}return n},le=(e,t)=>{if(e.role!=t.role)return!1;if("string"==typeof e.content||"string"==typeof t.content)return e.content==t.content;if(e.content.length!=t.content.length)return!1;for(let n=0;n<e.content.length;n++){const a=e.content[n],o=t.content[n];if(a.type!=o.type)return!1;if("text"==a.type&&"text"==o.type){if(a.text!=o.text)return!1}else if("image"==a.type&&"image"==o.type&&a.url!=o.url)return!1}return!0},de=async function*({engine:e,mod:t,params:n,messages:a,session:o}){const r=a[a.length-1],s=T(n.limits,n.reasoning);if("user"!=r?.role)throw new Error("No user turn");const i=a.slice(0,-1),c=(await ce([r]))[0];if(!c)throw new Error("Empty prompt");const l=!!o.conversation&&o.reasoning==n.reasoning&&o.maxTokens==s.max_tokens&&((e,t)=>{const n="system"==e[0]?.role?e[0]:void 0,a="system"==t[0]?.role?t[0]:void 0;if(!n!=!a)return!1;if(n&&a&&!le(n,a))return!1;const o=n?e.slice(1):e,r=a?t.slice(1):t;if(!r.length||r.length>o.length)return!1;const s=o.length-r.length;for(let e=0;e<r.length;e++)if(!le(o[s+e],r[e]))return!1;return!0})(o.sent,i);let d;if(l&&o.conversation)d=o.conversation;else{const a=await ce(i,!1);o.conversation=void 0,o.sent=[],d=await e.createConversation({sessionConfig:{maxOutputTokens:s.max_tokens,samplerParams:{type:t.SamplerType.TOP_K,temperature:s.temperature,k:40,p:.95}},preface:{...a.length?{messages:a}:{},extra_context:{enable_thinking:n.reasoning}},prefillPrefaceOnInit:a.length>0}),o.conversation=d,o.reasoning=n.reasoning,o.maxTokens=s.max_tokens,o.sent=[...i]}o.sent.push(r);let g,u=!1,m="";const f=()=>{g&&g.cancel().catch(()=>{});try{d.cancel()}catch{}};n.signal?.addEventListener("abort",f,{once:!0});try{if(n.signal?.aborted)return;const e=d.sendMessageStreaming(c).getReader();for(g=e;;){const{done:t,value:n}=await e.read();if(t)break;const a={};let o="string"==typeof n.content?n.content:"";if(Array.isArray(n.content))for(const e of n.content)"text"==e.type&&(o+=e.text);o&&(a.content=o,m+=o);const r=n.channels?.thought;r&&(a.reasoning=r),(a.content||a.reasoning)&&(yield{choices:[{delta:a}]})}u=!0}finally{n.signal?.removeEventListener("abort",f),u&&m?o.sent.push({role:"assistant",content:m}):(o.conversation=void 0,o.sent=[])}},ge={backend:l.gpu,startLLM:async({model:e,limits:t,onProgress:n,signal:a,logging:o})=>{const s=e.litert;if(!s)throw new Error("Model has no LiteRT artifact");const i=await r.e(520).then(()=>r(507));if(await i.getOrLoadGlobalLiteRtLm(y().script),a?.aborted)throw new Error("Cancelled");const c=E(s.repo,s.file),g=await re();let u;const m=g?await g.match(c):void 0;if(m?.body)u=m.body;else{const e=await ae.stored(c);e?.complete&&(u=e.file.stream())}if(!u){const e=await ae.download({url:c,signal:a,onProgress:n}).catch(e=>{if(a?.aborted)throw new Error("Cancelled");if(!ee(e))throw e;o&&console.log("Local model store failed",e)});e&&(u=e.file.stream())}if(!u&&g)try{await(async(e,t,n,a)=>{const o=se.get(e);if(o)return await o;const r=(async()=>{const o=await fetch(e,{signal:n});if(!o.ok||!o.body)throw new Error("Model download failed");const r=C(o),s=ie(o.body,r,a);await t.put(e,new Response(s,{headers:{"content-type":"application/octet-stream",...r?{"content-length":`${r}`}:{}}}))})().finally(()=>{se.delete(e)});return se.set(e,r),await r})(c,g,a,n);const e=await g.match(c);e?.body&&(u=e.body)}catch(e){if(a?.aborted)throw new Error("Cancelled");o&&console.log("Local model cache failed",e)}if(u||(u=await(async(e,t,n)=>{const a=await fetch(e,{signal:t});if(!a.ok||!a.body)throw new Error("Model download failed");return ie(a.body,C(a),n)})(c,a,n)),a?.aborted)throw new Error("Cancelled");n(1,d.load);const f=await i.Engine.create({model:u,mainExecutorSettings:{maxNumTokens:4096,...s.vision?{maxNumImages:Math.max(1,t.maxImages)}:{},backendConfig:oe},...s.vision?{visionBackend:i.Backend.GPU}:{}});if(a?.aborted)throw await f.delete(),new Error("Cancelled");const p={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})=>{const r=()=>{try{o.conversation?.cancel()}catch{}o.conversation=void 0,o.sent=[]};try{return await A({model:t,params:a,stopped:()=>!!a.signal?.aborted,onStall:r,start:async r=>{if(a.signal?.aborted)throw new Error("Cancelled");if(L(r)&&!t.litert?.vision)throw new Error("Model does not accept images");return de({engine:e,mod:n,params:a,messages:r,session:o})}})}catch(e){throw r(),e}})({engine:f,model:e,mod:i,params:t,session:p}),unload:async()=>{p.conversation=void 0,p.sent=[],await f.delete()}}},cached:async e=>{const t=e.litert;if(!t)return!1;const n=E(t.repo,t.file),[a,o]=await Promise.all([re(),ae.stored(n)]);if(o?.complete)return!0;try{return!(!a||!await a.match(n))}catch{return!1}},hasCache:async()=>{try{for(const e of u){const t=e.litert;if(!t)continue;if(await ae.stored(E(t.repo,t.file)))return!0}const e=await re();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 re();await ae.remove(n),a&&await a.delete(n)}catch{}},purge:async()=>{try{await ae.purge(),"undefined"!=typeof caches&&await caches.delete(ne)}catch{}}},ue=te("wllama-store");let me,fe,pe="",he=!1;const we=e=>`${e.wasm}|${e.compatWasm}|${e.compatWorker}`,ye=e=>{const t=w();return{wasm:e?.wasm||t.wasm,compatWasm:e?.compatWasm||t.compatWasm,compatWorker:e?.compatWorker||t.compatWorker}},be=e=>{const t=e=>(...t)=>{he&&("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)}},ve=async()=>{if(me)return me;const e=await r.e(924).then(()=>r(577)),t=fe||ye(),n=new e.Wllama({default:t.wasm},{logger:be(e.LoggerWithoutDebug)});return fe||(fe=t,pe=we(t)),n.setCompat({wasm:t.compatWasm,worker:t.compatWorker}),me=n,n},xe=async()=>{const e=me;me=void 0;try{await(e?.exit())}catch{}},ke=(e,t)=>e.name.includes(t)||!!e.metadata?.originalURL?.includes(t),_e=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)}]:[]]:[]},Me=async e=>{const t=_e(e);if(!t.length)return!1;for(const e of t){const t=await ue.stored(e.url);if(!t?.complete)return!1}return!0},Te=async(e,t)=>{const n=await e.cacheManager.list();return _e(t).every(e=>n.some(t=>ke(t,e.name)))},Ee=async({model:e,signal:t,onProgress:n})=>{const a=_e(e),o=[];for(let e=0;e<a.length;e++){if(t?.aborted)throw new Error("Cancelled");const r=await ue.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},Ce=async e=>{const t=[];for(const n of e){if("string"==typeof n.content){"system"==n.role?t.push({role:"system",content:n.content}):"assistant"==n.role?t.push({role:"assistant",content:n.content}):t.push({role:"user",content:n.content});continue}const e=[];for(const t of n.content)e.push("text"==t.type?{type:"text",text:t.text}:{type:"image",data:await v(t.url)});t.push({role:"user",content:e})}return t},Le={backend:l.cpu,startLLM:async({model:e,limits:t,onProgress:n,signal:a,logging:o,assets:r})=>{he=!0===o;const s=e.cpu;if(!s)throw new Error("Model has no CPU artifact");const i=ye(r),c=we(i);me&&c!=pe&&await xe(),fe=i,pe=c;const g=(u="undefined"!=typeof navigator&&navigator.hardwareConcurrency||4,m=t.maxThreads,Math.max(1,Math.min(Math.max(2,Math.min(8,(u||4)-1)),m)));var u,m;const f=await ve(),p=await Me(e),h=s.mmproj?.file,w=E(s.repo,s.file),y=h?E(s.repo,h):"";if(a?.aborted)throw await xe(),new Error("Cancelled");if(!p&&h){const e=await f.cacheManager.list(),t=e.some(e=>ke(e,s.file)),n=e.some(e=>ke(e,h));t&&!n&&await f.cacheManager.deleteMany(e=>ke(e,s.file))}const b={n_ctx:8192,n_threads:g,reasoning_budget_tokens:t.maxThinkingTokens,n_gpu_layers:0,jinja:!0};try{let t=p?await Ee({model:e,signal:a,onProgress:n}):void 0;t||p||await Te(f,e).catch(()=>!1)||(t=await Ee({model:e,signal:a,onProgress:n}).catch(e=>{if(a?.aborted)throw new Error("Cancelled");if(!ee(e))throw e;o&&console.log("Local model store failed",e)})),t?(n(1,d.load),await f.loadModel(t,b)):await f.loadModelFromUrl(y?{url:w,mmprojUrl:y}:w,{...b,useCache:!0,signal:a,progressCallback:({loaded:e,total:t})=>{const a=t?e/t:0;n(a,a>=1?d.load:d.download)}})}catch(e){throw await xe(),e}if(a?.aborted)throw await xe(),new Error("Cancelled");if(!f.getLoadedContextInfo().n_ctx)throw await xe(),new Error("Failed to create model context");return{model:e,backend:l.cpu,contextSize:8192,chat:t=>(async(e,t,n)=>{if(he=!0===n.logging,e.isModelLoaded())return await A({model:t,params:n,stopped:()=>!!n.signal?.aborted,start:async a=>{if(L(a)&&!e.supportInputModality("image"))throw new Error("Model does not accept images");return await e.createChatCompletion({messages:await Ce(a),...T(n.limits,n.reasoning),abortSignal:n.signal,...t.reasoning?{chat_template_kwargs:{enable_thinking:n.reasoning}}:{}})}})})(f,e,t),unload:async()=>{if(me==f)await xe();else try{await f.exit()}catch{}}}},cached:async e=>{try{return!!e.cpu&&(!!await Me(e)||await Te(await ve(),e))}catch{return!1}},hasCache:async()=>{try{for(const e of u.flatMap(e=>_e(e)))if(await ue.stored(e.url))return!0;const e=await ve();return(await e.cacheManager.list()).length>0}catch{return!1}},remove:async e=>{const t=_e(e);try{for(const e of t)await ue.remove(e.url)}catch{}try{const e=await ve();await e.cacheManager.deleteMany(e=>t.some(t=>ke(e,t.name)))}catch{}},purge:async()=>{try{await ue.purge()}catch{}try{const e=await ve();await e.cacheManager.clear()}catch{}}},Pe=((e={})=>{const t=e.engines?.cpu||Le,n=e.engines?.litert||ge;let a,o,r,s,i,g,m,f=l.none,p=c.idle,h=[],w=0,y={percent:0,phase:d.download};const b=a=>{if(e.backend!=l.none)return e.backend==l.cpu?a.cpu?t:void 0:e.backend==l.gpu?a.litert?n:void 0:a.litert&&"undefined"!=typeof navigator&&navigator.gpu&&"undefined"!=typeof WebAssembly?n:!a.cpu||"undefined"==typeof window&&"undefined"==typeof self&&"undefined"==typeof Worker||"undefined"==typeof WebAssembly?void 0:t},v=(e,t)=>{y={percent:Math.max(0,Math.min(1,e)),phase:t||d.download};for(const e of[...h])e.report(y.percent,y.phase)},x=e=>{p=e},k=()=>{w++,g?.abort(),g=void 0,i=void 0,p==c.downloading&&x(c.idle)},_=(e,t)=>{const n=s;return n?new Promise(a=>{let o=!1;const r={report:e,settle:e=>{o||(o=!0,t?.removeEventListener("abort",i),a(e))}},i=()=>{(e=>{h=h.filter(t=>t!=e),queueMicrotask(()=>{!h.length&&s&&k()})})(r),r.settle(!1)};h.push(r),t?.aborted?i():t?.addEventListener("abort",i,{once:!0}),n.then(r.settle,()=>r.settle(!1))}):Promise.resolve(!1)},M=()=>p==c.ready&&!!a&&!!o;return{load:async({model:e,limits:t,assets:n,logging:l,signal:m,onProgress:p})=>{if(m?.aborted)return!1;if(M()&&o?.id==e.id)return!0;if(s&&i==e.id)return p(y.percent,y.phase),await _(p,m);s&&(k(),h=[],await s.catch(()=>!1));const T=++w,E=(async()=>{await(async()=>{try{if("undefined"==typeof navigator)return;await(navigator.storage?.persist?.())}catch{}})();const s=b(e);if(T!=w)return!1;if(!s)return x(c.unsupported),!1;if(await(async(e,t)=>{if(o&&o.id!=e.id){const e=o,t=r,n=a;a=void 0,o=void 0,r=void 0,await(n?.unload()),await(t?.remove(e))}if(await t.hasCache())for(const n of u)n.id!=e.id&&b(n)==t&&await t.remove(n)})(e,s),T!=w)return!1;const i=await s.cached(e);if(T!=w)return!1;x(c.downloading),v(0,d.download),i&&v(1,d.load);const m=new AbortController;g=m;try{const i=await s.startLLM({model:e,limits:t,logging:l,signal:m.signal,assets:n,onProgress:(e,t)=>{T==w&&v(e,t)}});return T!=w||m.signal.aborted?(await i.unload(),!1):(a=i,o=e,r=s,f=s.backend,x(c.ready),v(1,d.load),!0)}catch(e){return T!=w||(a=void 0,o=void 0,r=void 0,l&&console.log("Local model failed",e),x(c.error)),!1}finally{g==m&&(g=void 0)}})().finally(()=>{s==E&&(s=void 0,i=void 0,h=[])});return s=E,i=e.id,await _(p,m)},chat:async e=>{if(!M()||!a)return;for(;m;)await m.catch(()=>{});if(!M()||!a)return;const t=a.chat(e);m=t;try{return await t}finally{m==t&&(m=void 0)}},unload:async()=>{const e=a;k(),a=void 0,o=void 0,r=void 0,x(c.idle),await(e?.unload())},ready:M,loading:()=>!M()&&p==c.downloading,status:()=>p,backend:()=>f,model:()=>o}})(),je=(e,t,n)=>{const a=e.status(),o=m(t);return{availability:a==c.ready?i.online:a==c.error||a==c.unsupported?i.offline:i.away,status:a,backend:e.backend(),supportsImages:!!o&&h(o),ready:e.ready(),loading:e.loading(),requesting:n.requesting,phase:n.phase,percent:n.percent,...n.error?{error:n.error}:{}}},$e=(e=>{let t,n=0;const a=t=>t.input.onStatus?.(je(e,t.model??t.input.model,t));return o=>{t?.controller.abort();const r={token:++n,input:o,controller:new AbortController,percent:e.ready()?100:0,phase:e.ready()?d.load:d.download,requesting:!1,cancelled:!1};return t=r,a(r),(async t=>{const{input:o}=t,r=!0===o.logging,i=await p(o.model);if(t.model=i?.id,t.token!=n)return;const c=o.messages.some(e=>e.role!=s.system&&"string"==typeof e.content&&!!e.content.trim()),l=o.messages.some(e=>e.role==s.user&&!!e.images?.some(e=>"string"==typeof e&&!!e)),g=c||l;if(!i)return t.error="Unknown model",void a(t);if(!g)return void a(t);if(l&&!c&&!h(i))return t.error="Model does not accept images",void a(t);const u=M(o.limits,!!i.litert),m=await e.load({model:i,limits:u,logging:r,signal:t.controller.signal,onProgress:(e,o)=>{t.token==n&&(t.percent=Math.round(100*e),t.phase=o,a(t))}}).catch(e=>(t.token==n&&(t.error=`${e?.message||`${e}`}`),!1));if(t.token!=n)return;if(t.cancelled)return;if(!m)return t.error||(t.error="Model not ready"),void a(t);const f=h(i),w=l&&!f?o.messages.map(({images:e,...t})=>t):o.messages;if(l&&!f&&!c)return t.error="Model does not accept images",void a(t);t.requesting=!0,t.error=void 0,t.percent=100,t.phase=d.load,a(t);let y,b="",v="";try{y=await e.chat({messages:w,limits:u,reasoning:!0===o.reasoning,logging:r,onReason:e=>{t.token!=n||t.cancelled||(b+=e,o.onThinking?.(b))},onText:e=>{t.token!=n||t.cancelled||(v+=e,o.onResponse?.(v))},signal:t.controller.signal})}catch(e){if(t.token!=n)return;return t.requesting=!1,t.error=`${e?.message||`${e}`}`,void a(t)}t.token==n&&(t.requesting=!1,y||t.cancelled||(t.error="No reply"),a(t))})(r),()=>(t=>{t.token==n&&(t.cancelled=!0,t.controller.abort(),e.status()!=c.ready?queueMicrotask(()=>a(t)):e.unload().then(()=>a(t)))})(r)}})(Pe),Re=(Se=Pe,async(e={})=>{const t=await p(e.model),n=n=>e.onStatus?.(je(Se,t?.id,n));if(!t)return n({requesting:!1,phase:d.download,percent:0,error:"Unknown model"}),!1;let a,o=0,r=d.download;const s=Se.ready()&&Se.model()?.id==t.id;return await Se.load({model:t,limits:M(e.limits,!!t.litert),logging:!0===e.logging,onProgress:(e,t)=>{o=Math.round(100*e),r=t,n({requesting:!1,phase:r,percent:o})}}).catch(e=>(a=`${e?.message||`${e}`}`,!1))?(s&&n({requesting:!1,phase:d.load,percent:100}),!0):(n({requesting:!1,phase:r,percent:o,error:a||"Model not ready"}),!1)});var Se;export{g as ChatModelId,i as ChatStatus,l as LlmBackend,d as LlmPhase,s as LlmRole,c as LlmStatus,$e as createChat,Re as preloadModel};
package/dist/types.d.ts CHANGED
@@ -35,7 +35,7 @@ interface LlmLimits {
35
35
  maxReplyTokens: number;
36
36
  /** Max chain-of-thought tokens, used by the CPU engine when it loads */
37
37
  maxThinkingTokens: number;
38
- /** Total output budget when reasoning is on, thinking plus answer */
38
+ /** Total output budget when reasoning is on, thought plus answer */
39
39
  maxReasoningTokens: number;
40
40
  /** Max images sent in one prompt */
41
41
  maxImages: number;
@@ -66,8 +66,8 @@ interface ChatStatusSnapshot {
66
66
  }
67
67
  /** One chat request, invoked for every response */
68
68
  interface ChatInput {
69
- /** On-device model that answers this request */
70
- model: ChatModelId;
69
+ /** Model that answers, the GPU model when WebGPU is available, else the CPU one */
70
+ model?: ChatModelId;
71
71
  /** Complete prompt, including the system and user turns */
72
72
  messages: ChatMessage[];
73
73
  /** Override any token, prompt or thread budget */
@@ -105,7 +105,12 @@ interface LitertAssets {
105
105
  }
106
106
  /** Navigator with the WebGPU entry point, absent in Node */
107
107
  interface GpuNavigator extends Navigator {
108
- gpu?: object;
108
+ gpu?: {
109
+ /** Ask the browser for a WebGPU adapter */
110
+ requestAdapter: (options?: {
111
+ powerPreference?: `low-power` | `high-performance`;
112
+ }) => Promise<unknown>;
113
+ };
109
114
  }
110
115
  /** Request state that builds one status snapshot */
111
116
  interface ChatState {
@@ -123,6 +128,8 @@ interface ActiveCall extends ChatState {
123
128
  input: ChatInput;
124
129
  controller: AbortController;
125
130
  cancelled: boolean;
131
+ /** Model resolved for this call */
132
+ model?: ChatModelId;
126
133
  }
127
134
  /** Engine and backend overrides used by tests */
128
135
  interface RuntimeDeps {
@@ -330,12 +337,24 @@ type LitertMessage = import('@litert-lm/core').Message;
330
337
  type LitertEngine = import('@litert-lm/core').Engine;
331
338
  /** The LiteRT-LM module namespace */
332
339
  type LitertModule = typeof import('@litert-lm/core');
340
+ /** One reused LiteRT-LM conversation and the turns it already holds */
341
+ interface LitertSession {
342
+ /** Live conversation, dropped when a turn cannot be continued */
343
+ conversation?: import('@litert-lm/core').Conversation;
344
+ /** Built turns already sent to the conversation */
345
+ sent: EngineMessage[];
346
+ /** Reasoning flag the conversation was created with */
347
+ reasoning: boolean;
348
+ /** Output token budget the conversation was created with */
349
+ maxTokens: number;
350
+ }
333
351
  /** Params of one LiteRT-LM chat call */
334
352
  interface LitertChatParams {
335
353
  engine: LitertEngine;
336
354
  model: ChatModel;
337
355
  mod: LitertModule;
338
356
  params: EngineChatParams;
357
+ session: LitertSession;
339
358
  }
340
359
  /** Params of one LiteRT-LM stream */
341
360
  interface StreamLitertParams {
@@ -343,6 +362,7 @@ interface StreamLitertParams {
343
362
  mod: LitertModule;
344
363
  params: EngineChatParams;
345
364
  messages: EngineMessage[];
365
+ session: LitertSession;
346
366
  }
347
367
  /** Streamed delta, reasoning may be inline or structured */
348
368
  interface EngineDelta {
@@ -370,6 +390,12 @@ interface StreamReplyParams {
370
390
  stopped: () => boolean;
371
391
  /** Whether engine logs reach the console */
372
392
  logging?: boolean;
393
+ /** Budget before the first chunk arrives, milliseconds */
394
+ firstTimeoutMs?: number;
395
+ /** Budget between two chunks, milliseconds */
396
+ idleTimeoutMs?: number;
397
+ /** Called when a slow stream keeps the text already delivered */
398
+ onStall?: () => void;
373
399
  }
374
400
  /** Params of one engine chat call */
375
401
  interface EngineRunParams {
@@ -379,5 +405,7 @@ interface EngineRunParams {
379
405
  start: (messages: EngineMessage[]) => Promise<AsyncIterable<unknown>>;
380
406
  /** Whether the caller stopped the generation */
381
407
  stopped: () => boolean;
408
+ /** Called when a slow stream keeps the text already delivered */
409
+ onStall?: () => void;
382
410
  }
383
- 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, LitertChatParams, StreamLitertParams, EngineDelta, EngineChunk, StreamReplyParams, EngineRunParams, };
411
+ 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, LitertSession, LitertChatParams, StreamLitertParams, EngineDelta, EngineChunk, StreamReplyParams, EngineRunParams, };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@degreesign/chat",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
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",