summaryrefslogtreecommitdiff
path: root/htroot
diff options
context:
space:
mode:
authorMichael Peter Christen <mc@yacy.net>2025-11-07 00:30:55 +0100
committerMichael Peter Christen <mc@yacy.net>2025-11-07 00:30:55 +0100
commit3ea9276d823616757615712d687978552c31e327 (patch)
tree2bb65648744c25a7e7e311862808a358a32da122 /htroot
parent9888473d36e955d1a2799cb96fd203d2ea3d055b (diff)
added download option for recommended models in LLMSelection
(this is still a stub)
Diffstat (limited to 'htroot')
-rw-r--r--htroot/LLMSelection_p.html185
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"/>&nbsp; (not required for Ollama or LMStudio)
+ <dd><input type="text" name="apikey" id="apikey" value="" disabled=true size="30" maxlength="60" class="form-control"/>&nbsp; (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>&nbsp; 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">