zerochat 7.3.0__py3-none-any.whl
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.
- zerochat-7.3.0.dist-info/METADATA +70 -0
- zerochat-7.3.0.dist-info/RECORD +130 -0
- zerochat-7.3.0.dist-info/WHEEL +5 -0
- zerochat-7.3.0.dist-info/entry_points.txt +2 -0
- zerochat-7.3.0.dist-info/licenses/LICENSE +34 -0
- zerochat-7.3.0.dist-info/top_level.txt +2 -0
- zerochat.py +2004 -0
- zerochat_runtime/__init__.py +1 -0
- zerochat_runtime/assets/css/base.css +199 -0
- zerochat_runtime/assets/css/components/composer.css +1258 -0
- zerochat_runtime/assets/css/components/debug.css +777 -0
- zerochat_runtime/assets/css/components/header.css +97 -0
- zerochat_runtime/assets/css/components/icons.css +39 -0
- zerochat_runtime/assets/css/components/markdown.css +685 -0
- zerochat_runtime/assets/css/components/messages.css +473 -0
- zerochat_runtime/assets/css/components/modals.css +1319 -0
- zerochat_runtime/assets/css/components/sidebar.css +622 -0
- zerochat_runtime/assets/css/components/tools.css +1223 -0
- zerochat_runtime/assets/css/layout.css +171 -0
- zerochat_runtime/assets/css/print.css +120 -0
- zerochat_runtime/assets/css/styles.css +18 -0
- zerochat_runtime/assets/css/theme-overrides.css +555 -0
- zerochat_runtime/assets/css/tokens.css +165 -0
- zerochat_runtime/assets/help/architecture.html +247 -0
- zerochat_runtime/assets/help/composer.html +134 -0
- zerochat_runtime/assets/help/conversations.html +105 -0
- zerochat_runtime/assets/help/debug.html +108 -0
- zerochat_runtime/assets/help/en/architecture.html +247 -0
- zerochat_runtime/assets/help/en/composer.html +134 -0
- zerochat_runtime/assets/help/en/conversations.html +104 -0
- zerochat_runtime/assets/help/en/debug.html +105 -0
- zerochat_runtime/assets/help/en/gemini-free.html +39 -0
- zerochat_runtime/assets/help/en/index.html +191 -0
- zerochat_runtime/assets/help/en/learning.html +71 -0
- zerochat_runtime/assets/help/en/mcp.html +143 -0
- zerochat_runtime/assets/help/en/openrouter-free.html +39 -0
- zerochat_runtime/assets/help/en/profiles.html +173 -0
- zerochat_runtime/assets/help/en/rag.html +130 -0
- zerochat_runtime/assets/help/en/reasoning-telemetry.html +108 -0
- zerochat_runtime/assets/help/en/tools-agent.html +133 -0
- zerochat_runtime/assets/help/en/webllm.html +355 -0
- zerochat_runtime/assets/help/gemini-free.html +39 -0
- zerochat_runtime/assets/help/help.css +604 -0
- zerochat_runtime/assets/help/help.js +51 -0
- zerochat_runtime/assets/help/index.html +194 -0
- zerochat_runtime/assets/help/learning.html +71 -0
- zerochat_runtime/assets/help/mcp.html +143 -0
- zerochat_runtime/assets/help/openrouter-free.html +39 -0
- zerochat_runtime/assets/help/profiles.html +174 -0
- zerochat_runtime/assets/help/rag.html +130 -0
- zerochat_runtime/assets/help/reasoning-telemetry.html +111 -0
- zerochat_runtime/assets/help/tools-agent.html +133 -0
- zerochat_runtime/assets/help/webllm.html +359 -0
- zerochat_runtime/assets/js/agent-core.js +1553 -0
- zerochat_runtime/assets/js/api.js +844 -0
- zerochat_runtime/assets/js/app.js +2558 -0
- zerochat_runtime/assets/js/attachments.js +220 -0
- zerochat_runtime/assets/js/charts.js +338 -0
- zerochat_runtime/assets/js/chat-engine.js +566 -0
- zerochat_runtime/assets/js/config-store.js +207 -0
- zerochat_runtime/assets/js/context-manager.js +584 -0
- zerochat_runtime/assets/js/conversation-service.js +484 -0
- zerochat_runtime/assets/js/cookies.js +688 -0
- zerochat_runtime/assets/js/data-reset-service.js +136 -0
- zerochat_runtime/assets/js/debug.js +508 -0
- zerochat_runtime/assets/js/defaults.js +16 -0
- zerochat_runtime/assets/js/export.js +179 -0
- zerochat_runtime/assets/js/file-parser.js +2088 -0
- zerochat_runtime/assets/js/generation-controller.js +417 -0
- zerochat_runtime/assets/js/i18n.js +1483 -0
- zerochat_runtime/assets/js/icons.js +156 -0
- zerochat_runtime/assets/js/ingestionEngine.js +436 -0
- zerochat_runtime/assets/js/markdown.js +588 -0
- zerochat_runtime/assets/js/mcp.js +1271 -0
- zerochat_runtime/assets/js/message-turns.js +62 -0
- zerochat_runtime/assets/js/profile-backup.js +118 -0
- zerochat_runtime/assets/js/profile-export-bundle.js +481 -0
- zerochat_runtime/assets/js/profile-repository.js +213 -0
- zerochat_runtime/assets/js/providers-webllm.js +486 -0
- zerochat_runtime/assets/js/providers.js +1560 -0
- zerochat_runtime/assets/js/rag-index.js +295 -0
- zerochat_runtime/assets/js/rag-service.js +530 -0
- zerochat_runtime/assets/js/rag-ui.js +997 -0
- zerochat_runtime/assets/js/ragStorage.js +676 -0
- zerochat_runtime/assets/js/sandbox.js +449 -0
- zerochat_runtime/assets/js/state.js +704 -0
- zerochat_runtime/assets/js/storage-db.js +144 -0
- zerochat_runtime/assets/js/tool-cards.js +330 -0
- zerochat_runtime/assets/js/tool-security.js +653 -0
- zerochat_runtime/assets/js/tools/README.md +50 -0
- zerochat_runtime/assets/js/tools/builtin/agent-checkpoint.tool.js +212 -0
- zerochat_runtime/assets/js/tools/builtin/download-pdf.tool.js +111 -0
- zerochat_runtime/assets/js/tools/builtin/execute-javascript.tool.js +219 -0
- zerochat_runtime/assets/js/tools/builtin/fetch-web-page.tool.js +112 -0
- zerochat_runtime/assets/js/tools/builtin/list-documents.tool.js +117 -0
- zerochat_runtime/assets/js/tools/builtin/read-knowledge-chunk.tool.js +129 -0
- zerochat_runtime/assets/js/tools/builtin/read-knowledge-image.tool.js +95 -0
- zerochat_runtime/assets/js/tools/builtin/render-chart.tool.js +90 -0
- zerochat_runtime/assets/js/tools/builtin/search-knowledge-base.tool.js +124 -0
- zerochat_runtime/assets/js/tools/builtin/search-web.tool.js +125 -0
- zerochat_runtime/assets/js/tools/tool-manifest.js +53 -0
- zerochat_runtime/assets/js/tools/tool-runtime.js +77 -0
- zerochat_runtime/assets/js/ui-composer.js +319 -0
- zerochat_runtime/assets/js/ui-conversation.js +655 -0
- zerochat_runtime/assets/js/ui-dialogs.js +132 -0
- zerochat_runtime/assets/js/ui-generation-status.js +119 -0
- zerochat_runtime/assets/js/ui-inspector.js +804 -0
- zerochat_runtime/assets/js/ui-mcp.js +604 -0
- zerochat_runtime/assets/js/ui-profiles.js +824 -0
- zerochat_runtime/assets/js/ui-reasoning.js +146 -0
- zerochat_runtime/assets/js/ui-settings.js +825 -0
- zerochat_runtime/assets/js/ui-shell.js +215 -0
- zerochat_runtime/assets/js/ui-sidebar.js +429 -0
- zerochat_runtime/assets/js/ui-telemetry.js +404 -0
- zerochat_runtime/assets/js/ui-transfer.js +262 -0
- zerochat_runtime/assets/js/utils.js +216 -0
- zerochat_runtime/assets/js/vendor/orama.browser.js +11 -0
- zerochat_runtime/assets/js/web-browser.js +569 -0
- zerochat_runtime/assets/js/web-search.js +519 -0
- zerochat_runtime/assets/manifest.webmanifest +20 -0
- zerochat_runtime/assets/services/dummy_mcp/dummy_mcp_server.py +47 -0
- zerochat_runtime/assets/services/dummy_mcp/service.json +22 -0
- zerochat_runtime/assets/services/lsp/installer.json +9 -0
- zerochat_runtime/assets/services/lsp/service.json +22 -0
- zerochat_runtime/assets/services/memory/installer.json +9 -0
- zerochat_runtime/assets/services/memory/service.json +24 -0
- zerochat_runtime/assets/services/playwright/installer.json +10 -0
- zerochat_runtime/assets/services/playwright/service.json +39 -0
- zerochat_runtime/assets/sw.js +164 -0
- zerochat_runtime/assets/zerochat.html +671 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Versioned repository for editable connection profiles.
|
|
3
|
+
* Runtime consumers must use ChatConfig instead of reading this repository.
|
|
4
|
+
*/
|
|
5
|
+
(function (root, factory) {
|
|
6
|
+
if (typeof exports === 'object' && typeof module !== 'undefined') {
|
|
7
|
+
module.exports = factory(require('./cookies.js'), require('./profile-backup.js'));
|
|
8
|
+
} else {
|
|
9
|
+
root.ChatProfileRepository = factory(root.ChatStorage, root.ChatProfileBackup);
|
|
10
|
+
}
|
|
11
|
+
}(typeof self !== 'undefined' ? self : this, function (Storage, Backup) {
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
const STORAGE_KEY = 'profiles_v1';
|
|
15
|
+
const SCHEMA_VERSION = 1;
|
|
16
|
+
const READONLY_PROFILE_ID = 'profile:mirror';
|
|
17
|
+
const PROFILE_FIELDS = Object.freeze([
|
|
18
|
+
'apiUrl', 'apiType', 'model', 'systemPrompt', 'temperature',
|
|
19
|
+
'reasoningEffort', 'reasoningTransport', 'maxAgentTurns', 'modelReasoningConfig', 'enabledTools',
|
|
20
|
+
'enableRawLogs', 'enableContextCache', 'contextLimitOverride', 'webllmConfig', 'apiKeyLocked'
|
|
21
|
+
]);
|
|
22
|
+
const STORAGE_PROFILE_FIELDS = Object.freeze([...PROFILE_FIELDS, 'apiKey']);
|
|
23
|
+
|
|
24
|
+
function clone(value) {
|
|
25
|
+
return value === undefined ? undefined : JSON.parse(JSON.stringify(value));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function createId(name) {
|
|
29
|
+
return `profile:${encodeURIComponent(String(name || '').trim())}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const NEW_PROFILE_SETTINGS = Object.freeze({
|
|
33
|
+
apiUrl: 'http://localhost:1234/v1', apiType: 'openai', model: '', systemPrompt: '',
|
|
34
|
+
temperature: '0.7', reasoningEffort: 'medium', reasoningTransport: 'auto', maxAgentTurns: 15, modelReasoningConfig: null,
|
|
35
|
+
enabledTools: { execute_javascript: true, search_web: true, fetch_web_page: true, download_pdf: true, render_chart: true },
|
|
36
|
+
enableRawLogs: false, enableContextCache: true, contextLimitOverride: null, apiKeyLocked: false,
|
|
37
|
+
webllmConfig: {
|
|
38
|
+
context_window_size: 'default',
|
|
39
|
+
prefill_chunk_size: 'default'
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const MIRROR_PROFILE = Object.freeze({
|
|
44
|
+
id: READONLY_PROFILE_ID,
|
|
45
|
+
name: 'Espejo',
|
|
46
|
+
description: 'Muestra la petición OpenAI sin enviarla.',
|
|
47
|
+
settings: {
|
|
48
|
+
...NEW_PROFILE_SETTINGS, apiUrl: 'mirror://local', apiType: 'mirror', model: 'mirror'
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
function normalizeSettings(source = {}) {
|
|
53
|
+
if (!source || typeof source !== 'object' || Array.isArray(source)) throw new Error('Los datos del perfil no son válidos.');
|
|
54
|
+
const settings = {};
|
|
55
|
+
STORAGE_PROFILE_FIELDS.forEach(key => {
|
|
56
|
+
if (key === 'apiKey') {
|
|
57
|
+
if (source[key] === '' || source[key] === null || source[key] === undefined) {
|
|
58
|
+
settings.apiKey = null;
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (!Backup?.validateApiKeySecret) throw new Error('La validación de API keys no está disponible.');
|
|
62
|
+
Backup.validateApiKeySecret(source[key]);
|
|
63
|
+
}
|
|
64
|
+
if (source[key] !== undefined) settings[key] = clone(source[key]);
|
|
65
|
+
});
|
|
66
|
+
return settings;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function normalizeRecord(record = {}) {
|
|
70
|
+
const name = String(record.name || '').trim();
|
|
71
|
+
if (!name) throw new Error('El nombre del perfil no puede estar vacío.');
|
|
72
|
+
return {
|
|
73
|
+
id: String(record.id || createId(name)),
|
|
74
|
+
name,
|
|
75
|
+
description: String(record.description || '').trim(),
|
|
76
|
+
schemaVersion: SCHEMA_VERSION,
|
|
77
|
+
version: Number.isInteger(record.version) && record.version > 0 ? record.version : 1,
|
|
78
|
+
updatedAt: Number(record.updatedAt) || Date.now(),
|
|
79
|
+
settings: normalizeSettings(record.settings || record)
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function createRepository(storage = Storage) {
|
|
84
|
+
function readDocument() {
|
|
85
|
+
const raw = storage?.getStorageItem ? storage.getStorageItem(STORAGE_KEY) : null;
|
|
86
|
+
if (!raw) return null;
|
|
87
|
+
let doc;
|
|
88
|
+
try {
|
|
89
|
+
doc = JSON.parse(raw);
|
|
90
|
+
} catch (_) {
|
|
91
|
+
throw new Error('El almacenamiento de perfiles no es JSON válido.');
|
|
92
|
+
}
|
|
93
|
+
if (!doc || doc.schemaVersion !== SCHEMA_VERSION || !Array.isArray(doc.profiles)) {
|
|
94
|
+
throw new Error('El almacenamiento de perfiles no tiene un formato válido.');
|
|
95
|
+
}
|
|
96
|
+
return { schemaVersion: SCHEMA_VERSION, profiles: doc.profiles.map(normalizeRecord) };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function writeDocument(document) {
|
|
100
|
+
if (!storage?.setStorageItem) throw new Error('El almacenamiento de perfiles no está disponible.');
|
|
101
|
+
storage.setStorageItem(STORAGE_KEY, JSON.stringify(document));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function initialize() {
|
|
105
|
+
const existing = readDocument();
|
|
106
|
+
if (existing) {
|
|
107
|
+
if (!existing.profiles.some(profile => profile.id === READONLY_PROFILE_ID)) {
|
|
108
|
+
existing.profiles.unshift(normalizeRecord(MIRROR_PROFILE));
|
|
109
|
+
writeDocument(existing);
|
|
110
|
+
}
|
|
111
|
+
return clone(existing);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const document = { schemaVersion: SCHEMA_VERSION, profiles: [normalizeRecord(MIRROR_PROFILE)] };
|
|
115
|
+
writeDocument(document);
|
|
116
|
+
return clone(document);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function list() {
|
|
120
|
+
return initialize().profiles;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function get(id) {
|
|
124
|
+
const cleanId = String(id || '');
|
|
125
|
+
return list().find(profile => profile.id === cleanId) || null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function load(id) {
|
|
129
|
+
const profile = get(id);
|
|
130
|
+
if (!profile) return null;
|
|
131
|
+
const secret = profile.settings.apiKey;
|
|
132
|
+
if (secret) {
|
|
133
|
+
if (!Backup?.decryptApiKey) throw new Error('El descifrado de API keys no está disponible.');
|
|
134
|
+
profile.settings.apiKey = await Backup.decryptApiKey(secret);
|
|
135
|
+
} else {
|
|
136
|
+
profile.settings.apiKey = '';
|
|
137
|
+
}
|
|
138
|
+
return profile;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function findByName(name) {
|
|
142
|
+
const cleanName = String(name || '').trim();
|
|
143
|
+
return list().find(profile => profile.name === cleanName) || null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function save(record) {
|
|
147
|
+
const current = initialize();
|
|
148
|
+
const normalized = normalizeRecord(record);
|
|
149
|
+
if (normalized.id === READONLY_PROFILE_ID || normalized.name === MIRROR_PROFILE.name) {
|
|
150
|
+
throw new Error('El perfil Espejo no se puede modificar.');
|
|
151
|
+
}
|
|
152
|
+
const index = current.profiles.findIndex(profile => profile.id === normalized.id);
|
|
153
|
+
if (index >= 0) {
|
|
154
|
+
if (current.profiles[index].settings.apiKeyLocked === true) throw new Error('Los cambios de este perfil están bloqueados.');
|
|
155
|
+
normalized.version = current.profiles[index].version + 1;
|
|
156
|
+
}
|
|
157
|
+
normalized.updatedAt = Date.now();
|
|
158
|
+
if (index >= 0) current.profiles[index] = normalized;
|
|
159
|
+
else current.profiles.push(normalized);
|
|
160
|
+
writeDocument(current);
|
|
161
|
+
return clone(normalized);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function saveEditable(record) {
|
|
165
|
+
if (typeof record?.settings?.apiKey !== 'string') throw new Error('La API key del perfil no es válida.');
|
|
166
|
+
if (!Backup?.encryptApiKey) throw new Error('El cifrado de API keys no está disponible.');
|
|
167
|
+
const apiKey = await Backup.encryptApiKey(record.settings.apiKey);
|
|
168
|
+
return save({ ...record, settings: { ...record.settings, apiKey } });
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function remove(id) {
|
|
172
|
+
if (String(id || '') === READONLY_PROFILE_ID) return false;
|
|
173
|
+
const current = initialize();
|
|
174
|
+
const index = current.profiles.findIndex(profile => profile.id === String(id || ''));
|
|
175
|
+
if (index < 0) return false;
|
|
176
|
+
current.profiles.splice(index, 1);
|
|
177
|
+
writeDocument(current);
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function mergeImported(records) {
|
|
182
|
+
if (!Array.isArray(records) || records.length > 100) throw new Error('La lista de perfiles importados no es válida.');
|
|
183
|
+
const current = initialize();
|
|
184
|
+
const imported = records.map(normalizeRecord).filter(profile => profile.id !== READONLY_PROFILE_ID && profile.name !== MIRROR_PROFILE.name);
|
|
185
|
+
if (imported.length !== records.length) throw new Error('La copia no puede modificar el perfil Espejo.');
|
|
186
|
+
const ids = new Set();
|
|
187
|
+
const names = new Set();
|
|
188
|
+
imported.forEach(profile => {
|
|
189
|
+
if (ids.has(profile.id) || names.has(profile.name)) throw new Error('La copia contiene perfiles duplicados.');
|
|
190
|
+
ids.add(profile.id); names.add(profile.name);
|
|
191
|
+
});
|
|
192
|
+
const editable = current.profiles.filter(profile => profile.id !== READONLY_PROFILE_ID);
|
|
193
|
+
const byId = new Map(editable.map(profile => [profile.id, profile]));
|
|
194
|
+
const existingNames = new Map(editable.map(profile => [profile.name, profile.id]));
|
|
195
|
+
let added = 0;
|
|
196
|
+
let replaced = 0;
|
|
197
|
+
imported.forEach(profile => {
|
|
198
|
+
const nameOwner = existingNames.get(profile.name);
|
|
199
|
+
if (nameOwner && nameOwner !== profile.id) throw new Error(`Ya existe un perfil llamado "${profile.name}" con otro identificador.`);
|
|
200
|
+
if (byId.has(profile.id)) replaced += 1;
|
|
201
|
+
else added += 1;
|
|
202
|
+
byId.set(profile.id, profile);
|
|
203
|
+
});
|
|
204
|
+
writeDocument({ schemaVersion: SCHEMA_VERSION, profiles: [normalizeRecord(MIRROR_PROFILE), ...byId.values()] });
|
|
205
|
+
return { added, replaced, total: imported.length };
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return { initialize, list, get, load, findByName, save, saveEditable, remove, mergeImported };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const defaultRepository = createRepository();
|
|
212
|
+
return { STORAGE_KEY, SCHEMA_VERSION, PROFILE_FIELDS, STORAGE_PROFILE_FIELDS, READONLY_PROFILE_ID, MIRROR_PROFILE: clone(MIRROR_PROFILE), NEW_PROFILE_SETTINGS: clone(NEW_PROFILE_SETTINGS), createRepository, ...defaultRepository };
|
|
213
|
+
}));
|
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptador WebLLM: carga diferida y transporte local en el navegador.
|
|
3
|
+
* WebLLM conserva por defecto los artefactos de los modelos mediante Cache Storage.
|
|
4
|
+
*/
|
|
5
|
+
(function (root, factory) {
|
|
6
|
+
if (typeof exports === 'object' && typeof module !== 'undefined') {
|
|
7
|
+
module.exports = factory(require('./providers.js'));
|
|
8
|
+
} else {
|
|
9
|
+
root.ChatWebLLM = factory(root.ChatProviders);
|
|
10
|
+
}
|
|
11
|
+
})(typeof self !== 'undefined' ? self : this, function (Providers) {
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
const WEBLLM_URL = 'https://cdn.jsdelivr.net/npm/@mlc-ai/web-llm@0.2.85/+esm';
|
|
15
|
+
const COMPLETED_MODELS_STORAGE_KEY = 'webllm_completed_models_v1';
|
|
16
|
+
let modulePromise = null;
|
|
17
|
+
|
|
18
|
+
function supported() {
|
|
19
|
+
return typeof navigator !== 'undefined' && !!navigator.gpu && typeof caches !== 'undefined' && typeof ReadableStream !== 'undefined' && typeof AbortController !== 'undefined';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function supportError() {
|
|
23
|
+
if (typeof navigator === 'undefined' || !navigator.gpu) return 'WebGPU no está disponible en este navegador.';
|
|
24
|
+
if (typeof caches === 'undefined') return 'Cache Storage no está disponible en este navegador.';
|
|
25
|
+
return 'El navegador no admite los requisitos de WebLLM.';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function getAppConfig(webllm) {
|
|
29
|
+
const base = webllm.prebuiltAppConfig;
|
|
30
|
+
if (!base || !Array.isArray(base.model_list)) throw new Error('WebLLM did not provide a valid model catalog.');
|
|
31
|
+
return { ...base, model_list: base.model_list };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function getStorage() {
|
|
35
|
+
if (typeof globalThis !== 'undefined' && globalThis.ChatStorage) return globalThis.ChatStorage;
|
|
36
|
+
return typeof require !== 'undefined' ? require('./cookies.js') : null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function completedModels() {
|
|
40
|
+
const Storage = getStorage();
|
|
41
|
+
if (!Storage?.getStorageItem) return null;
|
|
42
|
+
try {
|
|
43
|
+
const models = JSON.parse(Storage.getStorageItem(COMPLETED_MODELS_STORAGE_KEY) || '[]');
|
|
44
|
+
return new Set(Array.isArray(models) ? models.filter(model => typeof model === 'string') : []);
|
|
45
|
+
} catch (_) {
|
|
46
|
+
return new Set();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function getCompletedModelIds() {
|
|
51
|
+
const models = completedModels();
|
|
52
|
+
return models ? Array.from(models) : [];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function isModelCompleted(modelId) {
|
|
56
|
+
const models = completedModels();
|
|
57
|
+
return models ? models.has(modelId) : false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function setModelCompleted(modelId, completed) {
|
|
61
|
+
const Storage = getStorage();
|
|
62
|
+
if (!Storage?.setStorageItem) return false;
|
|
63
|
+
const models = completedModels() || new Set();
|
|
64
|
+
if (completed) models.add(modelId);
|
|
65
|
+
else models.delete(modelId);
|
|
66
|
+
try {
|
|
67
|
+
Storage.setStorageItem(COMPLETED_MODELS_STORAGE_KEY, JSON.stringify([...models]));
|
|
68
|
+
return true;
|
|
69
|
+
} catch (_) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function getModelAvailability(webllm, modelId, appConfig) {
|
|
75
|
+
if (typeof webllm.hasModelInCache !== 'function') return 'unknown';
|
|
76
|
+
const cached = await webllm.hasModelInCache(modelId, appConfig);
|
|
77
|
+
const recorded = completedModels();
|
|
78
|
+
if (cached !== true) return recorded?.has(modelId) ? 'incomplete' : 'missing';
|
|
79
|
+
if (recorded === null) return 'unknown';
|
|
80
|
+
return recorded.has(modelId) ? 'cached' : 'incomplete';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function isModelAvailable(webllm, modelId, appConfig) {
|
|
84
|
+
return await getModelAvailability(webllm, modelId, appConfig) === 'cached';
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function loadWebLLM(loader) {
|
|
88
|
+
if (!supported()) return Promise.reject(new Error(supportError()));
|
|
89
|
+
if (!modulePromise) {
|
|
90
|
+
const importModule = loader || (async (url) => {
|
|
91
|
+
if (typeof globalThis !== 'undefined' && globalThis.webllm?.CreateWebWorkerMLCEngine) {
|
|
92
|
+
return globalThis.webllm;
|
|
93
|
+
}
|
|
94
|
+
return await import(/* webpackIgnore: true */ url);
|
|
95
|
+
});
|
|
96
|
+
modulePromise = Promise.resolve().then(() => importModule(WEBLLM_URL)).catch(error => {
|
|
97
|
+
modulePromise = null;
|
|
98
|
+
throw new Error(`No se pudo preparar WebLLM: ${error.message || error}`);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return modulePromise;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function normalizeProgress(progress) {
|
|
105
|
+
const detail = String(progress?.text || '').trim();
|
|
106
|
+
const explicit = progress?.progress === null || progress?.progress === undefined ? NaN : Number(progress.progress);
|
|
107
|
+
const match = detail.match(/(\d+(?:\.\d+)?)%\s+completed/i);
|
|
108
|
+
const percent = Number.isFinite(explicit)
|
|
109
|
+
? Math.max(0, Math.min(100, explicit <= 1 ? explicit * 100 : explicit))
|
|
110
|
+
: (match ? Math.max(0, Math.min(100, Number(match[1]))) : null);
|
|
111
|
+
let phase = 'preparing';
|
|
112
|
+
if (/shader modules/i.test(detail)) phase = 'compiling';
|
|
113
|
+
else if (/fetch params|loading model/i.test(detail)) phase = 'loading';
|
|
114
|
+
else if (/finish loading/i.test(detail)) phase = 'ready';
|
|
115
|
+
return { phase, percent, detail };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function emitProgress(onProgress, progress) {
|
|
119
|
+
onProgress?.(typeof progress === 'string' ? { phase: progress, percent: null, detail: '' } : normalizeProgress(progress));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function abortError() {
|
|
123
|
+
return typeof DOMException === 'function' ? new DOMException('Aborted', 'AbortError') : Object.assign(new Error('Aborted'), { name: 'AbortError' });
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function workerError(detail) {
|
|
127
|
+
const suffix = detail ? ` (${detail})` : '';
|
|
128
|
+
const error = new Error(`WebLLM could not start its dedicated worker${suffix}. Local inference was not started to keep the browser responsive.`);
|
|
129
|
+
error.code = 'WEBLLM_WORKER_UNAVAILABLE';
|
|
130
|
+
return error;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function extractOverrides(config) {
|
|
134
|
+
if (!config || typeof config !== 'object') return null;
|
|
135
|
+
const overrides = {};
|
|
136
|
+
const parseParam = val => {
|
|
137
|
+
if (val === undefined || val === null || val === '' || val === 'default') return null;
|
|
138
|
+
const num = Number(val);
|
|
139
|
+
return Number.isFinite(num) && num > 0 ? Math.floor(num) : null;
|
|
140
|
+
};
|
|
141
|
+
const cw = parseParam(config.context_window_size);
|
|
142
|
+
if (cw !== null) overrides.context_window_size = cw;
|
|
143
|
+
const pc = parseParam(config.prefill_chunk_size);
|
|
144
|
+
if (pc !== null) overrides.prefill_chunk_size = pc;
|
|
145
|
+
return Object.keys(overrides).length > 0 ? overrides : null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function applyModelOverrides(appConfig, modelId, overrides) {
|
|
149
|
+
if (!overrides || Object.keys(overrides).length === 0) return appConfig;
|
|
150
|
+
const cloned = JSON.parse(JSON.stringify(appConfig || {}));
|
|
151
|
+
if (Array.isArray(cloned.model_list)) {
|
|
152
|
+
const entry = cloned.model_list.find(m => m.model_id === modelId);
|
|
153
|
+
if (entry) {
|
|
154
|
+
entry.overrides = Object.assign({}, entry.overrides, overrides);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return cloned;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function getActiveWebllmConfig() {
|
|
161
|
+
try {
|
|
162
|
+
const State = typeof ChatState !== 'undefined' ? ChatState : (typeof globalThis !== 'undefined' ? globalThis.ChatState : null);
|
|
163
|
+
const cfg = State?.get?.('config') || (typeof ChatConfig !== 'undefined' ? ChatConfig.get?.() : null);
|
|
164
|
+
return cfg?.webllmConfig || null;
|
|
165
|
+
} catch (_) {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function createWorkerEngine(webllm, modelId, appConfig, onProgress, signal, chatOpts = {}) {
|
|
171
|
+
if (typeof Worker === 'undefined' || typeof Blob === 'undefined' || typeof URL === 'undefined' || typeof URL.createObjectURL !== 'function' || typeof webllm.CreateWebWorkerMLCEngine !== 'function') {
|
|
172
|
+
return Promise.reject(workerError('Dedicated Web Worker support is unavailable'));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return new Promise((resolve, reject) => {
|
|
176
|
+
let settled = false;
|
|
177
|
+
let worker = null;
|
|
178
|
+
let workerUrl = null;
|
|
179
|
+
|
|
180
|
+
const workerFailure = event => fail(event);
|
|
181
|
+
const abort = () => fail(abortError(), false);
|
|
182
|
+
const cleanup = () => {
|
|
183
|
+
if (worker) {
|
|
184
|
+
worker.removeEventListener('error', workerFailure);
|
|
185
|
+
worker.removeEventListener('messageerror', workerFailure);
|
|
186
|
+
}
|
|
187
|
+
signal?.removeEventListener('abort', abort);
|
|
188
|
+
};
|
|
189
|
+
const discard = () => {
|
|
190
|
+
if (worker) {
|
|
191
|
+
worker.terminate();
|
|
192
|
+
}
|
|
193
|
+
if (workerUrl) {
|
|
194
|
+
URL.revokeObjectURL(workerUrl);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
const fail = (event, isWorkerFailure = true) => {
|
|
198
|
+
if (settled) return;
|
|
199
|
+
settled = true;
|
|
200
|
+
cleanup();
|
|
201
|
+
discard();
|
|
202
|
+
let detail = typeof event === 'string' ? event : (event?.message || event?.error?.message || event?.messageText || '');
|
|
203
|
+
if (typeof detail === 'string') detail = detail.trim();
|
|
204
|
+
if (detail === '[object Object]' || !detail) {
|
|
205
|
+
if (event && typeof event === 'object') {
|
|
206
|
+
detail = event.error?.message || event.cause?.message || (typeof event.error === 'string' ? event.error : '') || '';
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if (detail === '[object Object]' || !detail) {
|
|
210
|
+
detail = 'WebLLM model preparation failed.';
|
|
211
|
+
}
|
|
212
|
+
const error = event?.name === 'AbortError' || (!isWorkerFailure && event instanceof Error)
|
|
213
|
+
? (event.message === '[object Object]' ? new Error(detail, { cause: event }) : event)
|
|
214
|
+
: (isWorkerFailure
|
|
215
|
+
? workerError(detail)
|
|
216
|
+
: new Error(detail, { cause: event }));
|
|
217
|
+
if (typeof console !== 'undefined' && console.error) {
|
|
218
|
+
console.error('[WebLLM] Preparation error:', error);
|
|
219
|
+
}
|
|
220
|
+
reject(error);
|
|
221
|
+
};
|
|
222
|
+
const notifyProgress = progress => {
|
|
223
|
+
emitProgress(onProgress, progress);
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
signal?.addEventListener('abort', abort, { once: true });
|
|
227
|
+
if (signal?.aborted) return abort();
|
|
228
|
+
|
|
229
|
+
const source = `import * as webllm from ${JSON.stringify(WEBLLM_URL)};\nconst handler = new webllm.WebWorkerMLCEngineHandler();\nself.onmessage = event => handler.onmessage(event);`;
|
|
230
|
+
workerUrl = URL.createObjectURL(new Blob([source], { type: 'text/javascript' }));
|
|
231
|
+
try {
|
|
232
|
+
worker = new Worker(workerUrl, { type: 'module' });
|
|
233
|
+
} catch (error) {
|
|
234
|
+
URL.revokeObjectURL(workerUrl);
|
|
235
|
+
workerUrl = null;
|
|
236
|
+
fail(error);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
worker.addEventListener('error', workerFailure, { once: true });
|
|
241
|
+
worker.addEventListener('messageerror', workerFailure, { once: true });
|
|
242
|
+
|
|
243
|
+
const opts = chatOpts || {};
|
|
244
|
+
webllm.CreateWebWorkerMLCEngine(worker, modelId, { appConfig, initProgressCallback: notifyProgress }, opts)
|
|
245
|
+
.then(engine => {
|
|
246
|
+
if (settled) {
|
|
247
|
+
engine.unload?.();
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
settled = true;
|
|
251
|
+
cleanup();
|
|
252
|
+
resolve({
|
|
253
|
+
engine,
|
|
254
|
+
release: async () => {
|
|
255
|
+
try { await engine.unload?.(); } finally { discard(); }
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
})
|
|
259
|
+
.catch(error => fail(error, false));
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
class WebLLMEngineManager {
|
|
264
|
+
constructor(createEngine = createWorkerEngine) {
|
|
265
|
+
this.createEngine = createEngine;
|
|
266
|
+
this.active = null;
|
|
267
|
+
this.pending = null;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async acquire(webllm, modelId, appConfig, onProgress, signal, chatOpts = {}) {
|
|
271
|
+
if (this.active?.modelId === modelId) return { handle: this.active.handle, reused: true };
|
|
272
|
+
if (this.pending) {
|
|
273
|
+
if (this.pending.modelId !== modelId) throw new Error('Another WebLLM model is still being prepared.');
|
|
274
|
+
return { handle: await this.pending.promise, reused: false };
|
|
275
|
+
}
|
|
276
|
+
if (this.active) await this.dispose();
|
|
277
|
+
|
|
278
|
+
onProgress?.({ phase: 'starting', percent: null, detail: '' });
|
|
279
|
+
const controller = new AbortController();
|
|
280
|
+
const abort = () => controller.abort(signal?.reason);
|
|
281
|
+
signal?.addEventListener('abort', abort, { once: true });
|
|
282
|
+
if (signal?.aborted) abort();
|
|
283
|
+
const pending = { modelId, promise: null, controller };
|
|
284
|
+
pending.promise = this.createEngine(webllm, modelId, appConfig, onProgress, controller.signal, chatOpts)
|
|
285
|
+
.then(handle => {
|
|
286
|
+
this.active = { modelId, handle };
|
|
287
|
+
return handle;
|
|
288
|
+
})
|
|
289
|
+
.finally(() => {
|
|
290
|
+
signal?.removeEventListener('abort', abort);
|
|
291
|
+
if (this.pending === pending) this.pending = null;
|
|
292
|
+
});
|
|
293
|
+
this.pending = pending;
|
|
294
|
+
return { handle: await pending.promise, reused: false };
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async dispose(modelId) {
|
|
298
|
+
if (modelId && this.active?.modelId !== modelId && this.pending?.modelId !== modelId) return;
|
|
299
|
+
if (this.pending) {
|
|
300
|
+
this.pending.controller.abort();
|
|
301
|
+
try { await this.pending.promise; } catch (_) { /* The caller receives the preparation error. */ }
|
|
302
|
+
}
|
|
303
|
+
const active = this.active;
|
|
304
|
+
this.active = null;
|
|
305
|
+
await active?.handle.release();
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
async function listModels(options = {}) {
|
|
310
|
+
const webllm = await loadWebLLM(options.loader);
|
|
311
|
+
const appConfig = getAppConfig(webllm);
|
|
312
|
+
const entries = Array.isArray(appConfig?.model_list) ? appConfig.model_list : [];
|
|
313
|
+
const models = await Promise.all(entries.map(async entry => {
|
|
314
|
+
let cached = null;
|
|
315
|
+
try {
|
|
316
|
+
cached = await getModelAvailability(webllm, entry.model_id, appConfig);
|
|
317
|
+
} catch (_) { cached = 'unknown'; }
|
|
318
|
+
const defaultContext = Number(entry.overrides?.context_window_size);
|
|
319
|
+
const contextLength = Number.isFinite(defaultContext) && defaultContext > 0 ? Math.floor(defaultContext) : 4096;
|
|
320
|
+
return {
|
|
321
|
+
id: entry.model_id,
|
|
322
|
+
name: entry.model_id,
|
|
323
|
+
details: {
|
|
324
|
+
webllmCache: cached,
|
|
325
|
+
webllmVramMB: Number.isFinite(Number(entry.vram_required_MB)) ? Number(entry.vram_required_MB) : null,
|
|
326
|
+
loaded_context_length: contextLength,
|
|
327
|
+
max_context_length: contextLength
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
}));
|
|
331
|
+
models.sort((a, b) => {
|
|
332
|
+
const aCached = a?.details?.webllmCache === 'cached' ? 1 : 0;
|
|
333
|
+
const bCached = b?.details?.webllmCache === 'cached' ? 1 : 0;
|
|
334
|
+
return bCached - aCached;
|
|
335
|
+
});
|
|
336
|
+
return { success: true, models, count: models.length, endpoint: 'webllm://local' };
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function toSseStream(iterable, release) {
|
|
340
|
+
const encoder = new TextEncoder();
|
|
341
|
+
const iterator = iterable[Symbol.asyncIterator]();
|
|
342
|
+
return new ReadableStream({
|
|
343
|
+
async pull(controller) {
|
|
344
|
+
try {
|
|
345
|
+
const next = await iterator.next();
|
|
346
|
+
if (next.done) {
|
|
347
|
+
controller.enqueue(encoder.encode('data: [DONE]\n\n'));
|
|
348
|
+
controller.close();
|
|
349
|
+
await release();
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(next.value)}\n\n`));
|
|
353
|
+
} catch (error) {
|
|
354
|
+
await release();
|
|
355
|
+
controller.error(error);
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
async cancel() {
|
|
359
|
+
try { await iterator.return?.(); } finally { await release(); }
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const BaseProviderAdapter = Providers?.BaseProviderAdapter || class {};
|
|
365
|
+
class WebLLMProviderAdapter extends BaseProviderAdapter {
|
|
366
|
+
constructor(options = {}) {
|
|
367
|
+
super({
|
|
368
|
+
id: 'webllm', label: 'WebLLM (local)', reasoningLevels: ['none'],
|
|
369
|
+
connection: { endpoint: 'webllm://local', endpointReadOnly: true, credentials: false, localModelManagement: true },
|
|
370
|
+
capabilities: { vision: false, tools: false, reasoning: false, jsonMode: false, promptCaching: false, embeddings: false, modelListing: true },
|
|
371
|
+
...options
|
|
372
|
+
});
|
|
373
|
+
this.engines = options.engineManager || new WebLLMEngineManager();
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
normalizeEndpoint() { return 'webllm://local'; }
|
|
377
|
+
buildHeaders() { return {}; }
|
|
378
|
+
supportsUsageStatistics() { return false; }
|
|
379
|
+
async listModels(options) { return listModels(options); }
|
|
380
|
+
|
|
381
|
+
async disposeActiveEngine() {
|
|
382
|
+
await this.engines.dispose();
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
async deactivate() {
|
|
386
|
+
await this.disposeActiveEngine();
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
async createStreamResponse({ payload, signal, onProgress }) {
|
|
390
|
+
if (!payload.model) throw new Error('Selecciona un modelo WebLLM descargado en el perfil.');
|
|
391
|
+
const webllm = await loadWebLLM();
|
|
392
|
+
let appConfig = getAppConfig(webllm);
|
|
393
|
+
const available = await isModelAvailable(webllm, payload.model, appConfig);
|
|
394
|
+
if (!available) throw new Error('El modelo WebLLM no está disponible por completo en el almacenamiento local. Descárgalo desde el perfil antes de iniciar el chat.');
|
|
395
|
+
|
|
396
|
+
const activeWebllmConfig = payload.webllmConfig || getActiveWebllmConfig();
|
|
397
|
+
const overrides = extractOverrides(activeWebllmConfig);
|
|
398
|
+
if (overrides) {
|
|
399
|
+
appConfig = applyModelOverrides(appConfig, payload.model, overrides);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (signal?.aborted) throw abortError();
|
|
403
|
+
const acquired = await this.engines.acquire(webllm, payload.model, appConfig, onProgress, signal, overrides || {});
|
|
404
|
+
const engineHandle = acquired.handle;
|
|
405
|
+
if (signal?.aborted) {
|
|
406
|
+
throw abortError();
|
|
407
|
+
}
|
|
408
|
+
const interrupt = () => engineHandle.engine?.interruptGenerate?.();
|
|
409
|
+
signal?.addEventListener('abort', interrupt, { once: true });
|
|
410
|
+
const finishStream = () => signal?.removeEventListener('abort', interrupt);
|
|
411
|
+
try {
|
|
412
|
+
const stream = await engineHandle.engine.chat.completions.create({ ...payload, stream: true });
|
|
413
|
+
return { ok: true, body: toSseStream(stream, finishStream) };
|
|
414
|
+
} catch (error) {
|
|
415
|
+
finishStream();
|
|
416
|
+
throw error;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
async downloadModel(modelId, onProgress) {
|
|
421
|
+
const webllm = await loadWebLLM();
|
|
422
|
+
const appConfig = getAppConfig(webllm);
|
|
423
|
+
try {
|
|
424
|
+
await this.engines.acquire(webllm, modelId, appConfig, onProgress);
|
|
425
|
+
const cached = await webllm.hasModelInCache?.(modelId, appConfig);
|
|
426
|
+
if (cached !== true) {
|
|
427
|
+
throw new Error('WebLLM terminó la preparación, pero el navegador no pudo guardar el modelo en Cache Storage.');
|
|
428
|
+
}
|
|
429
|
+
if (!setModelCompleted(modelId, true)) {
|
|
430
|
+
throw new Error('WebLLM could not persist the completed model verification.');
|
|
431
|
+
}
|
|
432
|
+
} catch (error) {
|
|
433
|
+
setModelCompleted(modelId, false);
|
|
434
|
+
await this.engines.dispose(modelId);
|
|
435
|
+
throw error;
|
|
436
|
+
}
|
|
437
|
+
return true;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
async deleteModel(modelId) {
|
|
441
|
+
await this.engines.dispose(modelId);
|
|
442
|
+
const webllm = await loadWebLLM();
|
|
443
|
+
const appConfig = getAppConfig(webllm);
|
|
444
|
+
if (typeof webllm.deleteModelAllInfoInCache !== 'function') {
|
|
445
|
+
throw new Error('Esta versión de WebLLM no permite borrar modelos individualmente.');
|
|
446
|
+
}
|
|
447
|
+
await webllm.deleteModelAllInfoInCache(modelId, appConfig);
|
|
448
|
+
const removed = await webllm.hasModelInCache?.(modelId, appConfig) !== true;
|
|
449
|
+
if (removed) setModelCompleted(modelId, false);
|
|
450
|
+
return removed;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
async cancelModelOperation(modelId) {
|
|
454
|
+
await this.engines.dispose(modelId);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
getCompletedModelIds() {
|
|
458
|
+
return getCompletedModelIds();
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
isModelCompleted(modelId) {
|
|
462
|
+
return isModelCompleted(modelId);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const adapter = new WebLLMProviderAdapter();
|
|
467
|
+
Providers?.registry?.register(adapter);
|
|
468
|
+
return {
|
|
469
|
+
WEBLLM_URL,
|
|
470
|
+
COMPLETED_MODELS_STORAGE_KEY,
|
|
471
|
+
WebLLMEngineManager,
|
|
472
|
+
WebLLMProviderAdapter,
|
|
473
|
+
adapter,
|
|
474
|
+
loadWebLLM,
|
|
475
|
+
getAppConfig,
|
|
476
|
+
listModels,
|
|
477
|
+
getModelAvailability,
|
|
478
|
+
getCompletedModelIds,
|
|
479
|
+
isModelCompleted,
|
|
480
|
+
normalizeProgress,
|
|
481
|
+
createWorkerEngine,
|
|
482
|
+
extractOverrides,
|
|
483
|
+
applyModelOverrides,
|
|
484
|
+
getActiveWebllmConfig
|
|
485
|
+
};
|
|
486
|
+
});
|