summaryrefslogtreecommitdiff
path: root/htroot/LLMSelection_p.html
diff options
context:
space:
mode:
authorMichael Peter Christen <mc@yacy.net>2025-11-10 00:27:11 +0100
committerMichael Peter Christen <mc@yacy.net>2025-11-10 00:27:11 +0100
commit37c26bddf399fcf94e34cec110af023c8e35abbd (patch)
treecf253b341ffa3b6e535e60322e01b12cd43d6941 /htroot/LLMSelection_p.html
parent4fcf4d5eaa7544294cde0e9145e33a7e010fcb05 (diff)
added persistence to production models table
Diffstat (limited to 'htroot/LLMSelection_p.html')
-rw-r--r--htroot/LLMSelection_p.html137
1 files changed, 72 insertions, 65 deletions
diff --git a/htroot/LLMSelection_p.html b/htroot/LLMSelection_p.html
index ff95e98d3..4d3362a44 100644
--- a/htroot/LLMSelection_p.html
+++ b/htroot/LLMSelection_p.html
@@ -23,18 +23,18 @@
const PRODUCTION_MODEL_FEATURE_COLUMN_END = 11;
const PRODUCTION_MODEL_ACTION_COLUMN_INDEX = PRODUCTION_MODEL_TOTAL_COLUMNS - 1;
const PRODUCTION_MODEL_COLUMN_NAMES = [
- "Provider",
- "Model",
+ "service",
+ "model",
"hoststub",
- "API Key",
- "Max Token",
- "Answers",
- "Chat",
- "Translation",
- "Q/A-Generation",
- "Classification",
- "Shortener",
- "Vision"
+ "api_key",
+ "max_tokens",
+ "answers",
+ "chat",
+ "translation",
+ "qa-generation",
+ "classification",
+ "tldr-shortener",
+ "vision"
];
const PRODUCTION_MODEL_SUBMIT_URL = "LLMSelection_p.html";
@@ -121,20 +121,20 @@
return fetchJsonOrThrow(`${hoststub}/v1/models`);
}
- async function requestModelsForProvider(provider, hoststub) {
- return provider === "OLLAMA"
+ async function requestModelsForService(service, hoststub) {
+ return service === "OLLAMA"
? fetchOllamaModels(hoststub)
: fetchOpenAICompatibleModels(hoststub);
}
- function handleModelLoadError(provider, error) {
+ function handleModelLoadError(service, error) {
console.error("Error fetching models:", error);
const status = error && typeof error.status === "number" ? error.status : null;
if (status) {
const apikeyEl = document.getElementById("apikey");
apiKeyValue = apikeyEl ? apikeyEl.value.trim() : "";
- if (provider !== "OLLAMA" && provider !== "LMSTUDIO" && !apiKeyValue) {
- alert("an api key is required for this provider");
+ if (service !== "OLLAMA" && service !== "LMSTUDIO" && !apiKeyValue) {
+ alert("an api_key is required for this service");
} else {
alert(`Failed to load models. HTTP status: ${status}`);
}
@@ -145,13 +145,13 @@
async function loadModelList() {
availableModels = [];
- const provider = document.getElementById("provider").value;
+ const service = document.getElementById("service").value;
const hoststub = document.getElementById("hoststub").value;
try {
- const responsej = await requestModelsForProvider(provider, hoststub);
- renderAvailableModels(provider, responsej);
- if (provider === "OLLAMA") {
+ const responsej = await requestModelsForService(service, hoststub);
+ renderAvailableModels(service, responsej);
+ if (service === "OLLAMA") {
renderRecommendedModels(hoststub);
} else {
const loadModelContainer = document.getElementById("loadModelContainer");
@@ -160,7 +160,7 @@
loadModelContainer.style.display = "none";
}
} catch (error) {
- handleModelLoadError(provider, error);
+ handleModelLoadError(service, error);
}
}
@@ -249,18 +249,18 @@
***/
function setHoststub() {
- // this is called when the user changes the provider
- const provider = document.getElementById("provider").value;
+ // this is called when the user changes the service
+ const service = document.getElementById("service").value;
const hoststubInput = document.getElementById("hoststub");
const apikeyInput = document.getElementById("apikey");
- if (provider === "OLLAMA") {
+ if (service === "OLLAMA") {
hoststubInput.value = "http://localhost:11434";
- } else if (provider === "LMSTUDIO") {
+ } else if (service === "LMSTUDIO") {
hoststubInput.value = "http://localhost:1234";
- } else if (provider === "OPENAI") {
+ } else if (service === "OPENAI") {
hoststubInput.value = "https://api.openai.com";
- } else if (provider === "OPENROUTER") {
+ } else if (service === "OPENROUTER") {
hoststubInput.value = "https://openrouter.ai/api";
} else {
hoststubInput.value = "";
@@ -268,7 +268,7 @@
if (!apikeyInput) return;
- if (provider === "OLLAMA" || provider === "LMSTUDIO") {
+ if (service === "OLLAMA" || service === "LMSTUDIO") {
apikeyInput.disabled = true;
apikeyInput.value = "";
} else {
@@ -312,12 +312,12 @@
}
}
- function renderAvailableModels(provider, payload) {
+ function renderAvailableModels(service, payload) {
const container = document.getElementById("availableModelsContainer");
if (!container) return;
- const models = provider === "OLLAMA" ? (payload.models || []) : (payload.data || []);
- const getId = provider === "OLLAMA" ? m => m.model : m => m.id;
+ const models = service === "OLLAMA" ? (payload.models || []) : (payload.data || []);
+ const getId = service === "OLLAMA" ? m => m.model : m => m.id;
const rows = [];
models.forEach(m => {
@@ -330,9 +330,9 @@
ranking: info.ranking || "",
size: info.size || "",
description: info.description || "",
- provider: info.provider || "",
+ service: info.service || "",
license: info.license || "",
- renderActions: () => createAvailableModelActionButtons(provider, id)
+ renderActions: () => createAvailableModelActionButtons(service, id)
});
});
@@ -351,7 +351,7 @@
ranking: info.ranking || "",
size: info.size || "",
description: info.description || "",
- provider: info.provider || "",
+ service: info.service || "",
license: info.license || "",
renderActions: () => createDownloadButton(hoststub, m[0])
};
@@ -424,8 +424,8 @@
container.style.display = "block";
}
- function createAvailableModelActionButtons(provider, modelId) {
- return [createSelectButton(modelId), createDeleteButton(provider, modelId)];
+ function createAvailableModelActionButtons(service, modelId) {
+ return [createSelectButton(modelId), createDeleteButton(service, modelId)];
}
function createSelectButton(modelId) {
@@ -438,13 +438,13 @@
return selectBtn;
}
- function createDeleteButton(provider, modelId) {
+ function createDeleteButton(service, modelId) {
const deleteBtn = document.createElement("button");
deleteBtn.type = "button";
deleteBtn.className = "btn btn-danger btn-sm";
deleteBtn.textContent = "Delete";
styleActionButton(deleteBtn);
- if (provider === "OLLAMA") {
+ if (service === "OLLAMA") {
deleteBtn.addEventListener("click", () => {
const confirmed = window.confirm(`Do you really want to delete model "${modelId}"?`);
if (!confirmed) {
@@ -478,12 +478,12 @@
if (!tbody) return;
const hadRowsBeforeInsert = tbody.querySelectorAll("tr").length > 0;
- const providerField = document.getElementById("provider");
+ const serviceField = document.getElementById("service");
const hoststubField = document.getElementById("hoststub");
const apikeyField = document.getElementById("apikey");
const maxTokenField = document.getElementById("maxtoken");
- const provider = providerField ? providerField.value : "";
+ const service = serviceField ? serviceField.value : "";
const hoststub = hoststubField ? hoststubField.value.trim() : "";
const apikey = apikeyField ? apikeyField.value.trim() : "";
const maxToken = maxTokenField ? maxTokenField.value : "";
@@ -508,7 +508,7 @@
}
const cells = targetRow.cells;
- const values = [provider, modelName, hoststub, apikey, maxToken];
+ const values = [service, modelName, hoststub, apikey, maxToken];
values.forEach((value, index) => {
if (cells[index]) {
cells[index].textContent = value || "";
@@ -643,10 +643,11 @@
return false;
}
- function collectProductionTablePayload() {
+ function persistProductionModels() {
+ // read out table
const tbody = getProductionTableBody();
if (!tbody) return [];
- const rows = [];
+ const production_models_table = [];
Array.from(tbody.querySelectorAll("tr")).forEach(row => {
if (!row.cells || row.cells.length < PRODUCTION_MODEL_TOTAL_COLUMNS) {
return;
@@ -662,21 +663,16 @@
rowData[columnName] = row.cells[index] ? row.cells[index].textContent.trim() : "";
}
});
- const hasContent = rowData.Provider || rowData.Model || rowData["hoststub"];
+ const hasContent = rowData.service || rowData.model || rowData["hoststub"];
if (hasContent) {
- rows.push(rowData);
+ production_models_table.push(rowData);
}
});
- return rows;
- }
-
- function persistProductionModels() {
- const table = collectProductionTablePayload();
- const payload = { table };
+
+ // push to server
fetch(PRODUCTION_MODEL_SUBMIT_URL, {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify(payload)
+ method: "POST", headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ production_models: production_models_table })
}).catch(err => {
console.error("Failed to persist production models", err);
});
@@ -724,18 +720,18 @@
<h2>LLM Selection</h2>
<p>
- Here you can pick models from a LLM model provider to select them as production model.
+ Here you can pick models from a LLM model service to select them as production model.
In the "Production Models" - Matrix you can then assign each selected model a function inside YaCy
</p>
<p>
- <b>Install your local LLM provider!</b> You need either a local <a href="https://ollama.com/">ollama</a> or <a href="https://lmstudio.ai/">LM Studio</a> instance running on your local host or inside the intranet.
+ <b>Install your local LLM service!</b> You need either a local <a href="https://ollama.com/">ollama</a> or <a href="https://lmstudio.ai/">LM Studio</a> instance running on your local host or inside the intranet.
</p>
<form id="llmForm">
- <fieldset><legend>Provider</legend>
+ <fieldset><legend>Service Selection</legend>
<dl>
- <dt class="TableCellDark">Select a LLM Provider</dt>
+ <dt class="TableCellDark">service</dt>
<dd>
- <select name="provider" id="provider" class="form-control" onchange="setHoststub()">
+ <select name="service" id="service" class="form-control" onchange="setHoststub()">
<option value="OLLAMA" selected="selected">Ollama</option>
<option value="LMSTUDIO">LMStudio</option>
<option value="OPENAI">OpenAI</option>
@@ -743,15 +739,15 @@
</select>&nbsp; This makes a preset to the Hoststub value
</dd>
- <dt class="TableCellDark">Hoststub</dt>
+ <dt class="TableCellDark">hoststub</dt>
<dd><input type="text" name="hoststub" id="hoststub" value="http://localhost:11434" size="30" maxlength="60" class="form-control"/>&nbsp; you can probably leave this to the default value
</dd>
- <dt class="TableCellDark">API Key</dt>
+ <dt class="TableCellDark">api_key</dt>
<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>
+ <dt class="TableCellDark">max_tokens</dt>
<dd>
<select id="maxtoken" name="maxtoken" class="form-control">
<option selected="selected">4096</option>
@@ -761,7 +757,7 @@
<option>65536</option>
<option>131072</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
+ </select>&nbsp; You must set the Context Length in the LLM service to fit to your selected max_tokens; in Ollama you find a Context Length slider in the settings
</dd>
<dt>&nbsp;</dt>
@@ -781,11 +777,22 @@
<fieldset id="productionModelsContainer" style="display: block;"><legend>Production Models</legend>
<table class="table table-striped" id="productionModelsTable">
<thead class="thead-dark">
- <tr><td>Provider</td><td>Model</td><td>hoststub</td><td>API Key</td><td>Max Token</td><td>Answers</td><td>Chat</td><td>Translation</td><td>Q/A-Generation</td><td>Classification</td><td>Shortener</td><td>Vision</td><td>Actions</td></tr>
+ <tr><td>service</td><td>model</td><td>hoststub</td><td>api_key</td><td>max_tokens</td><td>answers</td><td>chat</td><td>translation</td><td>qa-generation</td><td>classification</td><td>shortener</td><td>vision</td><td>Actions</td></tr>
</thead>
<tbody>
#{productionmodels}#
- <tr class="TableCell#(dark)#Light::Dark#(/dark)#"><td>#[model]#</td><td>#[type]#</td><td>#[size]#</td><td>#[apikey]#</td><td>#[max_token]#</td><td>#[answers]#</td><td>#[chat]#</td><td>#[translation]#</td><td>#[qageneration]#</td><td>#[classification]#</td><td>#[shortener]#</td><td>#[vision]#</td><td></td></tr>
+ <tr class="TableCell#(dark)#Light::Dark#(/dark)#">
+ <td>#[service]#</td><td>#[model]#</td><td>#[hoststub]#</td><td>#[api_key]#</td><td>#[max_tokens]#</td>
+ <td><input type="checkbox" #(answers)#::checked=true#(/answers)#></td>
+ <td><input type="checkbox" #(chat)#::checked=true#(/chat)#></td>
+ <td><input type="checkbox" #(translation)#::checked=true#(/translation)#></td>
+ <td><input type="checkbox" #(qa-generation)#::checked=true#(/qa-generation)#></td>
+ <td><input type="checkbox" #(classification)#::checked=true#(/classification)#></td>
+ <td><input type="checkbox" #(shortener)#::checked=true#(/shortener)#></td>
+ <td><input type="checkbox" #(vision)#::checked=true#(/vision)#></td>
+ <td><input type="checkbox" #(chat)#::checked=true#(/chat)#></td>
+ <td></td>
+ </tr>
#{/productionmodels}#
</tbody>
</table>