| 1168 | 1221 | $('#siliconflow_vectorsModel').toggle(settings.source === 'siliconflow'); |
| 1169 | 1222 | $('#workers_ai_vectorsModel').toggle(settings.source === 'workers_ai'); |
| 1170 | 1223 | $('#vector_altEndpointUrl').toggle(vectorApiRequiresUrl.includes(settings.source)); |
| 1171 | 1224 | switchif (settings.source === 'webllm') { |
| 1172 | | - case 'webllm': |
| 1173 | 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 | 1232 | * PopulatesLoads themodels Electronfrom Huba modelremote embedding endpoint and populates the corresponding select element. |
| 1286 | 1233 | * @param {{string} id:source string,- name:The stringsource }[]}key modelsmatching Electrona HubremoteEmbeddingEndpoints modelsentry |
| 1287 | 1234 | */ |
| 1288 | 1235 | async function populateElectronHubModelSelectloadRemoteEmbeddingModels(modelssource) { |
| 1289 | 1236 | const selectconfig = $('#vectors_electronhub_model')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 | 1246 | * Populates the OpenRouter model 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 | 1249 | function populateOpenRouterModelSelectpopulateSelect(models) { |
| 1327 | 1250 | const select = $('`#vectors_openrouter_model'${selectId}`); |
| 1328 | 1251 | select.empty(); |
| 1329 | 1252 | for (const m of models) { |
| 1330 | 1253 | const option = document.createElement('option'); |
| 1331 | 1254 | option.value = m.id[valueProperty]; |
| 1332 | 1255 | option.text = textProperty ? (m.name[textProperty] || m.id[valueProperty]) : m[valueProperty]; |
| 1333 | 1256 | select.append(option); |
| 1334 | 1257 | } |
| 1335 | 1258 | if (!settings.openrouter_model[settingsKey] && models.length) { |
| 1336 | 1259 | settings.openrouter_model[settingsKey] = models[0].id[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 | 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 | 1271 | method: 'POST', |
| 1345 | 1272 | headers: getRequestHeaders(), |
| 1346 | 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 | 1277 | if (!response.ok) { |
| 1352 | 1278 | throw new Error(`HTTP ${response.status}`); |
| 1353 | 1279 | } |
| 1354 | 1280 | |
| 1355 | 1281 | /** @type {Array<any>} */ |
| 1356 | 1282 | const data = await response.json(); |
| 1357 | 1283 | constlet 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 | 1289 | } catch (err) { |
| 1397 | 1290 | console.warn('Workers AI`${source} models fetch failed'`, err); |
| 1398 | 1291 | populateWorkersAIModelSelectpopulateSelect([]); |
| 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 | /** |