Spaces:
Running
Running
Download core/localmodel.mjs from fivetech/Core-Agentic-ERP: direct link, hf CLI and curl.
- Browser
- Download file 3.19 kB
-
https://huggingface.co/spaces/fivetech/Core-Agentic-ERP/resolve/main/core/localmodel.mjs
- Command line
-
hf download hf://spaces/fivetech/Core-Agentic-ERP/core/localmodel.mjs
-
curl -L -o localmodel.mjs https://huggingface.co/spaces/fivetech/Core-Agentic-ERP/resolve/main/core/localmodel.mjs
3.19 kB
| /* Modelo local MiMo en el navegador (wllama 3.5.1, WASM/CPU). | |
| El chat de este Space no depende de ningún servicio externo de modelos: | |
| el GGUF se descarga desde Hugging Face (aprox. 5,8 GB la primera vez) y se | |
| reutiliza desde la caché del navegador mientras esta se conserve. */ | |
| const WLLAMA_ESM = 'https://cdn.jsdelivr.net/npm/@wllama/wllama@3.5.1/esm/index.js'; | |
| const WLLAMA_WASM = 'https://cdn.jsdelivr.net/npm/@wllama/wllama@3.5.1/esm/wasm/wllama.wasm'; | |
| const MODEL_URL = 'https://huggingface.co/bartowski/MiMo-V2.6-Distill-Qwen-9B-GGUF/resolve/main/MiMo-V2.6-Distill-Qwen-9B-Q4_K_M.gguf'; | |
| export const APLocalModel = { | |
| state: 'idle', // idle | downloading | ready | error | |
| error: '', | |
| wllama: null, | |
| label: 'MiMo-V2.6-Distill-Qwen-9B (local en este navegador)', | |
| async cleanupCache() { | |
| // Solo la caché del propio modelo/wllama de ESTE origen; nada más. | |
| try { | |
| for (const name of await caches.keys()) { | |
| const c = await caches.open(name); | |
| for (const req of await c.keys()) { | |
| if (/wllama|MiMo-V2\.6/i.test(req.url)) await c.delete(req); | |
| } | |
| } | |
| } catch (_) {} | |
| }, | |
| async load(onProgress) { | |
| if (this.state === 'ready' || this.state === 'downloading') return; | |
| this.state = 'downloading'; this.error = ''; | |
| try { | |
| const { Wllama } = await import(WLLAMA_ESM); | |
| if (this.wllama) { try { await this.wllama.exit(); } catch (_) {} } | |
| this.wllama = new Wllama({ default: WLLAMA_WASM }, { parallelDownloads: 5 }); | |
| const opts = { | |
| n_gpu_layers: 0, // WASM/CPU: sin aceleración garantizada | |
| n_ctx: 4096, n_batch: 512, | |
| n_threads: Math.max(1, Math.min(8, navigator.hardwareConcurrency || 4)), | |
| progressCallback: ({ loaded, total }) => { if (onProgress && total > 0) onProgress(loaded, total); }, | |
| }; | |
| try { | |
| await this.wllama.loadModelFromUrl(MODEL_URL, opts); | |
| } catch (first) { | |
| // Caché incompleta o corrupta: limpiar y reintentar una vez. | |
| await this.cleanupCache(); | |
| await this.wllama.loadModelFromUrl(MODEL_URL, opts); | |
| } | |
| this.state = 'ready'; | |
| } catch (e) { | |
| this.state = 'error'; this.error = String(e && e.message || e).slice(0, 200); | |
| throw e; | |
| } | |
| }, | |
| stop() { this._abort = true; }, | |
| async chat(messages, { maxTokens = 640, temperature = 0.2, onToken } = {}) { | |
| if (this.state !== 'ready') throw new Error('modelo local no cargado'); | |
| this.state = 'generating'; this._abort = false; | |
| let stream = null; | |
| try { | |
| stream = await this.wllama.createChatCompletion({ messages, max_tokens: maxTokens, temperature, stream: true }); | |
| let answer = ''; | |
| for await (const chunk of stream) { | |
| if (this._abort) break; // salir del bucle cierra el iterador y frena la generación | |
| const delta = (chunk.choices && chunk.choices[0] && chunk.choices[0].delta) || {}; | |
| const tk = delta.content || ''; | |
| if (tk) { answer += tk; if (onToken) onToken(answer); } | |
| } | |
| return answer; | |
| } finally { | |
| this._abort = false; | |
| if (this.state === 'generating') this.state = 'ready'; | |
| } | |
| }, | |
| }; | |
| globalThis.APLocalModel = APLocalModel; | |