| 266 | 266 | } |
| 267 | 267 | |
| 268 | 268 | export async function loadFeatherlessModels(data) { |
| 269 | + const searchBar = document.getElementById('model_search_bar'); |
| 270 | + const modelCardBlock = document.getElementById('model_card_block'); |
| 271 | + const paginationContainer = $('#model_pagination_container'); |
| 272 | + const sortOrderSelect = document.getElementById('model_sort_order'); |
| 273 | + const classSelect = document.getElementById('class_selection'); |
| 274 | + const storageKey = 'Models_PerPage'; |
| 275 | + |
| 276 | + // Store the original models data for search and filtering |
| 277 | + let originalModels = []; |
| 278 | + |
| 269 | 279 | if (!Array.isArray(data)) { |
| 270 | 280 | console.error('Invalid Featherless models data', data); |
| 271 | 281 | return; |
| 272 | 282 | } |
| 273 | 283 | |
| 284 | + // Sort the data by model id (default A-Z) |
| 274 | 285 | data.sort((a, b) => a.id.localeCompare(b.id)); |
| 275 | | - featherlessModels = data; |
| 286 | + originalModels = data; // Store the original data for search |
| 276 | 287 | |
| 277 | | - if (!data.find(x => x.id === textgen_settings.featherless_model)) { |
| 288 | + // Populate class select options with unique classes |
| 278 | | - textgen_settings.featherless_model = data[0]?.id || ''; |
| 289 | + populateClassSelection(data); |
| 290 | + |
| 291 | + // Retrieve the stored number of items per page or default to 5 |
| 292 | + const perPage = Number(localStorage.getItem(storageKey)) || 5; |
| 293 | + |
| 294 | + // Initialize pagination with the full set of models |
| 295 | + setupPagination(originalModels, perPage); |
| 296 | + |
| 297 | + // Function to set up pagination (also used for filtered results) |
| 298 | + function setupPagination(models, perPage) { |
| 299 | + paginationContainer.pagination({ |
| 300 | + dataSource: models, |
| 301 | + pageSize: perPage, |
| 302 | + sizeChangerOptions: [5, 10, 25, 50, 100, 250, 500, 1000], |
| 303 | + pageRange: 1, |
| 304 | + pageNumber: 1, |
| 305 | + showPageNumbers: true, |
| 306 | + showSizeChanger: true, |
| 307 | + prevText: '<', |
| 308 | + nextText: '>', |
| 309 | + formatNavigator: function (currentPage, totalPage) { |
| 310 | + return 'Page ' + currentPage + ' of ' + totalPage; |
| 311 | + }, |
| 312 | + showNavigator: true, |
| 313 | + callback: function (modelsOnPage) { |
| 314 | + // Clear the model card block before adding new cards |
| 315 | + modelCardBlock.innerHTML = ''; |
| 316 | + |
| 317 | + // Loop through the models for the current page and create cards |
| 318 | + modelsOnPage.forEach(model => { |
| 319 | + const card = document.createElement('div'); |
| 320 | + card.classList.add('model-card'); |
| 321 | + |
| 322 | + const modelNameContainer = document.createElement('div'); |
| 323 | + modelNameContainer.classList.add('model-name-container'); |
| 324 | + |
| 325 | + const modelTitle = document.createElement('div'); |
| 326 | + modelTitle.classList.add('model-title'); |
| 327 | + modelTitle.textContent = model.id; |
| 328 | + modelNameContainer.appendChild(modelTitle); |
| 329 | + |
| 330 | + const detailsContainer = document.createElement('div'); |
| 331 | + detailsContainer.classList.add('details-container'); |
| 332 | + |
| 333 | + const modelClassDiv = document.createElement('div'); |
| 334 | + modelClassDiv.classList.add('model-class'); |
| 335 | + modelClassDiv.textContent = `Class: ${model.model_class || 'N/A'}`; |
| 336 | + |
| 337 | + const contextLengthDiv = document.createElement('div'); |
| 338 | + contextLengthDiv.classList.add('model-context-length'); |
| 339 | + contextLengthDiv.textContent = `Context Length: ${model.context_length}`; |
| 340 | + |
| 341 | + detailsContainer.appendChild(modelClassDiv); |
| 342 | + detailsContainer.appendChild(contextLengthDiv); |
| 343 | + |
| 344 | + card.appendChild(modelNameContainer); |
| 345 | + card.appendChild(detailsContainer); |
| 346 | + |
| 347 | + // Append the card to the container |
| 348 | + modelCardBlock.appendChild(card); |
| 349 | + |
| 350 | + // Check if this card is the currently selected model |
| 351 | + if (model.id === selectedModelId) { |
| 352 | + card.classList.add('selected'); // Keep the selected class if it's the same model |
| 353 | + } |
| 354 | + |
| 355 | + // Add click event listener to the card |
| 356 | + card.addEventListener('click', function() { |
| 357 | + // Remove the selected class from all other cards |
| 358 | + document.querySelectorAll('.model-card').forEach(c => c.classList.remove('selected')); |
| 359 | + |
| 360 | + // Add the selected class to the clicked card |
| 361 | + card.classList.add('selected'); |
| 362 | + |
| 363 | + // Call the onFeatherlessModelSelect function with the selected model ID |
| 364 | + onFeatherlessModelSelect(model.id); |
| 365 | + }); |
| 366 | + }); |
| 367 | + }, |
| 368 | + afterSizeSelectorChange: function (e) { |
| 369 | + const newPerPage = e.target.value; |
| 370 | + localStorage.setItem('Models_PerPage', newPerPage); // Save the new value in localStorage |
| 371 | + setupPagination(models, Number(newPerPage)); // Reinitialize pagination with the new per page value |
| 372 | + }, |
| 373 | + }); |
| 279 | 374 | } |
| 280 | 375 | |
| 281 | | - $('#featherless_model').empty(); |
| 376 | + // Add event listener for input on the search bar |
| 282 | | - for (const model of data) { |
| 377 | + searchBar.addEventListener('input', function() { |
| 378 | + applyFiltersAndSort(); |
| 379 | + }); |
| 380 | + |
| 381 | + // Add event listener for the sort order select |
| 382 | + sortOrderSelect.addEventListener('change', function() { |
| 383 | + applyFiltersAndSort(); |
| 384 | + }); |
| 385 | + |
| 386 | + // Add event listener for the class select |
| 387 | + classSelect.addEventListener('change', function() { |
| 388 | + applyFiltersAndSort(); |
| 389 | + }); |
| 390 | + |
| 391 | + // Function to populate class selection dropdown |
| 392 | + function populateClassSelection(models) { |
| 393 | + const uniqueClasses = [...new Set(models.map(model => model.model_class).filter(Boolean))]; // Get unique class names |
| 394 | + uniqueClasses.forEach(className => { |
| 283 | 395 | const option = document.createElement('option'); |
| 284 | 396 | option.value = model.idclassName; |
| 285 | 397 | option.texttextContent = model.idclassName; |
| 286 | | - option.selected = model.id === textgen_settings.featherless_model; |
| 398 | + classSelect.appendChild(option); |
| 287 | | - $('#featherless_model').append(option); |
| 399 | + }); |
| 288 | 400 | } |
| 401 | + |
| 402 | + // Function to apply sorting and filtering based on user input |
| 403 | + function applyFiltersAndSort() { |
| 404 | + const searchQuery = searchBar.value.toLowerCase(); |
| 405 | + const selectedSortOrder = sortOrderSelect.value; |
| 406 | + const selectedClass = classSelect.value; |
| 407 | + |
| 408 | + // Filter the models based on the search query and selected class |
| 409 | + let filteredModels = originalModels.filter(model => { |
| 410 | + const matchesSearch = model.id.toLowerCase().includes(searchQuery); |
| 411 | + const matchesClass = selectedClass ? model.model_class === selectedClass : true; |
| 412 | + return matchesSearch && matchesClass; |
| 413 | + }); |
| 414 | + |
| 415 | + // Sort the filtered models based on selected sort order (A-Z or Z-A) |
| 416 | + if (selectedSortOrder === 'asc') { |
| 417 | + filteredModels.sort((a, b) => a.id.localeCompare(b.id)); |
| 418 | + } else if (selectedSortOrder === 'desc') { |
| 419 | + filteredModels.sort((a, b) => b.id.localeCompare(a.id)); |
| 289 | 420 | } |
| 290 | 421 | |
| 291 | | -function onFeatherlessModelSelect() { |
| 422 | + // Reinitialize pagination with the filtered and sorted models |
| 292 | | - const modelId = String($('#featherless_model').val()); |
| 423 | + setupPagination(filteredModels, Number(localStorage.getItem(storageKey)) || perPage); |
| 424 | + } |
| 425 | +} |
| 426 | + |
| 427 | +let selectedModelId = null; |
| 428 | +function onFeatherlessModelSelect(modelId) { |
| 429 | + // Find the selected model and set the settings |
| 430 | + const model = featherlessModels.find(x => x.id === modelId); |
| 293 | 431 | textgen_settings.featherless_model = modelId; |
| 294 | 432 | $('#api_button_textgenerationwebui').trigger('click'); |
| 295 | | - const model = featherlessModels.find(x => x.id === modelId); |
| 296 | 433 | setGenerationParamsFromPreset({ max_length: model.context_length }); |
| 434 | + selectedModelId = modelId; // Store the selected model ID |
| 297 | 435 | } |
| 298 | | - |
| 299 | | - |
| 300 | 436 | function onMancerModelSelect() { |
| 301 | 437 | const modelId = String($('#mancer_model').val()); |
| 302 | 438 | textgen_settings.mancer_model = modelId; |