diff options
| author | Michael Peter Christen <mc@yacy.net> | 2025-11-07 00:30:55 +0100 |
|---|---|---|
| committer | Michael Peter Christen <mc@yacy.net> | 2025-11-07 00:30:55 +0100 |
| commit | 3ea9276d823616757615712d687978552c31e327 (patch) | |
| tree | 2bb65648744c25a7e7e311862808a358a32da122 /htroot | |
| parent | 9888473d36e955d1a2799cb96fd203d2ea3d055b (diff) | |
added download option for recommended models in LLMSelection
(this is still a stub)
Diffstat (limited to 'htroot')
| -rw-r--r-- | htroot/LLMSelection_p.html | 185 |
1 files changed, 158 insertions, 27 deletions
diff --git a/htroot/LLMSelection_p.html b/htroot/LLMSelection_p.html index 923de1a46..0943039da 100644 --- a/htroot/LLMSelection_p.html +++ b/htroot/LLMSelection_p.html @@ -24,39 +24,169 @@ } else { hoststubInput.value = ""; // Clear the hoststub if another provider is selected } + + // Disable apikey for providers that don't require it + if (provider === "OLLAMA" || provider === "LMSTUDIO") { + apikey.disabled = true; + apikey.value = ""; + } else { + apikey.disabled = false; + } } async function loadModels() { + availableModels = []; const provider = document.getElementById("provider").value; const hoststub = document.getElementById("hoststub").value; - const apiUrl = `${hoststub}/api/tags`; + const apiUrl = provider === "OLLAMA" ? `${hoststub}/api/tags` : `${hoststub}/v1/models`; try { const response = await fetch(apiUrl); - const data = await response.json(); - - const models = data.models; - const modelsContainer = document.getElementById("modelsContainer"); - - models.forEach(model => { - const radioBtn = document.createElement("input"); - radioBtn.type = "radio"; - radioBtn.name = "model"; - radioBtn.value = model.model; - radioBtn.id = model.model; - - const label = document.createElement("label"); - label.htmlFor = model.model; - label.textContent = model.model; - - const br = document.createElement("br"); - - modelsContainer.appendChild(radioBtn); - modelsContainer.appendChild(label); - modelsContainer.appendChild(br); + if (response.status !== 200) { + const apikeyEl = document.getElementById("apikey"); + const apikey = apikeyEl ? apikeyEl.value.trim() : ""; + + if (provider !== "OLLAMA" && provider !== "LMSTUDIO" && !apikey) { + alert("an api key is required for this provider"); + } else { + alert(`Failed to load models. HTTP status: ${response.status}`); + } + console.error('Non-200 response', response); + return; + } + const responsej = await response.json(); + + const availableModelsContainer = document.getElementById("availableModelsContainer"); + availableModelsContainer.innerHTML = "<legend>Available Models</legend>"; + availableModelsContainer.style.display = "none"; + + const models = provider === "OLLAMA" ? (responsej.models || []) : (responsej.data || []); + const getId = provider === "OLLAMA" ? m => m.model : m => m.id; + + const frag = document.createDocumentFragment(); + models.forEach(m => { + const id = getId(m); + const radio = Object.assign(document.createElement("input"), { type: "radio", name: "model", value: id, id }); + const label = Object.assign(document.createElement("label"), { htmlFor: id, textContent: id }); + frag.appendChild(radio); frag.appendChild(label); frag.appendChild(document.createElement("br")); + availableModels.push(id); }); - - + availableModelsContainer.appendChild(frag); + availableModelsContainer.style.display = "block"; + + if (provider === "OLLAMA") { + // show load model container + + const loadModelContainer = document.getElementById("loadModelContainer"); + loadModelContainer.innerHTML = "<legend>Recommended Models</legend>"; + loadModelContainer.style.display = "none"; + recommendedModels = [ + ["smollm2:360m-instruct-q4_K_M", "0.001", "0.5GB", "english-only minimalistic model for small devices", "Huggingface", "apache-2.0"], + ["hf.co/mradermacher/EuroLLM-1.7B-Instruct-GGUF:Q4_K_M", "0.09", "1.5GB", "European Union - funded model, multilingual", "Various European Universities", "apache-2.0"], + ["llama3.2:1b-instruct-q4_K_M", "0.18", "1.5GB", "A good 1B model", "Meta", "llama3.2"], + ["llama3.2:3b-instruct-q4_K_M", "0.66", "3GB", "A good 3B model", "Meta", "llama3.2"], + ["qwen3:4b-instruct-2507-q4_K_M", "7.70", "3GB", "Exceptional good 4B model", "Alibaba", "apache-2.0"], + ["hf.co/mradermacher/Josiefied-Qwen3-4B-Instruct-2507-abliterated-v1-GGUF:Q4_K_M", "7.70", "3GB", "uncensored version of qwen3:4b", "huggingface.co/Goekdeniz-Guelmez", "apache-2.0"], + ["hf.co/mradermacher/medgemma-4b-it-GGUF:Q4_K_M", "0.84", "4GB", "Medical Knowledge and Vision", "Google", "health-ai-developer-foundations"], + ["hf.co/mradermacher/occiglot-7b-eu5-instruct-GGUF:Q4_K_M", "0.19", "5GB", "Support for top-5 EU languages (English, Spanish, French, German, and Italian)", "occiglot.eu", "apache-2.0"], + ["hf.co/allenai/OLMoE-1B-7B-0125-Instruct-GGUF:Q4_K_M", "0.22", "5GB", "open and accessible training data, open-source training code, very fast", "allenai.org", "apache-2.0"], + ["hf.co/bartowski/AGI-0_Art-0-8B-GGUF:Q4_K_M", "11.9", "6GB", "Exceptional good 8B model, ranking above ChatGPT-3.5", "AGI-0.com and Alibaba", "apache-2.0"], + ["phi4:14b-q4_K_M", "5.24", "10GB", "Very strong, made with synthetic data", "Microsoft", "mit"], + ["hf.co/mistralai/Magistral-Small-2509-GGUF:Q4_K_M", "5.18", "16GB", "European flagship model, strong multilangual, reasoning", "mistral.ai", "apache-2.0"], + ["hf.co/bartowski/cognitivecomputations_Dolphin-Mistral-24B-Venice-Edition-GGUF:Q4_K_M", "", "16GB", "Uncensored multilingual european Mistral-24B for role playing", "mistral.ai and dphn.ai", "apache-2.0"] + ["qwen3-vl:30b-a3b-instruct-q4_K_M", "17.33", "22GB", "Very fast, exceptional good 30B model, ranking above GPT-4-turbo, GPT-4.1-nano, GPT-o1, GPT-4o-mini", "Alibaba", "apache-2.0"] + ]; + // subtract all models in availableModels from recommendedModels to get a list of downloadable models + const downloadableModels = recommendedModels.filter(m => m && !availableModels.includes(m[0])); + + // create table + const table = document.createElement("table"); table.className = "table table-striped"; + + // header + const thead = document.createElement("thead"); thead.className = "thead-dark"; + const headerRow = document.createElement("tr"); + ["Model", "Ranking", "Size", "Description", "Provider", "License"].forEach(h => { + const th = document.createElement("th"); + th.textContent = h; + headerRow.appendChild(th); + }); + thead.appendChild(headerRow); + table.appendChild(thead); + + // attach rows + const frag = document.createDocumentFragment(); + downloadableModels.forEach(m => { + const model = m[0]; + const tr = document.createElement("tr"); + + // first column: radio button and model name + const tdMain = document.createElement("td"); + const radioId = `downloadable_${model.replace(/\s+/g, "_")}`; + const radio = Object.assign(document.createElement("input"), { type: "radio", name: "model", value: model, id: radioId }); + const label = Object.assign(document.createElement("label"), { htmlFor: radioId, textContent: model }); + tdMain.appendChild(radio); + tdMain.appendChild(document.createTextNode(" ")); + tdMain.appendChild(label); + tr.appendChild(tdMain); + + // other columns + [m[1], m[2], m[3], m[4], m[5]].forEach(text => { + const td = document.createElement("td"); + td.textContent = text; + tr.appendChild(td); + }); + + frag.appendChild(tr); + }); + + // download button + const downloadModelBtn = document.createElement("button"); + downloadModelBtn.type = "button"; + downloadModelBtn.id = "downloadModelBtn"; + downloadModelBtn.className = "btn btn-primary"; + downloadModelBtn.textContent = "Download Model"; + downloadModelBtn.addEventListener("click", async () => { + const sel = loadModelContainer.querySelector('input[type="radio"][name="model"]:checked'); + model = sel ? sel.value : null; + if (!model) return alert("Please select a model to download."); + console.log("Download requested for model:", model); + alert(`Downloading model: ${model}`); + try { + const apiUrl = `${hoststub}/api/pull`; + const response = await fetch(apiUrl, { + method: "POST", + headers: {"Accept": "application/json", "Content-Type": "application/json"}, + body: JSON.stringify({ model: model, stream: false }) + }); + if (response.status !== 200) { + alert(`Failed to download model. HTTP status: ${response.status}`); + console.error('Non-200 response', response); + return; + } + const responsej = await response.json().catch(() => null); + if (responsej && responsej.error) { + console.error(`Error pulling model ${model} from server ${hoststub}.`, responsej); + return; + } else { + console.log(`Model ${model} is now available on server ${hoststub}.`); + return; + } + } catch (err) { + console.error("Error during model pull request:", err); + return; + } + }); + + const tbody = document.createElement("tbody"); + tbody.appendChild(frag); + table.appendChild(tbody); + + // attach table to loadModelContainer + loadModelContainer.appendChild(table); + loadModelContainer.style.display = "block"; + loadModelContainer.appendChild(downloadModelBtn); + } + } catch (error) { console.error("Error fetching models:", error); alert("Failed to load models. Check the hoststub and console for errors."); @@ -85,7 +215,7 @@ </dd> <dt class="TableCellDark">API Key</dt> - <dd><input type="text" name="apikey" id="apikey" value="" size="30" maxlength="60" class="form-control"/> (not required for Ollama or LMStudio) + <dd><input type="text" name="apikey" id="apikey" value="" disabled=true size="30" maxlength="60" class="form-control"/> (not required for Ollama or LMStudio) </dd> <dt class="TableCellDark">Max Token</dt> @@ -97,7 +227,7 @@ <option>32768</option> <option>65536</option> <option>131072</option> - <option>262.44</option> + <option>262440</option> </select> You must set the Context Length in the LLM provider to fit to your selected max token; in Ollama you find a Context Length slider in the settings </dd> @@ -108,7 +238,8 @@ </fieldset> </form> - <fieldset id="modelsContainer"><legend>Models</legend></fieldset> + <fieldset id="availableModelsContainer" style="display:none"></fieldset> + <fieldset id="loadModelContainer" style="display:none"></fieldset> <fieldset><legend>LLM List</legend> <table border="0" summary="Pack List Archive"> |
