PageSourceSearch

https://jayreddinjr-jr-odysseus-hf.hf.space/static/js/compare/models.js

js jayreddinjr-jr-odysseus-hf.hf.space collected 2026-10-04 17:00:15 UTC 3,997 bytes, 104 lines download raw bytes

1// compare/models.js — model classification, fetching, display names, persistence
2import Storage from '../storage.js';
3import state from './state.js';
4import uiModule from '../ui.js';
5import { sortModelObjects } from '../modelSort.js';
6
7var escapeHtml = uiModule.esc;
8
9// ── Model classification constants ──
10const NON_CHAT_PREFIXES = ['tts-', 'whisper-', 'text-embedding-', 'text-moderation-', 'moderation-', 'embedding'];
11const NON_CHAT_SUFFIXES = ['deep-research', '-online'];
12const IMAGE_PREFIXES = ['dall-e-3', 'gpt-image', 'chatgpt-image'];
13const DEPRECATED_IMAGE = ['dall-e-2'];
14
15function classifyModel(id) {
16  const lower = id.toLowerCase();
17  if (DEPRECATED_IMAGE.some(p => lower.startsWith(p))) return 'other';
18  if (IMAGE_PREFIXES.some(p => lower.startsWith(p))) return 'image';
19  if (NON_CHAT_PREFIXES.some(p => lower.startsWith(p))) return 'other';
20  if (NON_CHAT_SUFFIXES.some(p => lower.endsWith(p) || lower.includes(p))) return 'other';
21  return 'chat';
22}
23
24/** Build display names for selected models, adding endpoint name when the same model appears from multiple providers. */
25function _modelDisplayNames(models) {
26  const nameCount = {};
27  for (const m of models) {
28    const short = m.name || m.model.split('/').pop();
29    nameCount[short] = (nameCount[short] || 0) + 1;
30  }
31  return models.map(m => {
32    const short = m.name || m.model.split('/').pop();
33    if (nameCount[short] > 1 && m.endpointName) return short + ' (' + escapeHtml(m.endpointName) + ')';
34    return short;
35  });
36}
37
38/** Save selected models and synth models to localStorage, keyed by compare mode. */
39function _persistSelections() {
40  if (state._selectedModels.length > 0) {
41    Storage.setJSON('odysseus-compare-selections-' + (state._compareMode || 'chat'), state._selectedModels);
42  }
43  if ((state._compareMode === 'search' || state._compareMode === 'research') && state._searchSynthModels) {
44    Storage.setJSON('odysseus-compare-synth-' + state._compareMode, state._searchSynthModels);
45  }
46}
47
48// ── Model fetching with cache ──
49const MODELS_CACHE_TTL = 30000; // 30 seconds
50
51/** Fetch available models from API. */
52async function fetchModels() {
53  const now = Date.now();
54  if (state._fetchModelsCache && (now - state._fetchModelsCacheTime) < MODELS_CACHE_TTL) {
55    return state._fetchModelsCache;
56  }
57  const res = await fetch(`${state.API_BASE}/api/models`);
58  const data = await res.json();
59  const models = [];
60  if (data.items && data.items.length > 0) {
61    data.items.forEach(item => {
62      const displayNames = item.models_display || item.models || [];
63      const extraDisplay = item.models_extra_display || item.models_extra || [];
64      // Curated list (item.models) takes priority; non-curated extras come
65      // after so newer/uncatalogued models (e.g. deepseek-v4-pro) still show.
66      (item.models || []).forEach((mid, i) => {
67        models.push({
68          id: mid,
69          url: item.url,
70          name: (displayNames[i] || mid).split('/').pop(),
71          endpointId: item.endpoint_id || null,
72          endpointName: item.endpoint_name || '',
73          type: classifyModel(mid),
74        });
75      });
76      (item.models_extra || []).forEach((mid, i) => {
77        models.push({
78          id: mid,
79          url: item.url,
80          name: (extraDisplay[i] || mid).split('/').pop(),
81          endpointId: item.endpoint_id || null,
82          endpointName: item.endpoint_name || '',
83          type: classifyModel(mid),
84        });
85      });
86    });
87  }
88  state._fetchModelsCache = sortModelObjects(models);
89  state._fetchModelsCacheTime = now;
90  return state._fetchModelsCache;
91}
92
93// ── Shuffle pool persistence ──
94const POOL_STORAGE_KEY = 'odysseus-shuffle-pool-excluded';
95
96function getExcludedModels() {
97  return Storage.getJSON(POOL_STORAGE_KEY, []);
98}
99
100function setExcludedModels(arr) {
101  Storage.setJSON(POOL_STORAGE_KEY, arr);
102}
103
104export { classifyModel, _modelDisplayNames, fetchModels, _persistSelections, getExcludedModels, setExcludedModels };

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.