Add model selection support for llama.cpp router mode (#4910) * Add model selection support for llama.cpp router mode - Add llamacpp_model setting to textgen-settings.js - Implement loadLlamaCppModels() function to fetch and populate models - Add onLlamaCppModelSelect() handler for model selection - Update status check to load llama.cpp models when connecting - Update getTextGenModel() to return selected llama.cpp model - Add model dropdown to HTML UI in llama.cpp section - Initialize event handlers and Select2 for better UX - Add llamacpp_model to preset manager for save/load support - Add llamacpp_model to slash commands support This implements model selection for llama.cpp router mode, allowing users to select from multiple models without restarting the server. Follows the same pattern as Ollama, Tabby, and vLLM implementations. * Correct spelling * Fix clear selection position --------- Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>

7f98e3e21305ea41a917a2f4d10bc0d69a78b7c5

my-alt <250851737+my-alt-acct@users.noreply.github.com>

Signed
6 files changed, +66 -1Ignore whitespace
public/css/select2-overrides.css+5 -0
@@ -16,6 +16,11 @@
1616 user-select: none;
1717}
1818
19+.select2-container .select2-selection.select2-selection--single.select2-selection--clearable .select2-selection__clear {
20+ top: 0;
21+ right: 25px;
22+}
23+
1924.select2-container .select2-selection .select2-selection__clear {
2025 color: var(--SmartThemeBodyColor);
2126 font-size: 20px;
public/index.html+10 -0
@@ -2679,6 +2679,16 @@
26792679 <small data-i18n="Example: http://127.0.0.1:8080">Example: http://127.0.0.1:8080</small>
26802680 <input id="llamacpp_api_url_text" class="text_pole wide100p" value="" autocomplete="off" data-server-history="llamacpp">
26812681 </div>
2682+ <div>
2683+ <h4>
2684+ <span data-i18n="llama.cpp Model">llama.cpp Model</span>
2685+ </h4>
2686+ <select id="llamacpp_model">
2687+ <option value="" data-i18n="-- Connect to the API --">
2688+ -- Connect to the API --
2689+ </option>
2690+ </select>
2691+ </div>
26822692 </div>
26832693 <div data-tg-type="ollama">
26842694 <div class="flex-container flexFlowColumn">
public/scripts/preset-manager.js+1 -0
@@ -701,6 +701,7 @@ class PresetManager {
701701 'ollama_model',
702702 'vllm_model',
703703 'aphrodite_model',
704+ 'llamacpp_model',
704705 'server_urls',
705706 'type',
706707 'custom_model',
public/scripts/slash-commands.js+1 -0
@@ -4834,6 +4834,7 @@ function getModelOptions(quiet) {
48344834 { id: 'aphrodite_model', api: 'textgenerationwebui', type: textgen_types.APHRODITE },
48354835 { id: 'ollama_model', api: 'textgenerationwebui', type: textgen_types.OLLAMA },
48364836 { id: 'tabby_model', api: 'textgenerationwebui', type: textgen_types.TABBY },
4837+ { id: 'llamacpp_model', api: 'textgenerationwebui', type: textgen_types.LLAMACPP },
48374838 { id: 'featherless_model', api: 'textgenerationwebui', type: textgen_types.FEATHERLESS },
48384839 { id: 'model_openai_select', api: 'openai', type: chat_completion_sources.OPENAI },
48394840 { id: 'model_claude_select', api: 'openai', type: chat_completion_sources.CLAUDE },
public/scripts/textgen-models.js+39 -0
@@ -17,6 +17,7 @@ let vllmModels = [];
1717let aphroditeModels = [];
1818let featherlessModels = [];
1919let tabbyModels = [];
20+let llamacppModels = [];
2021export let openRouterModels = [];
2122
2223/**
@@ -139,6 +140,30 @@ export async function loadTabbyModels(data) {
139140 }
140141}
141142
143+export async function loadLlamaCppModels(data) {
144+ if (!Array.isArray(data)) {
145+ console.error('Invalid llama.cpp models data', data);
146+ return;
147+ }
148+
149+ llamacppModels = data;
150+ llamacppModels.sort((a, b) => a.id.localeCompare(b.id));
151+ llamacppModels.unshift({ id: '' });
152+
153+ if (!llamacppModels.find(x => x.id === textgen_settings.llamacpp_model)) {
154+ textgen_settings.llamacpp_model = llamacppModels[0]?.id || '';
155+ }
156+
157+ $('#llamacpp_model').empty();
158+ for (const model of llamacppModels) {
159+ const option = document.createElement('option');
160+ option.value = model.id;
161+ option.text = model.id;
162+ option.selected = model.id === textgen_settings.llamacpp_model;
163+ $('#llamacpp_model').append(option);
164+ }
165+}
166+
142167export async function loadTogetherAIModels(data) {
143168 if (!Array.isArray(data)) {
144169 console.error('Invalid Together AI models data', data);
@@ -637,6 +662,12 @@ function onTabbyModelSelect() {
637662 $('#api_button_textgenerationwebui').trigger('click');
638663}
639664
665+function onLlamaCppModelSelect() {
666+ const modelId = String($('#llamacpp_model').val());
667+ textgen_settings.llamacpp_model = modelId;
668+ $('#api_button_textgenerationwebui').trigger('click');
669+}
670+
640671function onOpenRouterModelSelect() {
641672 const modelId = String($('#openrouter_model').val());
642673 textgen_settings.openrouter_model = modelId;
@@ -952,6 +983,7 @@ export function initTextGenModels() {
952983 $('#aphrodite_model').on('change', onAphroditeModelSelect);
953984 $('#tabby_download_model').on('click', downloadTabbyModel);
954985 $('#tabby_model').on('change', onTabbyModelSelect);
986+ $('#llamacpp_model').on('change', onLlamaCppModelSelect);
955987 $('#featherless_model').on('change', () => onFeatherlessModelSelect(String($('#featherless_model').val())));
956988
957989 const providersSelect = $('.openrouter_providers');
@@ -990,6 +1022,13 @@ export function initTextGenModels() {
9901022 width: '100%',
9911023 allowClear: true,
9921024 });
1025+ $('#llamacpp_model').select2({
1026+ placeholder: t`[Currently loaded]`,
1027+ searchInputPlaceholder: t`Search models...`,
1028+ searchInputCssClass: 'text_pole',
1029+ width: '100%',
1030+ allowClear: true,
1031+ });
9931032 $('#model_infermaticai_select').select2({
9941033 placeholder: t`Select a model`,
9951034 searchInputPlaceholder: t`Search models...`,
public/scripts/textgen-settings.js+10 -1
@@ -23,7 +23,7 @@ import { power_user, registerDebugFunction } from './power-user.js';
2323import { getActiveManualApiSamplers, loadApiSelectedSamplers, isSamplerManualPriorityEnabled } from './samplerSelect.js';
2424import { SECRET_KEYS, writeSecret } from './secrets.js';
2525import { getEventSourceStream } from './sse-stream.js';
2626import { getCurrentDreamGenModelTokenizer, getCurrentOpenRouterModelTokenizer, loadAphroditeModels, loadDreamGenModels, loadFeatherlessModels, loadGenericModels, loadInfermaticAIModels, loadLlamaCppModels, loadMancerModels, loadOllamaModels, loadOpenRouterModels, loadTabbyModels, loadTogetherAIModels, loadVllmModels } from './textgen-models.js';
2727import { ENCODE_TOKENIZERS, TEXTGEN_TOKENIZERS, TOKENIZER_SUPPORTED_KEY, getTextTokens, tokenizers } from './tokenizers.js';
2828import { AbortReason } from './util/AbortReason.js';
2929import { getSortableDelay, onlyUnique, arraysEqual, isObject } from './utils.js';
@@ -214,6 +214,7 @@ export const textgenerationwebui_settings = {
214214 aphrodite_model: '',
215215 dreamgen_model: 'lucid-v1-extra-large/text',
216216 tabby_model: '',
217+ llamacpp_model: '',
217218 sampler_order: KOBOLDCPP_ORDER,
218219 logit_bias: [],
219220 n: 1,
@@ -701,6 +702,9 @@ async function getStatusTextgen() {
701702 } else if (textgenerationwebui_settings.type === textgen_types.TABBY) {
702703 loadTabbyModels(data?.data);
703704 setOnlineStatus(textgenerationwebui_settings.tabby_model || data?.result);
705+ } else if (textgenerationwebui_settings.type === textgen_types.LLAMACPP) {
706+ loadLlamaCppModels(data?.data);
707+ setOnlineStatus(textgenerationwebui_settings.llamacpp_model || data?.result || t`Connected`);
704708 } else if (textgenerationwebui_settings.type === textgen_types.GENERIC) {
705709 loadGenericModels(data?.data);
706710 setOnlineStatus(textgenerationwebui_settings.generic_model || data?.result || t`Connected`);
@@ -1462,6 +1466,11 @@ export function getTextGenModel(settings = null) {
14621466 return settings.tabby_model;
14631467 }
14641468 break;
1469+ case LLAMACPP:
1470+ if (settings.llamacpp_model) {
1471+ return settings.llamacpp_model;
1472+ }
1473+ break;
14651474 default:
14661475 return undefined;
14671476 }