mihailik commited on
Commit
3ed38da
·
1 Parent(s): cbf8fa6

Caching slash-loaded models to use.

Browse files
package.json CHANGED
@@ -1,6 +1,6 @@
1
  {
2
  "name": "localm",
3
- "version": "1.1.19",
4
  "description": "Chat application",
5
  "scripts": {
6
  "build": "esbuild src/index.js --target=es6 --bundle --sourcemap --outfile=./index.js --format=iife --external:fs --external:path --external:child_process --external:ws --external:katex/dist/katex.min.css",
 
1
  {
2
  "name": "localm",
3
+ "version": "1.1.20",
4
  "description": "Chat application",
5
  "scripts": {
6
  "build": "esbuild src/index.js --target=es6 --bundle --sourcemap --outfile=./index.js --format=iife --external:fs --external:path --external:child_process --external:ws --external:katex/dist/katex.min.css",
src/app/model-list.js CHANGED
@@ -92,15 +92,16 @@ export async function fetchBrowserModels() {
92
  hasTokenizer: !!hasTokenizer,
93
  missingFiles: !!missingFiles,
94
  missingReason: missingReason || '',
95
- downloads: m.downloads || 0
 
96
  });
97
  } catch (e) {
98
  return null;
99
  }
100
  }).filter(m => m !== null);
101
 
102
- // Keep only models that have both ONNX and tokenizer files
103
- const withFiles = processed.filter(p => p && p.hasOnnx && p.hasTokenizer);
104
 
105
  // Sort by downloads desc
106
  withFiles.sort((a, b) => ((b && b.downloads) || 0) - ((a && a.downloads) || 0));
@@ -343,6 +344,31 @@ function detectRequiredFiles(model) {
343
  return { hasOnnx, hasTokenizer, missingFiles: missing, missingReason: reason };
344
  }
345
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
346
  /**
347
  * Get fallback models if API fetch fails
348
  * @returns {ModelInfo[]}
 
92
  hasTokenizer: !!hasTokenizer,
93
  missingFiles: !!missingFiles,
94
  missingReason: missingReason || '',
95
+ downloads: m.downloads || 0,
96
+ tags: Array.isArray(m.tags) ? m.tags.slice() : []
97
  });
98
  } catch (e) {
99
  return null;
100
  }
101
  }).filter(m => m !== null);
102
 
103
+ // Keep only models that have both ONNX and tokenizer files AND support chat
104
+ const withFiles = processed.filter(p => p && p.hasOnnx && p.hasTokenizer && isModelChatCapable(p));
105
 
106
  // Sort by downloads desc
107
  withFiles.sort((a, b) => ((b && b.downloads) || 0) - ((a && a.downloads) || 0));
 
344
  return { hasOnnx, hasTokenizer, missingFiles: missing, missingReason: reason };
345
  }
346
 
347
+ /**
348
+ * Determine if a model supports chat-style inputs/outputs.
349
+ * Uses pipeline_tag, tags, and name heuristics as fallback.
350
+ * @param {any} model
351
+ */
352
+ function isModelChatCapable(model) {
353
+ if (!model) return false;
354
+ const allowedPipelines = new Set(['text-generation', 'conversational', 'text2text-generation', 'chat']);
355
+ if (model.pipeline_tag && allowedPipelines.has(model.pipeline_tag)) return true;
356
+ // tags array may contain 'conversational' or 'chat'
357
+ if (Array.isArray(model.tags)) {
358
+ for (const t of model.tags) {
359
+ if (typeof t === 'string' && allowedPipelines.has(t)) return true;
360
+ }
361
+ }
362
+ // fallback heuristics in id/name: look for chat, conversational, dialog, instruct
363
+ const id = (model.id || '').toLowerCase();
364
+ const name = (model.name || '').toLowerCase();
365
+ const heuristics = ['chat', 'conversational', 'dialog', 'instruct', 'instruction'];
366
+ for (const h of heuristics) {
367
+ if (id.includes(h) || name.includes(h)) return true;
368
+ }
369
+ return false;
370
+ }
371
+
372
  /**
373
  * Get fallback models if API fetch fails
374
  * @returns {ModelInfo[]}
src/worker/boot-worker.js CHANGED
@@ -4,6 +4,7 @@ import { ModelCache } from './model-cache';
4
 
5
  export function bootWorker() {
6
  const modelCache = new ModelCache();
 
7
  // Report starting
8
  try {
9
  self.postMessage({ type: 'status', status: 'initializing' });
@@ -29,6 +30,7 @@ export function bootWorker() {
29
  const { modelName = modelCache.knownModels[0] } = data;
30
  try {
31
  const pipe = await modelCache.getModel({ modelName });
 
32
  self.postMessage({ id, type: 'response', result: { model: modelName, status: 'loaded' } });
33
  } catch (err) {
34
  self.postMessage({ id, type: 'error', error: String(err) });
@@ -43,7 +45,7 @@ export function bootWorker() {
43
  }
44
  }
45
 
46
- async function handleRunPrompt({ prompt, modelName = modelCache.knownModels[0], id, options }) {
47
  try {
48
  const pipe = await modelCache.getModel({ modelName });
49
  // run the pipeline
 
4
 
5
  export function bootWorker() {
6
  const modelCache = new ModelCache();
7
+ let selectedModel = modelCache.knownModels[0];
8
  // Report starting
9
  try {
10
  self.postMessage({ type: 'status', status: 'initializing' });
 
30
  const { modelName = modelCache.knownModels[0] } = data;
31
  try {
32
  const pipe = await modelCache.getModel({ modelName });
33
+ selectedModel = modelName;
34
  self.postMessage({ id, type: 'response', result: { model: modelName, status: 'loaded' } });
35
  } catch (err) {
36
  self.postMessage({ id, type: 'error', error: String(err) });
 
45
  }
46
  }
47
 
48
+ async function handleRunPrompt({ prompt, modelName = selectedModel, id, options }) {
49
  try {
50
  const pipe = await modelCache.getModel({ modelName });
51
  // run the pipeline