@degreesign/chat 0.3.0 → 0.3.1
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 +14 -5
- package/dist/browser/chat.min.mjs +1 -1
- package/dist/engines/litert.d.ts +4 -2
- package/dist/node/chat.node.min.js +1 -1
- package/dist/node/chat.node.min.mjs +1 -1
- package/dist/types.d.ts +11 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -151,11 +151,13 @@ requested model is already loaded, no download or load happens.
|
|
|
151
151
|
|
|
152
152
|
| State | Effect |
|
|
153
153
|
| ----- | ------ |
|
|
154
|
-
| Downloading or loading |
|
|
154
|
+
| Downloading or loading | Stops waiting for the load. A download another caller waits for keeps going. |
|
|
155
155
|
| Ready | Aborts the reply and unloads the model. |
|
|
156
156
|
| An older call | Nothing, because a newer call owns the runtime. |
|
|
157
157
|
|
|
158
|
-
The model stays loaded between calls until a cancel frees it.
|
|
158
|
+
The model stays loaded between calls until a cancel frees it. One model
|
|
159
|
+
download serves every caller: a chat that joins a preload shares its progress
|
|
160
|
+
and never starts a second download.
|
|
159
161
|
|
|
160
162
|
### Preload
|
|
161
163
|
|
|
@@ -166,7 +168,9 @@ preloadModel(input: ChatPreloadInput): Promise<boolean>
|
|
|
166
168
|
Call it before the first `createChat` to download and load the model early. It
|
|
167
169
|
resolves `true` when the runtime is ready and `false` when the model is unknown,
|
|
168
170
|
unsupported or failed. A later `createChat` for the same model reuses the loaded
|
|
169
|
-
runtime, and either call joins a load already in flight.
|
|
171
|
+
runtime, and either call joins a load already in flight. A preload owns no cancel
|
|
172
|
+
function, so a chat that joins its download and is cancelled leaves the download
|
|
173
|
+
running.
|
|
170
174
|
|
|
171
175
|
`ChatPreloadInput` is the subset of `ChatInput` that a preload needs:
|
|
172
176
|
|
|
@@ -297,8 +301,13 @@ Each engine keeps its own cache. Loading a model unloads the previous runtime
|
|
|
297
301
|
and removes the previous model's cache. The CPU engine uses the wllama cache
|
|
298
302
|
manager; the GPU engine stores the single `.litertlm` file in Cache Storage
|
|
299
303
|
under `litertlm-models`. A quota failure purges the cache of the engine being
|
|
300
|
-
loaded.
|
|
301
|
-
|
|
304
|
+
loaded.
|
|
305
|
+
|
|
306
|
+
The GPU engine stores the whole file before it compiles the model, and one
|
|
307
|
+
download serves every caller. A failed compile, a cancelled call or a repeated
|
|
308
|
+
`createChat` reads the stored file instead of downloading it again. The cache
|
|
309
|
+
write is best effort: if the browser rejects it, the model still streams from
|
|
310
|
+
the network and the next call downloads it again.
|
|
302
311
|
|
|
303
312
|
A CPU vision model downloads its projector with the model and caches both. A
|
|
304
313
|
GPU vision model keeps its encoder in the single `.litertlm` file.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/*! For license information please see chat.min.mjs.LICENSE.txt */
|
|
2
|
-
var e={};const t={};function n(a){const o=t[a];if(void 0!==o)return o.exports;const r=t[a]={exports:{}};return e[a](r,r.exports,n),r.exports}var a,o,r,s,i,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 s,i,c=0;for(s in o)n.o(o,s)&&(n.m[s]=o[s]);for(r&&r(n);c<a.length;c++)i=a[c],n.o(t,i)&&t[i]&&t[i][0](),t[i]=0};n.f.j=(o,r)=>{let s=n.o(t,o)?t[o]:void 0;if(0!==s)if(s)r.push(s[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=>s=t[o]=[e])]),r.push(s[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"}(s||(s={})),function(e){e.download="download",e.load="load"}(i||(i={})),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,m=e=>!!e.cpu?.mmproj||!!e.litert?.vision,g=new URL("./../assets/wllama/wllama.wasm",import.meta.url).href,u=new URL("./../assets/wllama/compat/wllama.wasm",import.meta.url).href,p=new URL("./../assets/wllama/compat/wllama.js",import.meta.url).href,f=new URL("./../assets/litert/litertlm_wasm_internal.js",import.meta.url).href,h=(new URL("./../assets/litert/litertlm_wasm_internal.wasm",import.meta.url).href,288),w=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},y=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})(h,h),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,h,h),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}},x={maxReplyTokens:200,maxThinkingTokens:64,maxReasoningTokens:150,maxImages:2,maxContentLength:1e3,promptCharBudget:1e4,maxThreads:2},b=(e,t,n)=>"number"==typeof e&&Number.isFinite(e)&&e>=n?Math.floor(e):t,v=(e={})=>{const t=x;return{maxReplyTokens:b(e.maxReplyTokens,t.maxReplyTokens,1),maxThinkingTokens:b(e.maxThinkingTokens,t.maxThinkingTokens,1),maxReasoningTokens:b(e.maxReasoningTokens,t.maxReasoningTokens,1),maxImages:b(e.maxImages,t.maxImages,0),maxContentLength:b(e.maxContentLength,t.maxContentLength,1),promptCharBudget:b(e.promptCharBudget,t.promptCharBudget,1),maxThreads:b(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=>e.some(e=>Array.isArray(e.content)&&e.content.some(e=>"image"==e.type)),T=["<|channel>thought\n","<|channel>thought","..."],L=["<channel|>","..."],C=(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,E=(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},j=async({model:e,logging:t,onReason:n,onText:a,start:o,stopped:r})=>{let s="",i="",c="";const l=((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,L);if(t>=0){t&&e(i.slice(0,t)),i=i.slice(t+s.length),a=!1;continue}const c=r(i,L);e(i.slice(0,i.length-c.length)),n=c;break}{const{at:e,tag:s}=o(i,T);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,T);t(i.slice(0,i.length-c.length)),n=c;break}}},flush:()=>{n&&(a?e(n):t(n),n="")}}})(e=>{i+=e,n(e)},e=>{s+=e,a(e)});try{const e=await o();for await(const t of e){if(r())break;const e=t?.choices?.[0],a=e?.delta,o="string"==typeof a?.reasoning_content?a.reasoning_content:"string"==typeof a?.reasoning?a.reasoning:"";e?.finish_reason&&(c=e.finish_reason),o&&(i+=o,n(o)),a?.content&&l.feed(a.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:n,stopped: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 y(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=[],s=[];let i=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),m=l.role==a.user?(l.images||[]).filter(e=>"string"==typeof e&&!!e).slice(0,Math.max(0,c)):[];if(d||m.length){if(d&&i+d.length>t.promptCharBudget)break;i+=d.length,c-=m.length,l.role==a.system?r.unshift(d):l.role==a.assistant?s.unshift({role:"assistant",content:d}):s.unshift({role:"user",content:C(d,m)})}}for(;s.length&&"user"!=s[0].role;)s.shift();const l=[];for(const e of s){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=E(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"),m=[];return d&&m.push({role:"system",content:d}),m.push(...l),m})(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 j({model:e,logging:t.logging,onReason:t.onReason,onText:t.onText,start:()=>n(r),stopped:o})},$="litertlm-models",q={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},P=async()=>{try{if("undefined"==typeof caches)return;return await caches.open($)}catch{return}},B=async e=>{const t=[];for(const n of e){if("string"==typeof n.content){t.push({role:n.role,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 w(t.url)});t.push({role:"user",content:e})}return t},U=async({engine:e,model:t,mod:n,params:a})=>await W({model:t,params:a,stopped:()=>!!a.signal?.aborted,start:async o=>{if(a.signal?.aborted)throw new Error("Cancelled");if(M(o)&&!t.litert?.vision)throw new Error("Model does not accept images");return async function*({engine:e,mod:t,params:n,messages:a}){const o=a[a.length-1],r=k(n.limits,n.reasoning);if("user"!=o?.role)throw new Error("No user turn");const s=await B(a.slice(0,-1)),i=(await B([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:n.reasoning}}});let l;const d=()=>{l&&l.cancel().catch(()=>{});try{c.cancel()}catch{}};n.signal?.addEventListener("abort",d,{once:!0});try{if(n.signal?.aborted)return;const e=c.sendMessageStreaming(i).getReader();for(l=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);const r=n.channels?.thought;r&&(a.reasoning=r),(a.content||a.reasoning)&&(yield{choices:[{delta:a}]})}}finally{n.signal?.removeEventListener("abort",d);try{await c.delete()}catch{}}}({engine:e,mod:n,params:a,messages:o})}}),I={backend:s.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(f),o?.aborted)throw new Error("Cancelled");const d=_(c.repo,c.file),m=await P(),g=m?await m.match(d):void 0;let u;if(g?.body)u=g.body,a(1,i.load);else{const e=await fetch(d,{signal:o});if(!e.ok||!e.body)throw new Error("Model download failed");const t=Number(e.headers.get("content-length"))||0;let n=0;const s=e.body.pipeThrough(new TransformStream({transform:(e,o)=>{n+=e.byteLength,a(t?n/t:0,i.download),o.enqueue(e)}}));if(m){const[e,n]=s.tee();m.put(d,new Response(n,{headers:{"content-type":"application/octet-stream",...t?{"content-length":`${t}`}:{}}})).catch(e=>{r&&console.log("Local model cache failed",e)}),u=e}else u=s}if(o?.aborted)throw new Error("Cancelled");a(1,i.load);const p=await l.Engine.create({model:u,mainExecutorSettings:{maxNumTokens:4096,...c.vision?{maxNumImages:Math.max(1,t.maxImages)}:{},backendConfig:q},...c.vision?{visionBackend:l.Backend.GPU}:{}});if(o?.aborted)throw await p.delete(),new Error("Cancelled");return{model:e,backend:s.gpu,contextSize:4096,chat:t=>U({engine:p,model:e,mod:l,params:t}),unload:async()=>{await p.delete()}}},cached:async e=>{try{const t=e.litert,n=await P();return!(!t||!n)&&!!await n.match(_(t.repo,t.file))}catch{return!1}},hasCache:async()=>{try{const e=await P();return!!e&&(await e.keys()).length>0}catch{return!1}},remove:async e=>{try{const t=e.litert,n=await P();if(!t||!n)return;await n.delete(_(t.repo,t.file))}catch{}},purge:async()=>{try{"undefined"!=typeof caches&&await caches.delete($)}catch{}}};let O,A,S="",F=!1;const N=e=>`${e.wasm}|${e.compatWasm}|${e.compatWorker}`,Q=e=>{const t={wasm:g,compatWasm:u,compatWorker:p};return{wasm:e?.wasm||t.wasm,compatWasm:e?.compatWasm||t.compatWasm,compatWorker:e?.compatWorker||t.compatWorker}},z=e=>{const t=e=>(...t)=>{F&&("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)}},G=async()=>{if(O)return O;const e=await n.e(924).then(()=>n(577)),t=A||Q(),a=new e.Wllama({default:t.wasm},{logger:z(e.LoggerWithoutDebug)});return A||(A=t,S=N(t)),a.setCompat({wasm:t.compatWasm,worker:t.compatWorker}),O=a,a},D=async()=>{const e=O;O=void 0;try{await(e?.exit())}catch{}},V=(e,t)=>e.name.includes(t)||!!e.metadata?.originalURL?.includes(t),K=e=>e.cpu?[e.cpu.file,...e.cpu.mmproj?[e.cpu.mmproj.file]:[]]:[],H=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 w(t.url)});t.push({role:"user",content:e})}return t},J={backend:s.cpu,startLLM:async({model:e,limits:t,onProgress:n,signal:a,logging:o,assets:r})=>{F=!0===o;const c=e.cpu;if(!c)throw new Error("Model has no CPU artifact");const l=Q(r),d=N(l);O&&d!=S&&await D(),A=l,S=d;const m=(g="undefined"!=typeof navigator&&navigator.hardwareConcurrency||4,u=t.maxThreads,Math.max(1,Math.min(Math.max(2,Math.min(8,(g||4)-1)),u)));var g,u;const p=await G(),f=_(c.repo,c.file),h=c.mmproj?.file,w=h?_(c.repo,h):"";if(a?.aborted)throw await D(),new Error("Cancelled");if(h){const e=await p.cacheManager.list(),t=e.some(e=>V(e,c.file)),n=e.some(e=>V(e,h));t&&!n&&await p.cacheManager.deleteMany(e=>V(e,c.file))}try{await p.loadModelFromUrl(w?{url:f,mmprojUrl:w}:f,{n_ctx:8192,n_threads:m,reasoning_budget_tokens:t.maxThinkingTokens,n_gpu_layers:0,useCache:!0,jinja:!0,signal:a,progressCallback:({loaded:e,total:t})=>{const a=t?e/t:0;n(a,a>=1?i.load:i.download)}})}catch(e){throw await D(),e}if(a?.aborted)throw await D(),new Error("Cancelled");if(!p.getLoadedContextInfo().n_ctx)throw await D(),new Error("Failed to create model context");return{model:e,backend:s.cpu,contextSize:8192,chat:t=>(async(e,t,n)=>{if(F=!0===n.logging,e.isModelLoaded())return await W({model:t,params:n,stopped:()=>!!n.signal?.aborted,start:async a=>{if(M(a)&&!e.supportInputModality("image"))throw new Error("Model does not accept images");return await e.createChatCompletion({messages:await H(a),...k(n.limits,n.reasoning),abortSignal:n.signal,...t.reasoning?{chat_template_kwargs:{enable_thinking:n.reasoning}}:{}})}})})(p,e,t),unload:async()=>{if(O==p)await D();else try{await p.exit()}catch{}}}},cached:async e=>{try{if(!e.cpu)return!1;const t=await G(),n=await t.cacheManager.list();return K(e).every(e=>n.some(t=>V(t,e)))}catch{return!1}},hasCache:async()=>{try{const e=await G();return(await e.cacheManager.list()).length>0}catch{return!1}},remove:async e=>{try{if(!e.cpu)return;const t=await G();await t.cacheManager.deleteMany(t=>K(e).some(e=>V(t,e)))}catch{}},purge:async()=>{try{const e=await G();await e.cacheManager.clear()}catch{}}},X=((e={})=>{const t=e.engines?.cpu||J,n=e.engines?.litert||I;let a,o,c,d,m,g,u,p,f=s.none,h=r.idle,w=0,y={percent:0,phase:i.download};const x=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},b=(e,t)=>{y={percent:Math.max(0,Math.min(1,e)),phase:t||i.download},u?.(y.percent,y.phase)},v=e=>{h=e},k=()=>h==r.ready&&!!a&&!!o;return{load:async({model:e,limits:t,assets:n,logging:s,onProgress:p})=>{if(k()&&o?.id==e.id)return!0;if(d&&m==e.id)return u=p,p(y.percent,y.phase),await d;d&&(u=void 0,g?.abort(),await d.catch(()=>!1)),u=p;const h=++w,_=(async()=>{await(async()=>{try{if("undefined"==typeof navigator)return;await(navigator.storage?.persist?.())}catch{}})();const d=x(e);if(h!=w)return!1;if(!d)return v(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&&x(n)==t&&await t.remove(n)})(e,d),h!=w)return!1;const m=await d.cached(e);if(h!=w)return!1;if(!m&&await(async()=>{try{if("undefined"==typeof navigator)return!1;if(!navigator.storage?.estimate)return!1;const{quota:e=0,usage:t=0}=await navigator.storage.estimate();return!!e&&t>=e}catch{return!1}})()&&await d.purge(),h!=w)return!1;v(r.downloading),b(0,i.download),m&&b(1,i.load);const u=new AbortController;g=u;try{const l=await d.startLLM({model:e,limits:t,logging:s,signal:u.signal,assets:n,onProgress:(e,t)=>{h==w&&b(e,t)}});return h!=w||u.signal.aborted?(await l.unload(),!1):(a=l,o=e,c=d,f=d.backend,v(r.ready),b(1,i.load),!0)}catch(e){return h!=w||(a=void 0,o=void 0,c=void 0,s&&console.log("Local model failed",e),(e=>["quota","no space","disk full","ns_error_dom_quota_reached"].some(t=>`${e?.name||""} ${e?.message||`${e}`}`.toLowerCase().includes(t)))(e)&&await d.purge(),v(r.error)),!1}finally{g==u&&(g=void 0)}})().finally(()=>{d==_&&(d=void 0,m=void 0)});return d=_,m=e.id,await _},chat:async e=>{if(!k()||!a)return;for(;p;)await p.catch(()=>{});if(!k()||!a)return;const t=a.chat(e);p=t;try{return await t}finally{p==t&&(p=void 0)}},unload:async()=>{w++,g?.abort(),g=void 0,m=void 0;const e=a;a=void 0,o=void 0,c=void 0,v(r.idle),await(e?.unload())},cancelLoad:()=>{w++,g?.abort(),g=void 0,m=void 0,h==r.downloading&&v(r.idle)},ready:k,loading:()=>!k()&&h==r.downloading,status:()=>h,backend:()=>f,model:()=>o}})(),Y=(e,t,n)=>{const a=e.status(),s=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:!!s&&m(s),ready:e.ready(),loading:e.loading(),requesting:n.requesting,phase:n.phase,percent:n.percent,...n.error?{error:n.error}:{}}},Z=(e=>{let t,n=0;const o=t=>t.input.onStatus?.(Y(e,t.input.model,t));return s=>{t?.controller.abort();const c={token:++n,input:s,controller:new AbortController,percent:e.ready()?100:0,phase:e.ready()?i.load:i.download,requesting:!1,cancelled:!1};return t=c,o(c),(async t=>{const{input:r}=t,s=!0===r.logging,c=d(r.model),l=r.messages.some(e=>e.role!=a.system&&"string"==typeof e.content&&!!e.content.trim()),g=r.messages.some(e=>e.role==a.user&&!!e.images?.some(e=>"string"==typeof e&&!!e)),u=l||g;if(!c)return t.error="Unknown model",void o(t);if(!u)return void o(t);if(g&&!l&&!m(c))return t.error="Model does not accept images",void o(t);const p=v(r.limits),f=await e.load({model:c,limits:p,logging:s,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(!f)return t.cancelled||t.error||(t.error="Model not ready"),void o(t);const h=m(c),w=g&&!h?r.messages.map(({images:e,...t})=>t):r.messages;if(g&&!h&&!l)return t.error="Model does not accept images",void o(t);t.requesting=!0,t.error=void 0,t.percent=100,t.phase=i.load,o(t);let y,x="",b="";try{y=await e.chat({messages:w,limits:p,reasoning:!0===r.reasoning,logging:s,onReason:e=>{t.token!=n||t.cancelled||(x+=e,r.onThinking?.(x))},onText:e=>{t.token!=n||t.cancelled||(b+=e,r.onResponse?.(b))},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?(e.cancelLoad(),o(t)):e.unload().then(()=>o(t)))})(c)}})(X),ee=(te=X,async e=>{const t=d(e.model),n=t=>e.onStatus?.(Y(te,e.model,t));if(!t)return n({requesting:!1,phase:i.download,percent:0,error:"Unknown model"}),!1;let a,o=0,r=i.download;const s=te.ready()&&te.model()?.id==t.id;return await te.load({model:t,limits:v(e.limits),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:i.load,percent:100}),!0):(n({requesting:!1,phase:r,percent:o,error:a||"Model not ready"}),!1)});var te;export{c as ChatModelId,o as ChatStatus,s as LlmBackend,i as LlmPhase,a as LlmRole,r as LlmStatus,Z 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,s,i,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 s,i,c=0;for(s in o)n.o(o,s)&&(n.m[s]=o[s]);for(r&&r(n);c<a.length;c++)i=a[c],n.o(t,i)&&t[i]&&t[i][0](),t[i]=0};n.f.j=(o,r)=>{let s=n.o(t,o)?t[o]:void 0;if(0!==s)if(s)r.push(s[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=>s=t[o]=[e])]),r.push(s[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"}(s||(s={})),function(e){e.download="download",e.load="load"}(i||(i={})),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,m=new URL("./../assets/wllama/wllama.wasm",import.meta.url).href,u=new URL("./../assets/wllama/compat/wllama.wasm",import.meta.url).href,p=new URL("./../assets/wllama/compat/wllama.js",import.meta.url).href,f=new URL("./../assets/litert/litertlm_wasm_internal.js",import.meta.url).href,h=(new URL("./../assets/litert/litertlm_wasm_internal.wasm",import.meta.url).href,288),w=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},y=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})(h,h),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,h,h),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}},b={maxReplyTokens:200,maxThinkingTokens:64,maxReasoningTokens:150,maxImages:2,maxContentLength:1e3,promptCharBudget:1e4,maxThreads:2},x=(e,t,n)=>"number"==typeof e&&Number.isFinite(e)&&e>=n?Math.floor(e):t,k=(e={})=>{const t=b;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)}},v=(e,t)=>({stream:!0,temperature:.7,max_tokens:t?e.maxReasoningTokens:e.maxReplyTokens}),_=(e,t)=>`https://huggingface.co/${e}/resolve/main/${t}`,M=e=>e.some(e=>Array.isArray(e.content)&&e.content.some(e=>"image"==e.type)),T=["<|channel>thought\n","<|channel>thought","..."],L=["<channel|>","..."],C=(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,E=(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},j=async({model:e,logging:t,onReason:n,onText:a,start:o,stopped:r})=>{let s="",i="",c="";const l=((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,L);if(t>=0){t&&e(i.slice(0,t)),i=i.slice(t+s.length),a=!1;continue}const c=r(i,L);e(i.slice(0,i.length-c.length)),n=c;break}{const{at:e,tag:s}=o(i,T);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,T);t(i.slice(0,i.length-c.length)),n=c;break}}},flush:()=>{n&&(a?e(n):t(n),n="")}}})(e=>{i+=e,n(e)},e=>{s+=e,a(e)});try{const e=await o();for await(const t of e){if(r())break;const e=t?.choices?.[0],a=e?.delta,o="string"==typeof a?.reasoning_content?a.reasoning_content:"string"==typeof a?.reasoning?a.reasoning:"";e?.finish_reason&&(c=e.finish_reason),o&&(i+=o,n(o)),a?.content&&l.feed(a.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},q=async({model:e,params:t,start:n,stopped: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 y(t.url)}:t);t.push({role:"user",content:e})}return t})(((e,t=b)=>{const n=t.maxContentLength,o=e.filter(e=>"string"==typeof e.content&&!!e.content.trim()||!(e.role!=a.user||!e.images?.length)),r=[],s=[];let i=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&&i+d.length>t.promptCharBudget)break;i+=d.length,c-=g.length,l.role==a.system?r.unshift(d):l.role==a.assistant?s.unshift({role:"assistant",content:d}):s.unshift({role:"user",content:C(d,g)})}}for(;s.length&&"user"!=s[0].role;)s.shift();const l=[];for(const e of s){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=E(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 j({model:e,logging:t.logging,onReason:t.onReason,onText:t.onText,start:()=>n(r),stopped:o})},P="litertlm-models",W={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},$=async()=>{try{if("undefined"==typeof caches)return;return await caches.open(P)}catch{return}},B=new Map,U=e=>Number(e.headers.get("content-length"))||0,I=(e,t,n)=>{let a=0;return e.pipeThrough(new TransformStream({transform:(e,o)=>{a+=e.byteLength,n(t?a/t:0,i.download),o.enqueue(e)}}))},O=async e=>{const t=[];for(const n of e){if("string"==typeof n.content){t.push({role:n.role,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 w(t.url)});t.push({role:"user",content:e})}return t},A=async({engine:e,model:t,mod:n,params:a})=>await q({model:t,params:a,stopped:()=>!!a.signal?.aborted,start:async o=>{if(a.signal?.aborted)throw new Error("Cancelled");if(M(o)&&!t.litert?.vision)throw new Error("Model does not accept images");return async function*({engine:e,mod:t,params:n,messages:a}){const o=a[a.length-1],r=v(n.limits,n.reasoning);if("user"!=o?.role)throw new Error("No user turn");const s=await O(a.slice(0,-1)),i=(await O([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:n.reasoning}}});let l;const d=()=>{l&&l.cancel().catch(()=>{});try{c.cancel()}catch{}};n.signal?.addEventListener("abort",d,{once:!0});try{if(n.signal?.aborted)return;const e=c.sendMessageStreaming(i).getReader();for(l=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);const r=n.channels?.thought;r&&(a.reasoning=r),(a.content||a.reasoning)&&(yield{choices:[{delta:a}]})}}finally{n.signal?.removeEventListener("abort",d);try{await c.delete()}catch{}}}({engine:e,mod:n,params:a,messages:o})}}),S={backend:s.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(f),o?.aborted)throw new Error("Cancelled");const d=_(c.repo,c.file),g=await $();let m;if(g){if(!await g.match(d))try{await(async(e,t,n,a)=>{const o=B.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=U(o),s=I(o.body,r,a);await t.put(e,new Response(s,{headers:{"content-type":"application/octet-stream",...r?{"content-length":`${r}`}:{}}}))})().finally(()=>{B.delete(e)});return B.set(e,r),await r})(d,g,o,a)}catch(e){if(o?.aborted)throw new Error("Cancelled");r&&console.log("Local model cache failed",e)}const e=await g.match(d);e?.body&&(m=e.body)}if(m||(m=await(async(e,t,n)=>{const a=await fetch(e,{signal:t});if(!a.ok||!a.body)throw new Error("Model download failed");return I(a.body,U(a),n)})(d,o,a)),o?.aborted)throw new Error("Cancelled");a(1,i.load);const u=await l.Engine.create({model:m,mainExecutorSettings:{maxNumTokens:4096,...c.vision?{maxNumImages:Math.max(1,t.maxImages)}:{},backendConfig:W},...c.vision?{visionBackend:l.Backend.GPU}:{}});if(o?.aborted)throw await u.delete(),new Error("Cancelled");return{model:e,backend:s.gpu,contextSize:4096,chat:t=>A({engine:u,model:e,mod:l,params:t}),unload:async()=>{await u.delete()}}},cached:async e=>{try{const t=e.litert,n=await $();return!(!t||!n)&&!!await n.match(_(t.repo,t.file))}catch{return!1}},hasCache:async()=>{try{const e=await $();return!!e&&(await e.keys()).length>0}catch{return!1}},remove:async e=>{try{const t=e.litert,n=await $();if(!t||!n)return;await n.delete(_(t.repo,t.file))}catch{}},purge:async()=>{try{"undefined"!=typeof caches&&await caches.delete(P)}catch{}}};let F,N,Q="",z=!1;const G=e=>`${e.wasm}|${e.compatWasm}|${e.compatWorker}`,D=e=>{const t={wasm:m,compatWasm:u,compatWorker:p};return{wasm:e?.wasm||t.wasm,compatWasm:e?.compatWasm||t.compatWasm,compatWorker:e?.compatWorker||t.compatWorker}},V=e=>{const t=e=>(...t)=>{z&&("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)}},K=async()=>{if(F)return F;const e=await n.e(924).then(()=>n(577)),t=N||D(),a=new e.Wllama({default:t.wasm},{logger:V(e.LoggerWithoutDebug)});return N||(N=t,Q=G(t)),a.setCompat({wasm:t.compatWasm,worker:t.compatWorker}),F=a,a},H=async()=>{const e=F;F=void 0;try{await(e?.exit())}catch{}},J=(e,t)=>e.name.includes(t)||!!e.metadata?.originalURL?.includes(t),X=e=>e.cpu?[e.cpu.file,...e.cpu.mmproj?[e.cpu.mmproj.file]:[]]:[],Y=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 w(t.url)});t.push({role:"user",content:e})}return t},Z={backend:s.cpu,startLLM:async({model:e,limits:t,onProgress:n,signal:a,logging:o,assets:r})=>{z=!0===o;const c=e.cpu;if(!c)throw new Error("Model has no CPU artifact");const l=D(r),d=G(l);F&&d!=Q&&await H(),N=l,Q=d;const g=(m="undefined"!=typeof navigator&&navigator.hardwareConcurrency||4,u=t.maxThreads,Math.max(1,Math.min(Math.max(2,Math.min(8,(m||4)-1)),u)));var m,u;const p=await K(),f=_(c.repo,c.file),h=c.mmproj?.file,w=h?_(c.repo,h):"";if(a?.aborted)throw await H(),new Error("Cancelled");if(h){const e=await p.cacheManager.list(),t=e.some(e=>J(e,c.file)),n=e.some(e=>J(e,h));t&&!n&&await p.cacheManager.deleteMany(e=>J(e,c.file))}try{await p.loadModelFromUrl(w?{url:f,mmprojUrl:w}:f,{n_ctx:8192,n_threads:g,reasoning_budget_tokens:t.maxThinkingTokens,n_gpu_layers:0,useCache:!0,jinja:!0,signal:a,progressCallback:({loaded:e,total:t})=>{const a=t?e/t:0;n(a,a>=1?i.load:i.download)}})}catch(e){throw await H(),e}if(a?.aborted)throw await H(),new Error("Cancelled");if(!p.getLoadedContextInfo().n_ctx)throw await H(),new Error("Failed to create model context");return{model:e,backend:s.cpu,contextSize:8192,chat:t=>(async(e,t,n)=>{if(z=!0===n.logging,e.isModelLoaded())return await q({model:t,params:n,stopped:()=>!!n.signal?.aborted,start:async a=>{if(M(a)&&!e.supportInputModality("image"))throw new Error("Model does not accept images");return await e.createChatCompletion({messages:await Y(a),...v(n.limits,n.reasoning),abortSignal:n.signal,...t.reasoning?{chat_template_kwargs:{enable_thinking:n.reasoning}}:{}})}})})(p,e,t),unload:async()=>{if(F==p)await H();else try{await p.exit()}catch{}}}},cached:async e=>{try{if(!e.cpu)return!1;const t=await K(),n=await t.cacheManager.list();return X(e).every(e=>n.some(t=>J(t,e)))}catch{return!1}},hasCache:async()=>{try{const e=await K();return(await e.cacheManager.list()).length>0}catch{return!1}},remove:async e=>{try{if(!e.cpu)return;const t=await K();await t.cacheManager.deleteMany(t=>X(e).some(e=>J(t,e)))}catch{}},purge:async()=>{try{const e=await K();await e.cacheManager.clear()}catch{}}},ee=((e={})=>{const t=e.engines?.cpu||Z,n=e.engines?.litert||S;let a,o,c,d,g,m,u,p=s.none,f=r.idle,h=[],w=0,y={percent:0,phase:i.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},x=(e,t)=>{y={percent:Math.max(0,Math.min(1,e)),phase:t||i.download};for(const e of[...h])e.report(y.percent,y.phase)},k=e=>{f=e},v=()=>{w++,m?.abort(),m=void 0,g=void 0,f==r.downloading&&k(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",s),a(e))}},s=()=>{(e=>{h=h.filter(t=>t!=e),queueMicrotask(()=>{!h.length&&d&&v()})})(r),r.settle(!1)};h.push(r),t?.aborted?s():t?.addEventListener("abort",s,{once:!0}),n.then(r.settle,()=>r.settle(!1))}):Promise.resolve(!1)},M=()=>f==r.ready&&!!a&&!!o;return{load:async({model:e,limits:t,assets:n,logging:s,signal:u,onProgress:f})=>{if(u?.aborted)return!1;if(M()&&o?.id==e.id)return!0;if(d&&g==e.id)return f(y.percent,y.phase),await _(f,u);d&&(v(),h=[],await d.catch(()=>!1));const T=++w,L=(async()=>{await(async()=>{try{if("undefined"==typeof navigator)return;await(navigator.storage?.persist?.())}catch{}})();const d=b(e);if(T!=w)return!1;if(!d)return k(r.unsupported),!1;if(await(async(e,t)=>{if(o&&o.id!=e.id){const e=o,t=c,n=a;a=void 0,o=void 0,c=void 0,await(n?.unload()),await(t?.remove(e))}if(await t.hasCache())for(const n of l)n.id!=e.id&&b(n)==t&&await t.remove(n)})(e,d),T!=w)return!1;const g=await d.cached(e);if(T!=w)return!1;if(!g&&await(async()=>{try{if("undefined"==typeof navigator)return!1;if(!navigator.storage?.estimate)return!1;const{quota:e=0,usage:t=0}=await navigator.storage.estimate();return!!e&&t>=e}catch{return!1}})()&&await d.purge(),T!=w)return!1;k(r.downloading),x(0,i.download),g&&x(1,i.load);const u=new AbortController;m=u;try{const l=await d.startLLM({model:e,limits:t,logging:s,signal:u.signal,assets:n,onProgress:(e,t)=>{T==w&&x(e,t)}});return T!=w||u.signal.aborted?(await l.unload(),!1):(a=l,o=e,c=d,p=d.backend,k(r.ready),x(1,i.load),!0)}catch(e){return T!=w||(a=void 0,o=void 0,c=void 0,s&&console.log("Local model failed",e),(e=>["quota","no space","disk full","ns_error_dom_quota_reached"].some(t=>`${e?.name||""} ${e?.message||`${e}`}`.toLowerCase().includes(t)))(e)&&await d.purge(),k(r.error)),!1}finally{m==u&&(m=void 0)}})().finally(()=>{d==L&&(d=void 0,g=void 0,h=[])});return d=L,g=e.id,await _(f,u)},chat:async e=>{if(!M()||!a)return;for(;u;)await u.catch(()=>{});if(!M()||!a)return;const t=a.chat(e);u=t;try{return await t}finally{u==t&&(u=void 0)}},unload:async()=>{const e=a;v(),a=void 0,o=void 0,c=void 0,k(r.idle),await(e?.unload())},ready:M,loading:()=>!M()&&f==r.downloading,status:()=>f,backend:()=>p,model:()=>o}})(),te=(e,t,n)=>{const a=e.status(),s=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:!!s&&g(s),ready:e.ready(),loading:e.loading(),requesting:n.requesting,phase:n.phase,percent:n.percent,...n.error?{error:n.error}:{}}},ne=(e=>{let t,n=0;const o=t=>t.input.onStatus?.(te(e,t.input.model,t));return s=>{t?.controller.abort();const c={token:++n,input:s,controller:new AbortController,percent:e.ready()?100:0,phase:e.ready()?i.load:i.download,requesting:!1,cancelled:!1};return t=c,o(c),(async t=>{const{input:r}=t,s=!0===r.logging,c=d(r.model),l=r.messages.some(e=>e.role!=a.system&&"string"==typeof e.content&&!!e.content.trim()),m=r.messages.some(e=>e.role==a.user&&!!e.images?.some(e=>"string"==typeof e&&!!e)),u=l||m;if(!c)return t.error="Unknown model",void o(t);if(!u)return void o(t);if(m&&!l&&!g(c))return t.error="Model does not accept images",void o(t);const p=k(r.limits),f=await e.load({model:c,limits:p,logging:s,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(!f)return t.error||(t.error="Model not ready"),void o(t);const h=g(c),w=m&&!h?r.messages.map(({images:e,...t})=>t):r.messages;if(m&&!h&&!l)return t.error="Model does not accept images",void o(t);t.requesting=!0,t.error=void 0,t.percent=100,t.phase=i.load,o(t);let y,b="",x="";try{y=await e.chat({messages:w,limits:p,reasoning:!0===r.reasoning,logging:s,onReason:e=>{t.token!=n||t.cancelled||(b+=e,r.onThinking?.(b))},onText:e=>{t.token!=n||t.cancelled||(x+=e,r.onResponse?.(x))},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)}})(ee),ae=(oe=ee,async e=>{const t=d(e.model),n=t=>e.onStatus?.(te(oe,e.model,t));if(!t)return n({requesting:!1,phase:i.download,percent:0,error:"Unknown model"}),!1;let a,o=0,r=i.download;const s=oe.ready()&&oe.model()?.id==t.id;return await oe.load({model:t,limits:k(e.limits),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:i.load,percent:100}),!0):(n({requesting:!1,phase:r,percent:o,error:a||"Model not ready"}),!1)});var oe;export{c as ChatModelId,o as ChatStatus,s as LlmBackend,i as LlmPhase,a as LlmRole,r as LlmStatus,ne as createChat,ae as preloadModel};
|
package/dist/engines/litert.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import type { EngineMessage, LitertMessage, LlmEngine } from '../types';
|
|
1
|
+
import type { EngineMessage, LitertMessage, LlmEngine, StartLlmParams } from '../types';
|
|
2
|
+
/** Fetch one model file into the cache, once per URL */
|
|
3
|
+
declare const cacheModel: (url: string, cache: Cache, signal: AbortSignal | undefined, onProgress: StartLlmParams[`onProgress`]) => Promise<void>;
|
|
2
4
|
/** Map built messages to LiteRT turns */
|
|
3
5
|
declare const toLitertMessages: (messages: EngineMessage[]) => Promise<LitertMessage[]>;
|
|
4
6
|
declare const litertEngine: LlmEngine;
|
|
5
|
-
export { litertEngine, toLitertMessages, };
|
|
7
|
+
export { cacheModel, litertEngine, toLitertMessages, };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/*! For license information please see chat.node.min.js.LICENSE.txt */
|
|
2
|
-
(()=>{"use strict";var e={};const t={};function n(a){const o=t[a];if(void 0!==o)return o.exports;const r=t[a]={exports:{}};return e[a](r,r.exports,n),r.exports}n.m=e,n.d=(e,t)=>{for(var a in t)n.o(t,a)&&!n.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:t[a]})},n.f={},n.e=e=>{const t=[];return n.f.require(e,t),Promise.all(t)},n.u=e=>({520:"engine-litert",924:"engine-wllama"}[e]+".node.min.js"),n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.r=e=>{Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{const e={792:1};n.f.require=(t,a)=>{if(!e[t]){const a=require("./"+n.u(t));e[t]||(t=>{const a=t.modules,o=t.ids,r=t.runtime;for(var s in a)n.o(a,s)&&(n.m[s]=a[s]);r&&r(n);for(var i=0;i<o.length;i++)e[o[i]]=1})(a)}}})();let a={};var o,r,s,i,c,l;n.r(a),n.d(a,{ChatModelId:()=>l,ChatStatus:()=>r,LlmBackend:()=>i,LlmPhase:()=>c,LlmRole:()=>o,LlmStatus:()=>s,createChat:()=>Z,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"}(s||(s={})),function(e){e.cpu="cpu",e.gpu="gpu",e.none="none"}(i||(i={})),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"),p=()=>{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},f=()=>{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},h=288,w=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},y=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})(h,h),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,h,h),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}},x={maxReplyTokens:200,maxThinkingTokens:64,maxReasoningTokens:150,maxImages:2,maxContentLength:1e3,promptCharBudget:1e4,maxThreads:2},v=(e,t,n)=>"number"==typeof e&&Number.isFinite(e)&&e>=n?Math.floor(e):t,b=(e={})=>{const t=x;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=>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,R=(e,t)=>{if("string"==typeof e&&"string"==typeof t)return`${e}\n${t}`;const n=[];for(const a of[...j(e),{type:"text",text:"\n"},...j(t)]){const e=n[n.length-1];"text"==a.type&&"text"==e?.type?e.text+=a.text:n.push(a)}return n},q=async({model:e,logging:t,onReason:n,onText:a,start:o,stopped:r})=>{let s="",i="",c="";const l=((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,T);if(t>=0){t&&e(i.slice(0,t)),i=i.slice(t+s.length),a=!1;continue}const c=r(i,T);e(i.slice(0,i.length-c.length)),n=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)),a=!0;continue}const c=r(i,C);t(i.slice(0,i.length-c.length)),n=c;break}}},flush:()=>{n&&(a?e(n):t(n),n="")}}})(e=>{i+=e,n(e)},e=>{s+=e,a(e)});try{const e=await o();for await(const t of e){if(r())break;const e=t?.choices?.[0],a=e?.delta,o="string"==typeof a?.reasoning_content?a.reasoning_content:"string"==typeof a?.reasoning?a.reasoning:"";e?.finish_reason&&(c=e.finish_reason),o&&(i+=o,n(o)),a?.content&&l.feed(a.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},E=async({model:e,params:t,start:n,stopped:a})=>{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 y(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=[],s=[];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==o.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==o.system?r.unshift(d):l.role==o.assistant?s.unshift({role:"assistant",content:d}):s.unshift({role:"user",content:L(d,g)})}}for(;s.length&&"user"!=s[0].role;)s.shift();const l=[];for(const e of s){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,r),await q({model:e,logging:t.logging,onReason:t.onReason,onText:t.onText,start:()=>n(r),stopped:a})},W="litertlm-models",$={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},B=async()=>{try{if("undefined"==typeof caches)return;return await caches.open(W)}catch{return}},P=async e=>{const t=[];for(const n of e){if("string"==typeof n.content){t.push({role:n.role,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 w(t.url)});t.push({role:"user",content:e})}return t},I=async({engine:e,model:t,mod:n,params:a})=>await E({model:t,params:a,stopped:()=>!!a.signal?.aborted,start:async o=>{if(a.signal?.aborted)throw new Error("Cancelled");if(M(o)&&!t.litert?.vision)throw new Error("Model does not accept images");return async function*({engine:e,mod:t,params:n,messages:a}){const o=a[a.length-1],r=k(n.limits,n.reasoning);if("user"!=o?.role)throw new Error("No user turn");const s=await P(a.slice(0,-1)),i=(await P([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:n.reasoning}}});let l;const d=()=>{l&&l.cancel().catch(()=>{});try{c.cancel()}catch{}};n.signal?.addEventListener("abort",d,{once:!0});try{if(n.signal?.aborted)return;const e=c.sendMessageStreaming(i).getReader();for(l=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);const r=n.channels?.thought;r&&(a.reasoning=r),(a.content||a.reasoning)&&(yield{choices:[{delta:a}]})}}finally{n.signal?.removeEventListener("abort",d);try{await c.delete()}catch{}}}({engine:e,mod:n,params:a,messages:o})}}),O={backend:i.gpu,startLLM:async({model:e,limits:t,onProgress:a,signal:o,logging:r})=>{const s=e.litert;if(!s)throw new Error("Model has no LiteRT artifact");const l=await n.e(520).then(()=>n(507));if(await l.getOrLoadGlobalLiteRtLm(f().script),o?.aborted)throw new Error("Cancelled");const d=_(s.repo,s.file),g=await B(),u=g?await g.match(d):void 0;let m;if(u?.body)m=u.body,a(1,c.load);else{const e=await fetch(d,{signal:o});if(!e.ok||!e.body)throw new Error("Model download failed");const t=Number(e.headers.get("content-length"))||0;let n=0;const s=e.body.pipeThrough(new TransformStream({transform:(e,o)=>{n+=e.byteLength,a(t?n/t:0,c.download),o.enqueue(e)}}));if(g){const[e,n]=s.tee();g.put(d,new Response(n,{headers:{"content-type":"application/octet-stream",...t?{"content-length":`${t}`}:{}}})).catch(e=>{r&&console.log("Local model cache failed",e)}),m=e}else m=s}if(o?.aborted)throw new Error("Cancelled");a(1,c.load);const p=await l.Engine.create({model:m,mainExecutorSettings:{maxNumTokens:4096,...s.vision?{maxNumImages:Math.max(1,t.maxImages)}:{},backendConfig:$},...s.vision?{visionBackend:l.Backend.GPU}:{}});if(o?.aborted)throw await p.delete(),new Error("Cancelled");return{model:e,backend:i.gpu,contextSize:4096,chat:t=>I({engine:p,model:e,mod:l,params:t}),unload:async()=>{await p.delete()}}},cached:async e=>{try{const t=e.litert,n=await B();return!(!t||!n)&&!!await n.match(_(t.repo,t.file))}catch{return!1}},hasCache:async()=>{try{const e=await B();return!!e&&(await e.keys()).length>0}catch{return!1}},remove:async e=>{try{const t=e.litert,n=await B();if(!t||!n)return;await n.delete(_(t.repo,t.file))}catch{}},purge:async()=>{try{"undefined"!=typeof caches&&await caches.delete(W)}catch{}}};let A,S,U="",F=!1;const N=e=>`${e.wasm}|${e.compatWasm}|${e.compatWorker}`,Q=e=>{const t=p();return{wasm:e?.wasm||t.wasm,compatWasm:e?.compatWasm||t.compatWasm,compatWorker:e?.compatWorker||t.compatWorker}},z=e=>{const t=e=>(...t)=>{F&&("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)}},G=async()=>{if(A)return A;const e=await n.e(924).then(()=>n(577)),t=S||Q(),a=new e.Wllama({default:t.wasm},{logger:z(e.LoggerWithoutDebug)});return S||(S=t,U=N(t)),a.setCompat({wasm:t.compatWasm,worker:t.compatWorker}),A=a,a},D=async()=>{const e=A;A=void 0;try{await(e?.exit())}catch{}},V=(e,t)=>e.name.includes(t)||!!e.metadata?.originalURL?.includes(t),K=e=>e.cpu?[e.cpu.file,...e.cpu.mmproj?[e.cpu.mmproj.file]:[]]:[],H=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 w(t.url)});t.push({role:"user",content:e})}return t},J={backend:i.cpu,startLLM:async({model:e,limits:t,onProgress:n,signal:a,logging:o,assets:r})=>{F=!0===o;const s=e.cpu;if(!s)throw new Error("Model has no CPU artifact");const l=Q(r),d=N(l);A&&d!=U&&await D(),S=l,U=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 p=await G(),f=_(s.repo,s.file),h=s.mmproj?.file,w=h?_(s.repo,h):"";if(a?.aborted)throw await D(),new Error("Cancelled");if(h){const e=await p.cacheManager.list(),t=e.some(e=>V(e,s.file)),n=e.some(e=>V(e,h));t&&!n&&await p.cacheManager.deleteMany(e=>V(e,s.file))}try{await p.loadModelFromUrl(w?{url:f,mmprojUrl:w}:f,{n_ctx:8192,n_threads:g,reasoning_budget_tokens:t.maxThinkingTokens,n_gpu_layers:0,useCache:!0,jinja:!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 D(),e}if(a?.aborted)throw await D(),new Error("Cancelled");if(!p.getLoadedContextInfo().n_ctx)throw await D(),new Error("Failed to create model context");return{model:e,backend:i.cpu,contextSize:8192,chat:t=>(async(e,t,n)=>{if(F=!0===n.logging,e.isModelLoaded())return await E({model:t,params:n,stopped:()=>!!n.signal?.aborted,start:async a=>{if(M(a)&&!e.supportInputModality("image"))throw new Error("Model does not accept images");return await e.createChatCompletion({messages:await H(a),...k(n.limits,n.reasoning),abortSignal:n.signal,...t.reasoning?{chat_template_kwargs:{enable_thinking:n.reasoning}}:{}})}})})(p,e,t),unload:async()=>{if(A==p)await D();else try{await p.exit()}catch{}}}},cached:async e=>{try{if(!e.cpu)return!1;const t=await G(),n=await t.cacheManager.list();return K(e).every(e=>n.some(t=>V(t,e)))}catch{return!1}},hasCache:async()=>{try{const e=await G();return(await e.cacheManager.list()).length>0}catch{return!1}},remove:async e=>{try{if(!e.cpu)return;const t=await G();await t.cacheManager.deleteMany(t=>K(e).some(e=>V(t,e)))}catch{}},purge:async()=>{try{const e=await G();await e.cacheManager.clear()}catch{}}},X=((e={})=>{const t=e.engines?.cpu||J,n=e.engines?.litert||O;let a,o,r,l,g,u,m,p,f=i.none,h=s.idle,w=0,y={percent:0,phase:c.download};const x=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||c.download},m?.(y.percent,y.phase)},b=e=>{h=e},k=()=>h==s.ready&&!!a&&!!o;return{load:async({model:e,limits:t,assets:n,logging:i,onProgress:p})=>{if(k()&&o?.id==e.id)return!0;if(l&&g==e.id)return m=p,p(y.percent,y.phase),await l;l&&(m=void 0,u?.abort(),await l.catch(()=>!1)),m=p;const h=++w,_=(async()=>{await(async()=>{try{if("undefined"==typeof navigator)return;await(navigator.storage?.persist?.())}catch{}})();const l=x(e);if(h!=w)return!1;if(!l)return b(s.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&&x(n)==t&&await t.remove(n)})(e,l),h!=w)return!1;const g=await l.cached(e);if(h!=w)return!1;if(!g&&await(async()=>{try{if("undefined"==typeof navigator)return!1;if(!navigator.storage?.estimate)return!1;const{quota:e=0,usage:t=0}=await navigator.storage.estimate();return!!e&&t>=e}catch{return!1}})()&&await l.purge(),h!=w)return!1;b(s.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:i,signal:m.signal,assets:n,onProgress:(e,t)=>{h==w&&v(e,t)}});return h!=w||m.signal.aborted?(await d.unload(),!1):(a=d,o=e,r=l,f=l.backend,b(s.ready),v(1,c.load),!0)}catch(e){return h!=w||(a=void 0,o=void 0,r=void 0,i&&console.log("Local model failed",e),(e=>["quota","no space","disk full","ns_error_dom_quota_reached"].some(t=>`${e?.name||""} ${e?.message||`${e}`}`.toLowerCase().includes(t)))(e)&&await l.purge(),b(s.error)),!1}finally{u==m&&(u=void 0)}})().finally(()=>{l==_&&(l=void 0,g=void 0)});return l=_,g=e.id,await _},chat:async e=>{if(!k()||!a)return;for(;p;)await p.catch(()=>{});if(!k()||!a)return;const t=a.chat(e);p=t;try{return await t}finally{p==t&&(p=void 0)}},unload:async()=>{w++,u?.abort(),u=void 0,g=void 0;const e=a;a=void 0,o=void 0,r=void 0,b(s.idle),await(e?.unload())},cancelLoad:()=>{w++,u?.abort(),u=void 0,g=void 0,h==s.downloading&&b(s.idle)},ready:k,loading:()=>!k()&&h==s.downloading,status:()=>h,backend:()=>f,model:()=>o}})(),Y=(e,t,n)=>{const a=e.status(),o=g(t);return{availability:a==s.ready?r.online:a==s.error||a==s.unsupported?r.offline:r.away,status:a,backend:e.backend(),supportsImages:!!o&&u(o),ready:e.ready(),loading:e.loading(),requesting:n.requesting,phase:n.phase,percent:n.percent,...n.error?{error:n.error}:{}}},Z=(e=>{let t,n=0;const a=t=>t.input.onStatus?.(Y(e,t.input.model,t));return r=>{t?.controller.abort();const i={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=i,a(i),(async t=>{const{input:r}=t,s=!0===r.logging,i=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(!i)return t.error="Unknown model",void a(t);if(!m)return void a(t);if(d&&!l&&!u(i))return t.error="Model does not accept images",void a(t);const p=b(r.limits),f=await e.load({model:i,limits:p,logging:s,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(!f)return t.cancelled||t.error||(t.error="Model not ready"),void a(t);const h=u(i),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,x="",v="";try{y=await e.chat({messages:w,limits:p,reasoning:!0===r.reasoning,logging:s,onReason:e=>{t.token!=n||t.cancelled||(x+=e,r.onThinking?.(x))},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))})(i),()=>(t=>{t.token==n&&(t.cancelled=!0,t.controller.abort(),e.status()!=s.ready?(e.cancelLoad(),a(t)):e.unload().then(()=>a(t)))})(i)}})(X),ee=(te=X,async e=>{const t=g(e.model),n=t=>e.onStatus?.(Y(te,e.model,t));if(!t)return n({requesting:!1,phase:c.download,percent:0,error:"Unknown model"}),!1;let a,o=0,r=c.download;const s=te.ready()&&te.model()?.id==t.id;return await te.load({model:t,limits:b(e.limits),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:c.load,percent:100}),!0):(n({requesting:!1,phase:r,percent:o,error:a||"Model not ready"}),!1)});var te;module.exports=a})();
|
|
2
|
+
(()=>{"use strict";var e={};const t={};function n(a){const o=t[a];if(void 0!==o)return o.exports;const r=t[a]={exports:{}};return e[a](r,r.exports,n),r.exports}n.m=e,n.d=(e,t)=>{for(var a in t)n.o(t,a)&&!n.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:t[a]})},n.f={},n.e=e=>{const t=[];return n.f.require(e,t),Promise.all(t)},n.u=e=>({520:"engine-litert",924:"engine-wllama"}[e]+".node.min.js"),n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.r=e=>{Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{const e={792:1};n.f.require=(t,a)=>{if(!e[t]){const a=require("./"+n.u(t));e[t]||(t=>{const a=t.modules,o=t.ids,r=t.runtime;for(var s in a)n.o(a,s)&&(n.m[s]=a[s]);r&&r(n);for(var i=0;i<o.length;i++)e[o[i]]=1})(a)}}})();let a={};var o,r,s,i,c,l;n.r(a),n.d(a,{ChatModelId:()=>l,ChatStatus:()=>r,LlmBackend:()=>i,LlmPhase:()=>c,LlmRole:()=>o,LlmStatus:()=>s,createChat:()=>ne,preloadModel:()=>ae}),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"}(s||(s={})),function(e){e.cpu="cpu",e.gpu="gpu",e.none="none"}(i||(i={})),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"),p=()=>{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},f=()=>{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},h=288,w=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},y=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})(h,h),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,h,h),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}},b={maxReplyTokens:200,maxThinkingTokens:64,maxReasoningTokens:150,maxImages:2,maxContentLength:1e3,promptCharBudget:1e4,maxThreads:2},x=(e,t,n)=>"number"==typeof e&&Number.isFinite(e)&&e>=n?Math.floor(e):t,v=(e={})=>{const t=b;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)}},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=>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,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(e),{type:"text",text:"\n"},...E(t)]){const e=n[n.length-1];"text"==a.type&&"text"==e?.type?e.text+=a.text:n.push(a)}return n},q=async({model:e,logging:t,onReason:n,onText:a,start:o,stopped:r})=>{let s="",i="",c="";const l=((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,T);if(t>=0){t&&e(i.slice(0,t)),i=i.slice(t+s.length),a=!1;continue}const c=r(i,T);e(i.slice(0,i.length-c.length)),n=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)),a=!0;continue}const c=r(i,C);t(i.slice(0,i.length-c.length)),n=c;break}}},flush:()=>{n&&(a?e(n):t(n),n="")}}})(e=>{i+=e,n(e)},e=>{s+=e,a(e)});try{const e=await o();for await(const t of e){if(r())break;const e=t?.choices?.[0],a=e?.delta,o="string"==typeof a?.reasoning_content?a.reasoning_content:"string"==typeof a?.reasoning?a.reasoning:"";e?.finish_reason&&(c=e.finish_reason),o&&(i+=o,n(o)),a?.content&&l.feed(a.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},R=async({model:e,params:t,start:n,stopped:a})=>{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 y(t.url)}:t);t.push({role:"user",content:e})}return t})(((e,t=b)=>{const n=t.maxContentLength,a=e.filter(e=>"string"==typeof e.content&&!!e.content.trim()||!(e.role!=o.user||!e.images?.length)),r=[],s=[];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==o.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==o.system?r.unshift(d):l.role==o.assistant?s.unshift({role:"assistant",content:d}):s.unshift({role:"user",content:L(d,g)})}}for(;s.length&&"user"!=s[0].role;)s.shift();const l=[];for(const e of s){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,r),await q({model:e,logging:t.logging,onReason:t.onReason,onText:t.onText,start:()=>n(r),stopped:a})},W="litertlm-models",$={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},P=async()=>{try{if("undefined"==typeof caches)return;return await caches.open(W)}catch{return}},B=new Map,I=e=>Number(e.headers.get("content-length"))||0,O=(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)}}))},A=async e=>{const t=[];for(const n of e){if("string"==typeof n.content){t.push({role:n.role,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 w(t.url)});t.push({role:"user",content:e})}return t},S=async({engine:e,model:t,mod:n,params:a})=>await R({model:t,params:a,stopped:()=>!!a.signal?.aborted,start:async o=>{if(a.signal?.aborted)throw new Error("Cancelled");if(M(o)&&!t.litert?.vision)throw new Error("Model does not accept images");return async function*({engine:e,mod:t,params:n,messages:a}){const o=a[a.length-1],r=k(n.limits,n.reasoning);if("user"!=o?.role)throw new Error("No user turn");const s=await A(a.slice(0,-1)),i=(await A([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:n.reasoning}}});let l;const d=()=>{l&&l.cancel().catch(()=>{});try{c.cancel()}catch{}};n.signal?.addEventListener("abort",d,{once:!0});try{if(n.signal?.aborted)return;const e=c.sendMessageStreaming(i).getReader();for(l=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);const r=n.channels?.thought;r&&(a.reasoning=r),(a.content||a.reasoning)&&(yield{choices:[{delta:a}]})}}finally{n.signal?.removeEventListener("abort",d);try{await c.delete()}catch{}}}({engine:e,mod:n,params:a,messages:o})}}),U={backend:i.gpu,startLLM:async({model:e,limits:t,onProgress:a,signal:o,logging:r})=>{const s=e.litert;if(!s)throw new Error("Model has no LiteRT artifact");const l=await n.e(520).then(()=>n(507));if(await l.getOrLoadGlobalLiteRtLm(f().script),o?.aborted)throw new Error("Cancelled");const d=_(s.repo,s.file),g=await P();let u;if(g){if(!await g.match(d))try{await(async(e,t,n,a)=>{const o=B.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=I(o),s=O(o.body,r,a);await t.put(e,new Response(s,{headers:{"content-type":"application/octet-stream",...r?{"content-length":`${r}`}:{}}}))})().finally(()=>{B.delete(e)});return B.set(e,r),await r})(d,g,o,a)}catch(e){if(o?.aborted)throw new Error("Cancelled");r&&console.log("Local model cache failed",e)}const e=await g.match(d);e?.body&&(u=e.body)}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 O(a.body,I(a),n)})(d,o,a)),o?.aborted)throw new Error("Cancelled");a(1,c.load);const m=await l.Engine.create({model:u,mainExecutorSettings:{maxNumTokens:4096,...s.vision?{maxNumImages:Math.max(1,t.maxImages)}:{},backendConfig:$},...s.vision?{visionBackend:l.Backend.GPU}:{}});if(o?.aborted)throw await m.delete(),new Error("Cancelled");return{model:e,backend:i.gpu,contextSize:4096,chat:t=>S({engine:m,model:e,mod:l,params:t}),unload:async()=>{await m.delete()}}},cached:async e=>{try{const t=e.litert,n=await P();return!(!t||!n)&&!!await n.match(_(t.repo,t.file))}catch{return!1}},hasCache:async()=>{try{const e=await P();return!!e&&(await e.keys()).length>0}catch{return!1}},remove:async e=>{try{const t=e.litert,n=await P();if(!t||!n)return;await n.delete(_(t.repo,t.file))}catch{}},purge:async()=>{try{"undefined"!=typeof caches&&await caches.delete(W)}catch{}}};let F,N,Q="",z=!1;const G=e=>`${e.wasm}|${e.compatWasm}|${e.compatWorker}`,D=e=>{const t=p();return{wasm:e?.wasm||t.wasm,compatWasm:e?.compatWasm||t.compatWasm,compatWorker:e?.compatWorker||t.compatWorker}},V=e=>{const t=e=>(...t)=>{z&&("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)}},K=async()=>{if(F)return F;const e=await n.e(924).then(()=>n(577)),t=N||D(),a=new e.Wllama({default:t.wasm},{logger:V(e.LoggerWithoutDebug)});return N||(N=t,Q=G(t)),a.setCompat({wasm:t.compatWasm,worker:t.compatWorker}),F=a,a},H=async()=>{const e=F;F=void 0;try{await(e?.exit())}catch{}},J=(e,t)=>e.name.includes(t)||!!e.metadata?.originalURL?.includes(t),X=e=>e.cpu?[e.cpu.file,...e.cpu.mmproj?[e.cpu.mmproj.file]:[]]:[],Y=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 w(t.url)});t.push({role:"user",content:e})}return t},Z={backend:i.cpu,startLLM:async({model:e,limits:t,onProgress:n,signal:a,logging:o,assets:r})=>{z=!0===o;const s=e.cpu;if(!s)throw new Error("Model has no CPU artifact");const l=D(r),d=G(l);F&&d!=Q&&await H(),N=l,Q=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 p=await K(),f=_(s.repo,s.file),h=s.mmproj?.file,w=h?_(s.repo,h):"";if(a?.aborted)throw await H(),new Error("Cancelled");if(h){const e=await p.cacheManager.list(),t=e.some(e=>J(e,s.file)),n=e.some(e=>J(e,h));t&&!n&&await p.cacheManager.deleteMany(e=>J(e,s.file))}try{await p.loadModelFromUrl(w?{url:f,mmprojUrl:w}:f,{n_ctx:8192,n_threads:g,reasoning_budget_tokens:t.maxThinkingTokens,n_gpu_layers:0,useCache:!0,jinja:!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 H(),e}if(a?.aborted)throw await H(),new Error("Cancelled");if(!p.getLoadedContextInfo().n_ctx)throw await H(),new Error("Failed to create model context");return{model:e,backend:i.cpu,contextSize:8192,chat:t=>(async(e,t,n)=>{if(z=!0===n.logging,e.isModelLoaded())return await R({model:t,params:n,stopped:()=>!!n.signal?.aborted,start:async a=>{if(M(a)&&!e.supportInputModality("image"))throw new Error("Model does not accept images");return await e.createChatCompletion({messages:await Y(a),...k(n.limits,n.reasoning),abortSignal:n.signal,...t.reasoning?{chat_template_kwargs:{enable_thinking:n.reasoning}}:{}})}})})(p,e,t),unload:async()=>{if(F==p)await H();else try{await p.exit()}catch{}}}},cached:async e=>{try{if(!e.cpu)return!1;const t=await K(),n=await t.cacheManager.list();return X(e).every(e=>n.some(t=>J(t,e)))}catch{return!1}},hasCache:async()=>{try{const e=await K();return(await e.cacheManager.list()).length>0}catch{return!1}},remove:async e=>{try{if(!e.cpu)return;const t=await K();await t.cacheManager.deleteMany(t=>X(e).some(e=>J(t,e)))}catch{}},purge:async()=>{try{const e=await K();await e.cacheManager.clear()}catch{}}},ee=((e={})=>{const t=e.engines?.cpu||Z,n=e.engines?.litert||U;let a,o,r,l,g,u,m,p=i.none,f=s.idle,h=[],w=0,y={percent:0,phase:c.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},x=(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)},v=e=>{f=e},k=()=>{w++,u?.abort(),u=void 0,g=void 0,f==s.downloading&&v(s.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",s),a(e))}},s=()=>{(e=>{h=h.filter(t=>t!=e),queueMicrotask(()=>{!h.length&&l&&k()})})(r),r.settle(!1)};h.push(r),t?.aborted?s():t?.addEventListener("abort",s,{once:!0}),n.then(r.settle,()=>r.settle(!1))}):Promise.resolve(!1)},M=()=>f==s.ready&&!!a&&!!o;return{load:async({model:e,limits:t,assets:n,logging:i,signal:m,onProgress:f})=>{if(m?.aborted)return!1;if(M()&&o?.id==e.id)return!0;if(l&&g==e.id)return f(y.percent,y.phase),await _(f,m);l&&(k(),h=[],await l.catch(()=>!1));const C=++w,T=(async()=>{await(async()=>{try{if("undefined"==typeof navigator)return;await(navigator.storage?.persist?.())}catch{}})();const l=b(e);if(C!=w)return!1;if(!l)return v(s.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),C!=w)return!1;const g=await l.cached(e);if(C!=w)return!1;if(!g&&await(async()=>{try{if("undefined"==typeof navigator)return!1;if(!navigator.storage?.estimate)return!1;const{quota:e=0,usage:t=0}=await navigator.storage.estimate();return!!e&&t>=e}catch{return!1}})()&&await l.purge(),C!=w)return!1;v(s.downloading),x(0,c.download),g&&x(1,c.load);const m=new AbortController;u=m;try{const d=await l.startLLM({model:e,limits:t,logging:i,signal:m.signal,assets:n,onProgress:(e,t)=>{C==w&&x(e,t)}});return C!=w||m.signal.aborted?(await d.unload(),!1):(a=d,o=e,r=l,p=l.backend,v(s.ready),x(1,c.load),!0)}catch(e){return C!=w||(a=void 0,o=void 0,r=void 0,i&&console.log("Local model failed",e),(e=>["quota","no space","disk full","ns_error_dom_quota_reached"].some(t=>`${e?.name||""} ${e?.message||`${e}`}`.toLowerCase().includes(t)))(e)&&await l.purge(),v(s.error)),!1}finally{u==m&&(u=void 0)}})().finally(()=>{l==T&&(l=void 0,g=void 0,h=[])});return l=T,g=e.id,await _(f,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,v(s.idle),await(e?.unload())},ready:M,loading:()=>!M()&&f==s.downloading,status:()=>f,backend:()=>p,model:()=>o}})(),te=(e,t,n)=>{const a=e.status(),o=g(t);return{availability:a==s.ready?r.online:a==s.error||a==s.unsupported?r.offline:r.away,status:a,backend:e.backend(),supportsImages:!!o&&u(o),ready:e.ready(),loading:e.loading(),requesting:n.requesting,phase:n.phase,percent:n.percent,...n.error?{error:n.error}:{}}},ne=(e=>{let t,n=0;const a=t=>t.input.onStatus?.(te(e,t.input.model,t));return r=>{t?.controller.abort();const i={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=i,a(i),(async t=>{const{input:r}=t,s=!0===r.logging,i=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(!i)return t.error="Unknown model",void a(t);if(!m)return void a(t);if(d&&!l&&!u(i))return t.error="Model does not accept images",void a(t);const p=v(r.limits),f=await e.load({model:i,limits:p,logging:s,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(!f)return t.error||(t.error="Model not ready"),void a(t);const h=u(i),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="",x="";try{y=await e.chat({messages:w,limits:p,reasoning:!0===r.reasoning,logging:s,onReason:e=>{t.token!=n||t.cancelled||(b+=e,r.onThinking?.(b))},onText:e=>{t.token!=n||t.cancelled||(x+=e,r.onResponse?.(x))},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))})(i),()=>(t=>{t.token==n&&(t.cancelled=!0,t.controller.abort(),e.status()!=s.ready?queueMicrotask(()=>a(t)):e.unload().then(()=>a(t)))})(i)}})(ee),ae=(oe=ee,async e=>{const t=g(e.model),n=t=>e.onStatus?.(te(oe,e.model,t));if(!t)return n({requesting:!1,phase:c.download,percent:0,error:"Unknown model"}),!1;let a,o=0,r=c.download;const s=oe.ready()&&oe.model()?.id==t.id;return await oe.load({model:t,limits:v(e.limits),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:c.load,percent:100}),!0):(n({requesting:!1,phase:r,percent:o,error:a||"Model not ready"}),!1)});var oe;module.exports=a})();
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/*! For license information please see chat.node.min.mjs.LICENSE.txt */
|
|
2
|
-
import{dirname as e,join as t}from"node:path";import{fileURLToPath as n}from"node:url";var a={};const o={};function r(e){const t=o[e];if(void 0!==t)return t.exports;const n=o[e]={exports:{}};return a[e](n,n.exports,r),n.exports}var s,i,c,l,d,g;r.m=a,r.d=(e,t)=>{for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.f={},r.e=e=>{const t=[];return r.f.j(e,t),Promise.all(t)},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r.r=e=>{Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{const e={520:()=>import("./engine-litert.node.min.mjs"),924:()=>import("./engine-wllama.node.min.mjs")},t={792:0},n=e=>{let{__webpack_esm_ids__:n,__webpack_esm_modules__:a,__webpack_esm_runtime__:o}=e;var s,i,c=0;for(s in a)r.o(a,s)&&(r.m[s]=a[s]);for(o&&o(r);c<n.length;c++)i=n[c],r.o(t,i)&&t[i]&&t[i][0](),t[i]=0};r.f.j=(a,o)=>{let s=r.o(t,a)?t[a]:void 0;if(0!==s)if(s)o.push(s[1]);else{let r=e[a]().then(n,e=>{throw 0!==t[a]&&(t[a]=void 0),e});r=Promise.race([r,new Promise(e=>s=t[a]=[e])]),o.push(s[1]=r)}}})(),function(e){e.system="system",e.user="user",e.assistant="assistant"}(s||(s={})),function(e){e[e.online=0]="online",e[e.away=1]="away",e[e.offline=2]="offline"}(i||(i={})),function(e){e[e.idle=0]="idle",e[e.downloading=1]="downloading",e[e.ready=2]="ready",e[e.error=3]="error",e[e.unsupported=4]="unsupported"}(c||(c={})),function(e){e.cpu="cpu",e.gpu="gpu",e.none="none"}(l||(l={})),function(e){e.download="download",e.load="load"}(d||(d={})),function(e){e.qwen3_vl_2b="qwen3-vl-2b",e.gemma4_e2b="gemma4-e2b"}(g||(g={}));const m=[{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}}],u=e=>e?m.find(t=>t.id==e):void 0,p=e=>!!e.cpu?.mmproj||!!e.litert?.vision,f=()=>{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},h=()=>{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},w=288,y=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})(w,w),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,w,w),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}},b={maxReplyTokens:200,maxThinkingTokens:64,maxReasoningTokens:150,maxImages:2,maxContentLength:1e3,promptCharBudget:1e4,maxThreads:2},v=(e,t,n)=>"number"==typeof e&&Number.isFinite(e)&&e>=n?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)}},_=(e,t)=>({stream:!0,temperature:.7,max_tokens:t?e.maxReasoningTokens:e.maxReplyTokens}),T=(e,t)=>`https://huggingface.co/${e}/resolve/main/${t}`,M=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,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(e),{type:"text",text:"\n"},...E(t)]){const e=n[n.length-1];"text"==a.type&&"text"==e?.type?e.text+=a.text:n.push(a)}return n},W=async({model:e,logging:t,onReason:n,onText:a,start:o,stopped:r})=>{let s="",i="",c="";const l=((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,L);if(t>=0){t&&e(i.slice(0,t)),i=i.slice(t+s.length),a=!1;continue}const c=r(i,L);e(i.slice(0,i.length-c.length)),n=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)),a=!0;continue}const c=r(i,C);t(i.slice(0,i.length-c.length)),n=c;break}}},flush:()=>{n&&(a?e(n):t(n),n="")}}})(e=>{i+=e,n(e)},e=>{s+=e,a(e)});try{const e=await o();for await(const t of e){if(r())break;const e=t?.choices?.[0],a=e?.delta,o="string"==typeof a?.reasoning_content?a.reasoning_content:"string"==typeof a?.reasoning?a.reasoning:"";e?.finish_reason&&(c=e.finish_reason),o&&(i+=o,n(o)),a?.content&&l.feed(a.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},$=async({model:e,params:t,start:n,stopped:a})=>{const o=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=b)=>{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: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=R(t.content,e.content):"assistant"==e.role?l.push({role:"assistant",content:e.content}):l.push({role:"user",content:e.content})}const d=o.join("\n\n"),g=[];return d&&g.push({role:"system",content:d}),g.push(...l),g})(t.messages,t.limits));return t.logging&&((e,t)=>{console.log("Local chat call",e.id,t.map(({role:e,content:t})=>({role:e,content:"****",...Array.isArray(t)?{images:t.filter(e=>"image"==e.type).length}:{}})))})(e,o),await W({model:e,logging:t.logging,onReason:t.onReason,onText:t.onText,start:()=>n(o),stopped:a})},q="litertlm-models",P={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},B=async()=>{try{if("undefined"==typeof caches)return;return await caches.open(q)}catch{return}},I=async e=>{const t=[];for(const n of e){if("string"==typeof n.content){t.push({role:n.role,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 y(t.url)});t.push({role:"user",content:e})}return t},O=async({engine:e,model:t,mod:n,params:a})=>await $({model:t,params:a,stopped:()=>!!a.signal?.aborted,start:async o=>{if(a.signal?.aborted)throw new Error("Cancelled");if(M(o)&&!t.litert?.vision)throw new Error("Model does not accept images");return async function*({engine:e,mod:t,params:n,messages:a}){const o=a[a.length-1],r=_(n.limits,n.reasoning);if("user"!=o?.role)throw new Error("No user turn");const s=await I(a.slice(0,-1)),i=(await I([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:n.reasoning}}});let l;const d=()=>{l&&l.cancel().catch(()=>{});try{c.cancel()}catch{}};n.signal?.addEventListener("abort",d,{once:!0});try{if(n.signal?.aborted)return;const e=c.sendMessageStreaming(i).getReader();for(l=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);const r=n.channels?.thought;r&&(a.reasoning=r),(a.content||a.reasoning)&&(yield{choices:[{delta:a}]})}}finally{n.signal?.removeEventListener("abort",d);try{await c.delete()}catch{}}}({engine:e,mod:n,params:a,messages:o})}}),A={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(h().script),a?.aborted)throw new Error("Cancelled");const c=T(s.repo,s.file),g=await B(),m=g?await g.match(c):void 0;let u;if(m?.body)u=m.body,n(1,d.load);else{const e=await fetch(c,{signal:a});if(!e.ok||!e.body)throw new Error("Model download failed");const t=Number(e.headers.get("content-length"))||0;let r=0;const s=e.body.pipeThrough(new TransformStream({transform:(e,a)=>{r+=e.byteLength,n(t?r/t:0,d.download),a.enqueue(e)}}));if(g){const[e,n]=s.tee();g.put(c,new Response(n,{headers:{"content-type":"application/octet-stream",...t?{"content-length":`${t}`}:{}}})).catch(e=>{o&&console.log("Local model cache failed",e)}),u=e}else u=s}if(a?.aborted)throw new Error("Cancelled");n(1,d.load);const p=await i.Engine.create({model:u,mainExecutorSettings:{maxNumTokens:4096,...s.vision?{maxNumImages:Math.max(1,t.maxImages)}:{},backendConfig:P},...s.vision?{visionBackend:i.Backend.GPU}:{}});if(a?.aborted)throw await p.delete(),new Error("Cancelled");return{model:e,backend:l.gpu,contextSize:4096,chat:t=>O({engine:p,model:e,mod:i,params:t}),unload:async()=>{await p.delete()}}},cached:async e=>{try{const t=e.litert,n=await B();return!(!t||!n)&&!!await n.match(T(t.repo,t.file))}catch{return!1}},hasCache:async()=>{try{const e=await B();return!!e&&(await e.keys()).length>0}catch{return!1}},remove:async e=>{try{const t=e.litert,n=await B();if(!t||!n)return;await n.delete(T(t.repo,t.file))}catch{}},purge:async()=>{try{"undefined"!=typeof caches&&await caches.delete(q)}catch{}}};let S,U,F="",N=!1;const Q=e=>`${e.wasm}|${e.compatWasm}|${e.compatWorker}`,D=e=>{const t=f();return{wasm:e?.wasm||t.wasm,compatWasm:e?.compatWasm||t.compatWasm,compatWorker:e?.compatWorker||t.compatWorker}},z=e=>{const t=e=>(...t)=>{N&&("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)}},G=async()=>{if(S)return S;const e=await r.e(924).then(()=>r(577)),t=U||D(),n=new e.Wllama({default:t.wasm},{logger:z(e.LoggerWithoutDebug)});return U||(U=t,F=Q(t)),n.setCompat({wasm:t.compatWasm,worker:t.compatWorker}),S=n,n},V=async()=>{const e=S;S=void 0;try{await(e?.exit())}catch{}},K=(e,t)=>e.name.includes(t)||!!e.metadata?.originalURL?.includes(t),H=e=>e.cpu?[e.cpu.file,...e.cpu.mmproj?[e.cpu.mmproj.file]:[]]:[],J=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 y(t.url)});t.push({role:"user",content:e})}return t},X={backend:l.cpu,startLLM:async({model:e,limits:t,onProgress:n,signal:a,logging:o,assets:r})=>{N=!0===o;const s=e.cpu;if(!s)throw new Error("Model has no CPU artifact");const i=D(r),c=Q(i);S&&c!=F&&await V(),U=i,F=c;const g=(m="undefined"!=typeof navigator&&navigator.hardwareConcurrency||4,u=t.maxThreads,Math.max(1,Math.min(Math.max(2,Math.min(8,(m||4)-1)),u)));var m,u;const p=await G(),f=T(s.repo,s.file),h=s.mmproj?.file,w=h?T(s.repo,h):"";if(a?.aborted)throw await V(),new Error("Cancelled");if(h){const e=await p.cacheManager.list(),t=e.some(e=>K(e,s.file)),n=e.some(e=>K(e,h));t&&!n&&await p.cacheManager.deleteMany(e=>K(e,s.file))}try{await p.loadModelFromUrl(w?{url:f,mmprojUrl:w}:f,{n_ctx:8192,n_threads:g,reasoning_budget_tokens:t.maxThinkingTokens,n_gpu_layers:0,useCache:!0,jinja:!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 V(),e}if(a?.aborted)throw await V(),new Error("Cancelled");if(!p.getLoadedContextInfo().n_ctx)throw await V(),new Error("Failed to create model context");return{model:e,backend:l.cpu,contextSize:8192,chat:t=>(async(e,t,n)=>{if(N=!0===n.logging,e.isModelLoaded())return await $({model:t,params:n,stopped:()=>!!n.signal?.aborted,start:async a=>{if(M(a)&&!e.supportInputModality("image"))throw new Error("Model does not accept images");return await e.createChatCompletion({messages:await J(a),..._(n.limits,n.reasoning),abortSignal:n.signal,...t.reasoning?{chat_template_kwargs:{enable_thinking:n.reasoning}}:{}})}})})(p,e,t),unload:async()=>{if(S==p)await V();else try{await p.exit()}catch{}}}},cached:async e=>{try{if(!e.cpu)return!1;const t=await G(),n=await t.cacheManager.list();return H(e).every(e=>n.some(t=>K(t,e)))}catch{return!1}},hasCache:async()=>{try{const e=await G();return(await e.cacheManager.list()).length>0}catch{return!1}},remove:async e=>{try{if(!e.cpu)return;const t=await G();await t.cacheManager.deleteMany(t=>H(e).some(e=>K(t,e)))}catch{}},purge:async()=>{try{const e=await G();await e.cacheManager.clear()}catch{}}},Y=((e={})=>{const t=e.engines?.cpu||X,n=e.engines?.litert||A;let a,o,r,s,i,g,u,p,f=l.none,h=c.idle,w=0,y={percent:0,phase:d.download};const x=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},b=(e,t)=>{y={percent:Math.max(0,Math.min(1,e)),phase:t||d.download},u?.(y.percent,y.phase)},v=e=>{h=e},k=()=>h==c.ready&&!!a&&!!o;return{load:async({model:e,limits:t,assets:n,logging:l,onProgress:p})=>{if(k()&&o?.id==e.id)return!0;if(s&&i==e.id)return u=p,p(y.percent,y.phase),await s;s&&(u=void 0,g?.abort(),await s.catch(()=>!1)),u=p;const h=++w,_=(async()=>{await(async()=>{try{if("undefined"==typeof navigator)return;await(navigator.storage?.persist?.())}catch{}})();const s=x(e);if(h!=w)return!1;if(!s)return v(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 m)n.id!=e.id&&x(n)==t&&await t.remove(n)})(e,s),h!=w)return!1;const i=await s.cached(e);if(h!=w)return!1;if(!i&&await(async()=>{try{if("undefined"==typeof navigator)return!1;if(!navigator.storage?.estimate)return!1;const{quota:e=0,usage:t=0}=await navigator.storage.estimate();return!!e&&t>=e}catch{return!1}})()&&await s.purge(),h!=w)return!1;v(c.downloading),b(0,d.download),i&&b(1,d.load);const u=new AbortController;g=u;try{const i=await s.startLLM({model:e,limits:t,logging:l,signal:u.signal,assets:n,onProgress:(e,t)=>{h==w&&b(e,t)}});return h!=w||u.signal.aborted?(await i.unload(),!1):(a=i,o=e,r=s,f=s.backend,v(c.ready),b(1,d.load),!0)}catch(e){return h!=w||(a=void 0,o=void 0,r=void 0,l&&console.log("Local model failed",e),(e=>["quota","no space","disk full","ns_error_dom_quota_reached"].some(t=>`${e?.name||""} ${e?.message||`${e}`}`.toLowerCase().includes(t)))(e)&&await s.purge(),v(c.error)),!1}finally{g==u&&(g=void 0)}})().finally(()=>{s==_&&(s=void 0,i=void 0)});return s=_,i=e.id,await _},chat:async e=>{if(!k()||!a)return;for(;p;)await p.catch(()=>{});if(!k()||!a)return;const t=a.chat(e);p=t;try{return await t}finally{p==t&&(p=void 0)}},unload:async()=>{w++,g?.abort(),g=void 0,i=void 0;const e=a;a=void 0,o=void 0,r=void 0,v(c.idle),await(e?.unload())},cancelLoad:()=>{w++,g?.abort(),g=void 0,i=void 0,h==c.downloading&&v(c.idle)},ready:k,loading:()=>!k()&&h==c.downloading,status:()=>h,backend:()=>f,model:()=>o}})(),Z=(e,t,n)=>{const a=e.status(),o=u(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&&p(o),ready:e.ready(),loading:e.loading(),requesting:n.requesting,phase:n.phase,percent:n.percent,...n.error?{error:n.error}:{}}},ee=(e=>{let t,n=0;const a=t=>t.input.onStatus?.(Z(e,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=u(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 a(t);if(!g)return void a(t);if(l&&!c&&!p(i))return t.error="Model does not accept images",void a(t);const m=k(o.limits),f=await e.load({model:i,limits:m,logging:r,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(!f)return t.cancelled||t.error||(t.error="Model not ready"),void a(t);const h=p(i),w=l&&!h?o.messages.map(({images:e,...t})=>t):o.messages;if(l&&!h&&!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,x="",b="";try{y=await e.chat({messages:w,limits:m,reasoning:!0===o.reasoning,logging:r,onReason:e=>{t.token!=n||t.cancelled||(x+=e,o.onThinking?.(x))},onText:e=>{t.token!=n||t.cancelled||(b+=e,o.onResponse?.(b))},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?(e.cancelLoad(),a(t)):e.unload().then(()=>a(t)))})(r)}})(Y),te=(ne=Y,async e=>{const t=u(e.model),n=t=>e.onStatus?.(Z(ne,e.model,t));if(!t)return n({requesting:!1,phase:d.download,percent:0,error:"Unknown model"}),!1;let a,o=0,r=d.download;const s=ne.ready()&&ne.model()?.id==t.id;return await ne.load({model:t,limits:k(e.limits),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 ne;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 m=[{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}}],u=e=>e?m.find(t=>t.id==e):void 0,p=e=>!!e.cpu?.mmproj||!!e.litert?.vision,f=()=>{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},h=()=>{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},w=288,y=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},b=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})(w,w),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,w,w),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}},x={maxReplyTokens:200,maxThinkingTokens:64,maxReasoningTokens:150,maxImages:2,maxContentLength:1e3,promptCharBudget:1e4,maxThreads:2},k=(e,t,n)=>"number"==typeof e&&Number.isFinite(e)&&e>=n?Math.floor(e):t,v=(e={})=>{const t=x;return{maxReplyTokens:k(e.maxReplyTokens,t.maxReplyTokens,1),maxThinkingTokens:k(e.maxThinkingTokens,t.maxThinkingTokens,1),maxReasoningTokens:k(e.maxReasoningTokens,t.maxReasoningTokens,1),maxImages:k(e.maxImages,t.maxImages,0),maxContentLength:k(e.maxContentLength,t.maxContentLength,1),promptCharBudget:k(e.promptCharBudget,t.promptCharBudget,1),maxThreads:k(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}`,T=e=>e.some(e=>Array.isArray(e.content)&&e.content.some(e=>"image"==e.type)),C=["<|channel>thought\n","<|channel>thought","..."],L=["<channel|>","..."],E=(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,R=(e,t)=>{if("string"==typeof e&&"string"==typeof t)return`${e}\n${t}`;const n=[];for(const a of[...j(e),{type:"text",text:"\n"},...j(t)]){const e=n[n.length-1];"text"==a.type&&"text"==e?.type?e.text+=a.text:n.push(a)}return n},q=async({model:e,logging:t,onReason:n,onText:a,start:o,stopped:r})=>{let s="",i="",c="";const l=((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,L);if(t>=0){t&&e(i.slice(0,t)),i=i.slice(t+s.length),a=!1;continue}const c=r(i,L);e(i.slice(0,i.length-c.length)),n=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)),a=!0;continue}const c=r(i,C);t(i.slice(0,i.length-c.length)),n=c;break}}},flush:()=>{n&&(a?e(n):t(n),n="")}}})(e=>{i+=e,n(e)},e=>{s+=e,a(e)});try{const e=await o();for await(const t of e){if(r())break;const e=t?.choices?.[0],a=e?.delta,o="string"==typeof a?.reasoning_content?a.reasoning_content:"string"==typeof a?.reasoning?a.reasoning:"";e?.finish_reason&&(c=e.finish_reason),o&&(i+=o,n(o)),a?.content&&l.feed(a.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},P=async({model:e,params:t,start:n,stopped:a})=>{const o=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 b(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!=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:E(d,g)})}}for(;r.length&&"user"!=r[0].role;)r.shift();const l=[];for(const e of r){const t=l[l.length-1];"assistant"==t?.role&&"assistant"==e.role?t.content=`${t.content}\n${e.content}`:"user"==t?.role&&"user"==e.role?t.content=R(t.content,e.content):"assistant"==e.role?l.push({role:"assistant",content:e.content}):l.push({role:"user",content:e.content})}const d=o.join("\n\n"),g=[];return d&&g.push({role:"system",content:d}),g.push(...l),g})(t.messages,t.limits));return t.logging&&((e,t)=>{console.log("Local chat call",e.id,t.map(({role:e,content:t})=>({role:e,content:"****",...Array.isArray(t)?{images:t.filter(e=>"image"==e.type).length}:{}})))})(e,o),await q({model:e,logging:t.logging,onReason:t.onReason,onText:t.onText,start:()=>n(o),stopped:a})},W="litertlm-models",$={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},B=async()=>{try{if("undefined"==typeof caches)return;return await caches.open(W)}catch{return}},I=new Map,O=e=>Number(e.headers.get("content-length"))||0,A=(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)}}))},S=async e=>{const t=[];for(const n of e){if("string"==typeof n.content){t.push({role:n.role,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 y(t.url)});t.push({role:"user",content:e})}return t},U=async({engine:e,model:t,mod:n,params:a})=>await P({model:t,params:a,stopped:()=>!!a.signal?.aborted,start:async o=>{if(a.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:n,messages:a}){const o=a[a.length-1],r=_(n.limits,n.reasoning);if("user"!=o?.role)throw new Error("No user turn");const s=await S(a.slice(0,-1)),i=(await S([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:n.reasoning}}});let l;const d=()=>{l&&l.cancel().catch(()=>{});try{c.cancel()}catch{}};n.signal?.addEventListener("abort",d,{once:!0});try{if(n.signal?.aborted)return;const e=c.sendMessageStreaming(i).getReader();for(l=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);const r=n.channels?.thought;r&&(a.reasoning=r),(a.content||a.reasoning)&&(yield{choices:[{delta:a}]})}}finally{n.signal?.removeEventListener("abort",d);try{await c.delete()}catch{}}}({engine:e,mod:n,params:a,messages:o})}}),F={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(h().script),a?.aborted)throw new Error("Cancelled");const c=M(s.repo,s.file),g=await B();let m;if(g){if(!await g.match(c))try{await(async(e,t,n,a)=>{const o=I.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=O(o),s=A(o.body,r,a);await t.put(e,new Response(s,{headers:{"content-type":"application/octet-stream",...r?{"content-length":`${r}`}:{}}}))})().finally(()=>{I.delete(e)});return I.set(e,r),await r})(c,g,a,n)}catch(e){if(a?.aborted)throw new Error("Cancelled");o&&console.log("Local model cache failed",e)}const e=await g.match(c);e?.body&&(m=e.body)}if(m||(m=await(async(e,t,n)=>{const a=await fetch(e,{signal:t});if(!a.ok||!a.body)throw new Error("Model download failed");return A(a.body,O(a),n)})(c,a,n)),a?.aborted)throw new Error("Cancelled");n(1,d.load);const u=await i.Engine.create({model:m,mainExecutorSettings:{maxNumTokens:4096,...s.vision?{maxNumImages:Math.max(1,t.maxImages)}:{},backendConfig:$},...s.vision?{visionBackend:i.Backend.GPU}:{}});if(a?.aborted)throw await u.delete(),new Error("Cancelled");return{model:e,backend:l.gpu,contextSize:4096,chat:t=>U({engine:u,model:e,mod:i,params:t}),unload:async()=>{await u.delete()}}},cached:async e=>{try{const t=e.litert,n=await B();return!(!t||!n)&&!!await n.match(M(t.repo,t.file))}catch{return!1}},hasCache:async()=>{try{const e=await B();return!!e&&(await e.keys()).length>0}catch{return!1}},remove:async e=>{try{const t=e.litert,n=await B();if(!t||!n)return;await n.delete(M(t.repo,t.file))}catch{}},purge:async()=>{try{"undefined"!=typeof caches&&await caches.delete(W)}catch{}}};let N,Q,D="",z=!1;const G=e=>`${e.wasm}|${e.compatWasm}|${e.compatWorker}`,V=e=>{const t=f();return{wasm:e?.wasm||t.wasm,compatWasm:e?.compatWasm||t.compatWasm,compatWorker:e?.compatWorker||t.compatWorker}},K=e=>{const t=e=>(...t)=>{z&&("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)}},H=async()=>{if(N)return N;const e=await r.e(924).then(()=>r(577)),t=Q||V(),n=new e.Wllama({default:t.wasm},{logger:K(e.LoggerWithoutDebug)});return Q||(Q=t,D=G(t)),n.setCompat({wasm:t.compatWasm,worker:t.compatWorker}),N=n,n},J=async()=>{const e=N;N=void 0;try{await(e?.exit())}catch{}},X=(e,t)=>e.name.includes(t)||!!e.metadata?.originalURL?.includes(t),Y=e=>e.cpu?[e.cpu.file,...e.cpu.mmproj?[e.cpu.mmproj.file]:[]]:[],Z=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 y(t.url)});t.push({role:"user",content:e})}return t},ee={backend:l.cpu,startLLM:async({model:e,limits:t,onProgress:n,signal:a,logging:o,assets:r})=>{z=!0===o;const s=e.cpu;if(!s)throw new Error("Model has no CPU artifact");const i=V(r),c=G(i);N&&c!=D&&await J(),Q=i,D=c;const g=(m="undefined"!=typeof navigator&&navigator.hardwareConcurrency||4,u=t.maxThreads,Math.max(1,Math.min(Math.max(2,Math.min(8,(m||4)-1)),u)));var m,u;const p=await H(),f=M(s.repo,s.file),h=s.mmproj?.file,w=h?M(s.repo,h):"";if(a?.aborted)throw await J(),new Error("Cancelled");if(h){const e=await p.cacheManager.list(),t=e.some(e=>X(e,s.file)),n=e.some(e=>X(e,h));t&&!n&&await p.cacheManager.deleteMany(e=>X(e,s.file))}try{await p.loadModelFromUrl(w?{url:f,mmprojUrl:w}:f,{n_ctx:8192,n_threads:g,reasoning_budget_tokens:t.maxThinkingTokens,n_gpu_layers:0,useCache:!0,jinja:!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 J(),e}if(a?.aborted)throw await J(),new Error("Cancelled");if(!p.getLoadedContextInfo().n_ctx)throw await J(),new Error("Failed to create model context");return{model:e,backend:l.cpu,contextSize:8192,chat:t=>(async(e,t,n)=>{if(z=!0===n.logging,e.isModelLoaded())return await P({model:t,params:n,stopped:()=>!!n.signal?.aborted,start:async a=>{if(T(a)&&!e.supportInputModality("image"))throw new Error("Model does not accept images");return await e.createChatCompletion({messages:await Z(a),..._(n.limits,n.reasoning),abortSignal:n.signal,...t.reasoning?{chat_template_kwargs:{enable_thinking:n.reasoning}}:{}})}})})(p,e,t),unload:async()=>{if(N==p)await J();else try{await p.exit()}catch{}}}},cached:async e=>{try{if(!e.cpu)return!1;const t=await H(),n=await t.cacheManager.list();return Y(e).every(e=>n.some(t=>X(t,e)))}catch{return!1}},hasCache:async()=>{try{const e=await H();return(await e.cacheManager.list()).length>0}catch{return!1}},remove:async e=>{try{if(!e.cpu)return;const t=await H();await t.cacheManager.deleteMany(t=>Y(e).some(e=>X(t,e)))}catch{}},purge:async()=>{try{const e=await H();await e.cacheManager.clear()}catch{}}},te=((e={})=>{const t=e.engines?.cpu||ee,n=e.engines?.litert||F;let a,o,r,s,i,g,u,p=l.none,f=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},x=(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)},k=e=>{f=e},v=()=>{w++,g?.abort(),g=void 0,i=void 0,f==c.downloading&&k(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&&v()})})(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=()=>f==c.ready&&!!a&&!!o;return{load:async({model:e,limits:t,assets:n,logging:l,signal:u,onProgress:f})=>{if(u?.aborted)return!1;if(M()&&o?.id==e.id)return!0;if(s&&i==e.id)return f(y.percent,y.phase),await _(f,u);s&&(v(),h=[],await s.catch(()=>!1));const T=++w,C=(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 k(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 m)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;if(!i&&await(async()=>{try{if("undefined"==typeof navigator)return!1;if(!navigator.storage?.estimate)return!1;const{quota:e=0,usage:t=0}=await navigator.storage.estimate();return!!e&&t>=e}catch{return!1}})()&&await s.purge(),T!=w)return!1;k(c.downloading),x(0,d.download),i&&x(1,d.load);const u=new AbortController;g=u;try{const i=await s.startLLM({model:e,limits:t,logging:l,signal:u.signal,assets:n,onProgress:(e,t)=>{T==w&&x(e,t)}});return T!=w||u.signal.aborted?(await i.unload(),!1):(a=i,o=e,r=s,p=s.backend,k(c.ready),x(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),(e=>["quota","no space","disk full","ns_error_dom_quota_reached"].some(t=>`${e?.name||""} ${e?.message||`${e}`}`.toLowerCase().includes(t)))(e)&&await s.purge(),k(c.error)),!1}finally{g==u&&(g=void 0)}})().finally(()=>{s==C&&(s=void 0,i=void 0,h=[])});return s=C,i=e.id,await _(f,u)},chat:async e=>{if(!M()||!a)return;for(;u;)await u.catch(()=>{});if(!M()||!a)return;const t=a.chat(e);u=t;try{return await t}finally{u==t&&(u=void 0)}},unload:async()=>{const e=a;v(),a=void 0,o=void 0,r=void 0,k(c.idle),await(e?.unload())},ready:M,loading:()=>!M()&&f==c.downloading,status:()=>f,backend:()=>p,model:()=>o}})(),ne=(e,t,n)=>{const a=e.status(),o=u(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&&p(o),ready:e.ready(),loading:e.loading(),requesting:n.requesting,phase:n.phase,percent:n.percent,...n.error?{error:n.error}:{}}},ae=(e=>{let t,n=0;const a=t=>t.input.onStatus?.(ne(e,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=u(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 a(t);if(!g)return void a(t);if(l&&!c&&!p(i))return t.error="Model does not accept images",void a(t);const m=v(o.limits),f=await e.load({model:i,limits:m,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(!f)return t.error||(t.error="Model not ready"),void a(t);const h=p(i),w=l&&!h?o.messages.map(({images:e,...t})=>t):o.messages;if(l&&!h&&!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="",x="";try{y=await e.chat({messages:w,limits:m,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||(x+=e,o.onResponse?.(x))},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)}})(te),oe=(re=te,async e=>{const t=u(e.model),n=t=>e.onStatus?.(ne(re,e.model,t));if(!t)return n({requesting:!1,phase:d.download,percent:0,error:"Unknown model"}),!1;let a,o=0,r=d.download;const s=re.ready()&&re.model()?.id==t.id;return await re.load({model:t,limits:v(e.limits),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 re;export{g as ChatModelId,i as ChatStatus,l as LlmBackend,d as LlmPhase,s as LlmRole,c as LlmStatus,ae as createChat,oe as preloadModel};
|
package/dist/types.d.ts
CHANGED
|
@@ -139,8 +139,17 @@ interface RuntimeLoadParams {
|
|
|
139
139
|
assets?: Partial<WllamaAssets>;
|
|
140
140
|
/** Whether engine logs reach the console */
|
|
141
141
|
logging?: boolean;
|
|
142
|
+
/** Stop waiting for this load, the download continues while another caller waits */
|
|
143
|
+
signal?: AbortSignal;
|
|
142
144
|
onProgress: (progress: number, phase: LlmPhase) => void;
|
|
143
145
|
}
|
|
146
|
+
/** One caller waiting on an in-flight model load */
|
|
147
|
+
interface RuntimeLoadWaiter {
|
|
148
|
+
/** Report load progress to this caller */
|
|
149
|
+
report: RuntimeLoadParams[`onProgress`];
|
|
150
|
+
/** Resolve this caller, early when it leaves the load */
|
|
151
|
+
settle: (loaded: boolean) => void;
|
|
152
|
+
}
|
|
144
153
|
/** One generation request */
|
|
145
154
|
interface RuntimeChatParams {
|
|
146
155
|
messages: ChatMessage[];
|
|
@@ -154,10 +163,10 @@ interface RuntimeChatParams {
|
|
|
154
163
|
}
|
|
155
164
|
/** On-device runtime shared by every request */
|
|
156
165
|
interface Runtime {
|
|
166
|
+
/** Load a model, or join a load of the same model already in flight */
|
|
157
167
|
load: (params: RuntimeLoadParams) => Promise<boolean>;
|
|
158
168
|
chat: (params: RuntimeChatParams) => Promise<string | undefined>;
|
|
159
169
|
unload: () => Promise<void>;
|
|
160
|
-
cancelLoad: () => void;
|
|
161
170
|
ready: () => boolean;
|
|
162
171
|
loading: () => boolean;
|
|
163
172
|
status: () => LlmStatus;
|
|
@@ -316,4 +325,4 @@ interface EngineRunParams {
|
|
|
316
325
|
/** Whether the caller stopped the generation */
|
|
317
326
|
stopped: () => boolean;
|
|
318
327
|
}
|
|
319
|
-
export type { ChatMessage, ChatModel, LlmLimits, ChatStatusSnapshot, ChatInput, ChatPreloadInput, ChatState, CancelChat, WllamaAssets, LitertAssets, GpuNavigator, ActiveCall, RuntimeDeps, RuntimeLoadParams, RuntimeChatParams, Runtime, EngineChatParams, StartLlmParams, LlmRuntime, LlmEngine, EngineMessage, EngineContentPart, WllamaContentPart, WllamaMessage, LitertContentPart, LitertMessage, LitertEngine, LitertModule, LitertChatParams, StreamLitertParams, EngineDelta, EngineChunk, StreamReplyParams, EngineRunParams, };
|
|
328
|
+
export type { ChatMessage, ChatModel, LlmLimits, ChatStatusSnapshot, ChatInput, ChatPreloadInput, ChatState, CancelChat, WllamaAssets, LitertAssets, GpuNavigator, ActiveCall, RuntimeDeps, RuntimeLoadParams, RuntimeLoadWaiter, RuntimeChatParams, Runtime, EngineChatParams, StartLlmParams, LlmRuntime, LlmEngine, EngineMessage, EngineContentPart, WllamaContentPart, WllamaMessage, LitertContentPart, LitertMessage, LitertEngine, LitertModule, LitertChatParams, StreamLitertParams, EngineDelta, EngineChunk, StreamReplyParams, EngineRunParams, };
|
package/package.json
CHANGED