| 1168 | $('#siliconflow_vectorsModel').toggle(settings.source === 'siliconflow'); | 1221 | $('#siliconflow_vectorsModel').toggle(settings.source === 'siliconflow'); |
| 1169 | $('#workers_ai_vectorsModel').toggle(settings.source === 'workers_ai'); | 1222 | $('#workers_ai_vectorsModel').toggle(settings.source === 'workers_ai'); |
| 1170 | $('#vector_altEndpointUrl').toggle(vectorApiRequiresUrl.includes(settings.source)); | 1223 | $('#vector_altEndpointUrl').toggle(vectorApiRequiresUrl.includes(settings.source)); |
| 1171 | switch (settings.source) { | 1224 | if (settings.source === 'webllm') { |
| 1172 | case 'webllm': | | |
| 1173 | loadWebLlmModels(); | 1225 | loadWebLlmModels(); |
| 1174 | break; | 1226 | } else if (settings.source in remoteEmbeddingEndpoints) { |
| 1175 | case 'electronhub': | 1227 | loadRemoteEmbeddingModels(settings.source); |
| 1176 | loadElectronHubModels(); | | |
| 1177 | break; | | |
| 1178 | case 'openrouter': | | |
| 1179 | loadOpenRouterModels(); | | |
| 1180 | break; | | |
| 1181 | case 'chutes': | | |
| 1182 | loadChutesModels(); | | |
| 1183 | break; | | |
| 1184 | case 'nanogpt': | | |
| 1185 | loadNanoGPTModels(); | | |
| 1186 | break; | | |
| 1187 | case 'siliconflow': | | |
| 1188 | loadSiliconFlowModels(); | | |
| 1189 | break; | | |
| 1190 | case 'workers_ai': | | |
| 1191 | loadWorkersAIModels(); | | |
| 1192 | break; | | |
| 1193 | } | | |
| 1194 | } | | |
| 1195 | | | |
| 1196 | async function loadChutesModels() { | | |
| 1197 | try { | | |
| 1198 | const response = await fetch('/api/openai/chutes/models/embedding', { | | |
| 1199 | method: 'POST', | | |
| 1200 | headers: getRequestHeaders({ omitContentType: true }), | | |
| 1201 | }); | | |
| 1202 | if (!response.ok) { | | |
| 1203 | throw new Error(`HTTP ${response.status}`); | | |
| 1204 | } | | |
| 1205 | /** @type {Array<any>} */ | | |
| 1206 | const data = await response.json(); | | |
| 1207 | const models = Array.isArray(data) ? data : []; | | |
| 1208 | populateChutesModelSelect(models); | | |
| 1209 | } catch (err) { | | |
| 1210 | console.warn('Chutes models fetch failed', err); | | |
| 1211 | populateChutesModelSelect([]); | | |
| 1212 | } | | |
| 1213 | } | | |
| 1214 | | | |
| 1215 | function populateChutesModelSelect(models) { | | |
| 1216 | const select = $('#vectors_chutes_model'); | | |
| 1217 | select.empty(); | | |
| 1218 | for (const m of models) { | | |
| 1219 | const option = document.createElement('option'); | | |
| 1220 | option.value = m.slug; | | |
| 1221 | option.text = m.name; | | |
| 1222 | select.append(option); | | |
| 1223 | } | | |
| 1224 | if (!settings.chutes_model && models.length) { | | |
| 1225 | settings.chutes_model = models[0].slug; | | |
| 1226 | } | | |
| 1227 | $('#vectors_chutes_model').val(settings.chutes_model); | | |
| 1228 | } | | |
| 1229 | | | |
| 1230 | async function loadNanoGPTModels() { | | |
| 1231 | try { | | |
| 1232 | const response = await fetch('/api/openai/nanogpt/models/embedding', { | | |
| 1233 | method: 'POST', | | |
| 1234 | headers: getRequestHeaders({ omitContentType: true }), | | |
| 1235 | }); | | |
| 1236 | if (!response.ok) { | | |
| 1237 | throw new Error(`HTTP ${response.status}`); | | |
| 1238 | } | | |
| 1239 | /** @type {Array<any>} */ | | |
| 1240 | const data = await response.json(); | | |
| 1241 | const models = Array.isArray(data) ? data : []; | | |
| 1242 | populateNanoGPTModelSelect(models); | | |
| 1243 | } catch (err) { | | |
| 1244 | console.warn('NanoGPT models fetch failed', err); | | |
| 1245 | populateNanoGPTModelSelect([]); | | |
| 1246 | } | | |
| 1247 | } | | |
| 1248 | | | |
| 1249 | function populateNanoGPTModelSelect(models) { | | |
| 1250 | const select = $('#vectors_nanogpt_model'); | | |
| 1251 | select.empty(); | | |
| 1252 | for (const m of models) { | | |
| 1253 | const option = document.createElement('option'); | | |
| 1254 | option.value = m.id; | | |
| 1255 | option.text = m.name || m.id; | | |
| 1256 | select.append(option); | | |
| 1257 | } | | |
| 1258 | if (!settings.nanogpt_model && models.length) { | | |
| 1259 | settings.nanogpt_model = models[0].id; | | |
| 1260 | } | | |
| 1261 | $('#vectors_nanogpt_model').val(settings.nanogpt_model); | | |
| 1262 | } | | |
| 1263 | | | |
| 1264 | async function loadElectronHubModels() { | | |
| 1265 | try { | | |
| 1266 | const response = await fetch('/api/openai/electronhub/models', { | | |
| 1267 | method: 'POST', | | |
| 1268 | headers: getRequestHeaders({ omitContentType: true }), | | |
| 1269 | }); | | |
| 1270 | if (!response.ok) { | | |
| 1271 | throw new Error(`HTTP ${response.status}`); | | |
| 1272 | } | | |
| 1273 | /** @type {Array<any>} */ | | |
| 1274 | const data = await response.json(); | | |
| 1275 | // filter by embeddings endpoint | | |
| 1276 | const models = Array.isArray(data) ? data.filter(m => Array.isArray(m?.endpoints) && m.endpoints.includes('/v1/embeddings')) : []; | | |
| 1277 | populateElectronHubModelSelect(models); | | |
| 1278 | } catch (err) { | | |
| 1279 | console.warn('Electron Hub models fetch failed', err); | | |
| 1280 | populateElectronHubModelSelect([]); | | |
| 1281 | } | 1228 | } |
| 1282 | } | 1229 | } |
| 1283 | | 1230 | |
| 1284 | /** | 1231 | /** |
| 1285 | * Populates the Electron Hub model select element. | 1232 | * Loads models from a remote embedding endpoint and populates the corresponding select element. |
| 1286 | * @param {{ id: string, name: string }[]} models Electron Hub models | 1233 | * @param {string} source - The source key matching a remoteEmbeddingEndpoints entry |
| 1287 | */ | 1234 | */ |
| 1288 | function populateElectronHubModelSelect(models) { | 1235 | async function loadRemoteEmbeddingModels(source) { |
| 1289 | const select = $('#vectors_electronhub_model'); | 1236 | const config = remoteEmbeddingEndpoints[source]; |
| 1290 | select.empty(); | 1237 | if (!config) { |
| 1291 | for (const m of models) { | 1238 | return; |
| 1292 | const option = document.createElement('option'); | | |
| 1293 | option.value = m.id; | | |
| 1294 | option.text = m.name || m.id; | | |
| 1295 | select.append(option); | | |
| 1296 | } | | |
| 1297 | if (!settings.electronhub_model && models.length) { | | |
| 1298 | settings.electronhub_model = models[0].id; | | |
| 1299 | } | | |
| 1300 | $('#vectors_electronhub_model').val(settings.electronhub_model); | | |
| 1301 | } | 1239 | } |
| 1302 | | 1240 | |
| 1303 | async function loadOpenRouterModels() { | 1241 | const { url, settingsKey, selectId, getBody, filter } = config; |
| 1304 | try { | 1242 | const valueProperty = config.valueProperty || 'id'; |
| 1305 | const response = await fetch('/api/openrouter/models/embedding', { | 1243 | const textProperty = config.textProperty; |
| 1306 | method: 'POST', | | |
| 1307 | headers: getRequestHeaders({ omitContentType: true }), | | |
| 1308 | }); | | |
| 1309 | if (!response.ok) { | | |
| 1310 | throw new Error(`HTTP ${response.status}`); | | |
| 1311 | } | | |
| 1312 | /** @type {Array<any>} */ | | |
| 1313 | const data = await response.json(); | | |
| 1314 | const models = Array.isArray(data) ? data : []; | | |
| 1315 | populateOpenRouterModelSelect(models); | | |
| 1316 | } catch (err) { | | |
| 1317 | console.warn('OpenRouter models fetch failed', err); | | |
| 1318 | populateOpenRouterModelSelect([]); | | |
| 1319 | } | | |
| 1320 | } | | |
| 1321 | | 1244 | |
| 1322 | /** | 1245 | /** |
| 1323 | * Populates the OpenRouter model select element. | 1246 | * Populates the select element with the given models. |
| 1324 | * @param {{ id: string, name: string }[]} models OpenRouter models | 1247 | * @param {any[]} models - Array of model objects |
| 1325 | */ | 1248 | */ |
| 1326 | function populateOpenRouterModelSelect(models) { | 1249 | function populateSelect(models) { |
| 1327 | const select = $('#vectors_openrouter_model'); | 1250 | const select = $(`#${selectId}`); |
| 1328 | select.empty(); | 1251 | select.empty(); |
| 1329 | for (const m of models) { | 1252 | for (const m of models) { |
| 1330 | const option = document.createElement('option'); | 1253 | const option = document.createElement('option'); |
| 1331 | option.value = m.id; | 1254 | option.value = m[valueProperty]; |
| 1332 | option.text = m.name || m.id; | 1255 | option.text = textProperty ? (m[textProperty] || m[valueProperty]) : m[valueProperty]; |
| 1333 | select.append(option); | 1256 | select.append(option); |
| 1334 | } | 1257 | } |
| 1335 | if (!settings.openrouter_model && models.length) { | 1258 | if (!settings[settingsKey] && models.length) { |
| 1336 | settings.openrouter_model = models[0].id; | 1259 | settings[settingsKey] = models[0][valueProperty]; |
| | 1260 | Object.assign(extension_settings.vectors, settings); |
| | 1261 | saveSettingsDebounced(); |
| 1337 | } | 1262 | } |
| 1338 | $('#vectors_openrouter_model').val(settings.openrouter_model); | 1263 | select.val(settings[settingsKey]); |
| 1339 | } | 1264 | } |
| 1340 | | 1265 | |
| 1341 | async function loadSiliconFlowModels() { | | |
| 1342 | try { | 1266 | try { |
| 1343 | const response = await fetch('/api/openai/siliconflow/models/embedding', { | 1267 | const body = typeof getBody === 'function' ? getBody() : {}; |
| | 1268 | |
| | 1269 | /** @type {RequestInit} */ |
| | 1270 | const fetchOptions = { |
| 1344 | method: 'POST', | 1271 | method: 'POST', |
| 1345 | headers: getRequestHeaders(), | 1272 | headers: getRequestHeaders(), |
| 1346 | body: JSON.stringify({ | 1273 | body: JSON.stringify(body || {}), |
| 1347 | siliconflow_endpoint: oai_settings.siliconflow_endpoint, | 1274 | }; |
| 1348 | }), | | |
| 1349 | }); | | |
| 1350 | | 1275 | |
| | 1276 | const response = await fetch(url, fetchOptions); |
| 1351 | if (!response.ok) { | 1277 | if (!response.ok) { |
| 1352 | throw new Error(`HTTP ${response.status}`); | 1278 | throw new Error(`HTTP ${response.status}`); |
| 1353 | } | 1279 | } |
| 1354 | | 1280 | |
| 1355 | /** @type {Array<any>} */ | 1281 | /** @type {Array<any>} */ |
| 1356 | const data = await response.json(); | 1282 | const data = await response.json(); |
| 1357 | const models = Array.isArray(data) ? data : []; | 1283 | let models = Array.isArray(data) ? data : []; |
| 1358 | populateSiliconFlowModelSelect(models); | 1284 | if (filter) { |
| 1359 | } catch (err) { | 1285 | models = filter(models); |
| 1360 | console.warn('SiliconFlow models fetch failed', err); | | |
| 1361 | populateSiliconFlowModelSelect([]); | | |
| 1362 | } | | |
| 1363 | } | 1286 | } |
| 1364 | | 1287 | |
| 1365 | function populateSiliconFlowModelSelect(models) { | 1288 | populateSelect(models); |
| 1366 | const select = $('#vectors_siliconflow_model'); | | |
| 1367 | select.empty(); | | |
| 1368 | for (const m of models) { | | |
| 1369 | const option = document.createElement('option'); | | |
| 1370 | option.value = m.id; | | |
| 1371 | option.text = m.id; | | |
| 1372 | select.append(option); | | |
| 1373 | } | | |
| 1374 | if (!settings.siliconflow_model && models.length) { | | |
| 1375 | settings.siliconflow_model = models[0].id; | | |
| 1376 | } | | |
| 1377 | $('#vectors_siliconflow_model').val(settings.siliconflow_model); | | |
| 1378 | } | | |
| 1379 | | | |
| 1380 | async function loadWorkersAIModels() { | | |
| 1381 | try { | | |
| 1382 | const response = await fetch('/api/openai/workers-ai/models/embedding', { | | |
| 1383 | method: 'POST', | | |
| 1384 | headers: getRequestHeaders(), | | |
| 1385 | body: JSON.stringify({ | | |
| 1386 | workers_ai_account_id: oai_settings.workers_ai_account_id, | | |
| 1387 | }), | | |
| 1388 | }); | | |
| 1389 | if (!response.ok) { | | |
| 1390 | throw new Error(`HTTP ${response.status}`); | | |
| 1391 | } | | |
| 1392 | /** @type {Array<any>} */ | | |
| 1393 | const data = await response.json(); | | |
| 1394 | const models = Array.isArray(data) ? data : []; | | |
| 1395 | populateWorkersAIModelSelect(models); | | |
| 1396 | } catch (err) { | 1289 | } catch (err) { |
| 1397 | console.warn('Workers AI models fetch failed', err); | 1290 | console.warn(`${source} models fetch failed`, err); |
| 1398 | populateWorkersAIModelSelect([]); | 1291 | populateSelect([]); |
| 1399 | } | | |
| 1400 | } | | |
| 1401 | | | |
| 1402 | function populateWorkersAIModelSelect(models) { | | |
| 1403 | const select = $('#vectors_workers_ai_model'); | | |
| 1404 | select.empty(); | | |
| 1405 | for (const m of models) { | | |
| 1406 | const option = document.createElement('option'); | | |
| 1407 | option.value = m.id; | | |
| 1408 | option.text = m.id; | | |
| 1409 | select.append(option); | | |
| 1410 | } | | |
| 1411 | if (!settings.workers_ai_model && models.length) { | | |
| 1412 | settings.workers_ai_model = models[0].id; | | |
| 1413 | Object.assign(extension_settings.vectors, settings); | | |
| 1414 | saveSettingsDebounced(); | | |
| 1415 | } | 1292 | } |
| 1416 | $('#vectors_workers_ai_model').val(settings.workers_ai_model); | | |
| 1417 | } | 1293 | } |
| 1418 | | 1294 | |
| 1419 | /** | 1295 | /** |