summaryrefslogtreecommitdiff
path: root/htroot/LLMSelection_p.html
diff options
context:
space:
mode:
authorMichael Peter Christen <mc@yacy.net>2025-11-11 23:15:40 +0100
committerMichael Peter Christen <mc@yacy.net>2025-11-11 23:15:40 +0100
commit0887619a1f0ddc697dfc188aca60115783612037 (patch)
treead9e0e1f424336874f0b9d7011ebeb750365a99d /htroot/LLMSelection_p.html
parent576d3b26574efc6032c9ddf138caee561d7a4c9f (diff)
distinguish between usage columns and feature columns, added tooling
Diffstat (limited to 'htroot/LLMSelection_p.html')
-rw-r--r--htroot/LLMSelection_p.html101
1 files changed, 42 insertions, 59 deletions
diff --git a/htroot/LLMSelection_p.html b/htroot/LLMSelection_p.html
index 9265343c0..22f5d1756 100644
--- a/htroot/LLMSelection_p.html
+++ b/htroot/LLMSelection_p.html
@@ -17,10 +17,12 @@
event.preventDefault();
event.returnValue = "Model downloads are still running. Please wait until they finish.";
};
- const PRODUCTION_MODEL_TOTAL_COLUMNS = 13;
+ const PRODUCTION_MODEL_TOTAL_COLUMNS = 14;
const PRODUCTION_MODEL_MODEL_COLUMN_INDEX = 1;
- const PRODUCTION_MODEL_FEATURE_COLUMN_START = 5;
- const PRODUCTION_MODEL_FEATURE_COLUMN_END = 11;
+ const PRODUCTION_MODEL_USAGE_COLUMN_START = 5;
+ const PRODUCTION_MODEL_USAGE_COLUMN_END = 10; // including
+ const PRODUCTION_MODEL_FEATURE_COLUMN_START = 11;
+ const PRODUCTION_MODEL_FEATURE_COLUMN_END = 12; // including
const PRODUCTION_MODEL_ACTION_COLUMN_INDEX = PRODUCTION_MODEL_TOTAL_COLUMNS - 1;
const PRODUCTION_MODEL_COLUMN_NAMES = [
"service",
@@ -34,6 +36,7 @@
"qa-generation",
"classification",
"tldr-shortener",
+ "tooling",
"vision"
];
const PRODUCTION_MODEL_SUBMIT_URL = "LLMSelection_p.html";
@@ -79,10 +82,6 @@
return response.json();
}
- async function fetchOllamaModels(hoststub) {
- return fetchJsonOrThrow(`${hoststub}/api/tags`);
- }
-
async function deleteOllamaModel(hoststub, modelName) {
const response = await fetch(`${hoststub}/api/delete`, {
method: "DELETE",
@@ -117,14 +116,8 @@
return payload;
}
- async function fetchOpenAICompatibleModels(hoststub) {
- return fetchJsonOrThrow(`${hoststub}/v1/models`);
- }
-
async function requestModelsForService(service, hoststub) {
- return service === "OLLAMA"
- ? fetchOllamaModels(hoststub)
- : fetchOpenAICompatibleModels(hoststub);
+ return service === "OLLAMA" ? fetchJsonOrThrow(`${hoststub}/api/tags`) : fetchJsonOrThrow(`${hoststub}/v1/models`);
}
function handleModelLoadError(service, error) {
@@ -278,7 +271,7 @@
async function handleModelDelete(modelName, deleteButton) {
if (!modelName) return;
- if (isModelInProduction(modelName)) {
+ if (getProductionModelNames().has(modelName)) {
alert(`Model "${modelName}" is currently used in the production table and cannot be deleted.`);
updateAvailableModelButtons();
return;
@@ -450,11 +443,6 @@
return modelNames;
}
- function isModelInProduction(modelName) {
- if (!modelName) return false;
- return getProductionModelNames().has(modelName);
- }
-
function updateAvailableModelButtons() {
const productionModels = getProductionModelNames();
document.querySelectorAll('button[data-action="delete-model"]').forEach(button => {
@@ -572,8 +560,7 @@
}
});
- const shouldEnableFeatures = !hadRowsBeforeInsert;
- ensureProductionRowFeatureCells(targetRow, shouldEnableFeatures);
+ ensureProductionRowUsageCells(targetRow, !hadRowsBeforeInsert);
ensureProductionRowActionButton(targetRow);
persistProductionModels();
}
@@ -591,15 +578,16 @@
for (let i = 0; i < missingCells; i += 1) {
row.appendChild(document.createElement("td"));
}
- ensureProductionRowFeatureCells(row, false);
+ ensureProductionRowUsageCells(row, false);
ensureProductionRowActionButton(row);
});
updateAvailableModelButtons();
}
- function ensureProductionRowFeatureCells(row, defaultChecked) {
+ function ensureProductionRowUsageCells(row, defaultChecked) {
if (!row) return;
- for (let col = PRODUCTION_MODEL_FEATURE_COLUMN_START; col <= PRODUCTION_MODEL_FEATURE_COLUMN_END; col += 1) {
+ // ensure existence of checkboxes
+ for (let col = PRODUCTION_MODEL_USAGE_COLUMN_START; col <= PRODUCTION_MODEL_FEATURE_COLUMN_END; col += 1) {
const cell = row.cells[col];
if (!cell) continue;
let checkbox = cell.querySelector('input[type="checkbox"]');
@@ -609,28 +597,40 @@
cell.textContent = "";
cell.appendChild(checkbox);
checkbox.checked = !!defaultChecked;
+ if (col >= PRODUCTION_MODEL_FEATURE_COLUMN_START) {
+ // disable the checkbox
+ checkbox.disabled = true
+ }
}
- initializeFeatureCheckbox(checkbox, col);
+ initializeUsageCheckbox(checkbox, col);
}
if (defaultChecked) {
- enforceFeatureExclusivityForRow(row);
+ // enforce feature exclusivity for row
+ if (!row) return;
+ for (let col = PRODUCTION_MODEL_USAGE_COLUMN_START; col <= PRODUCTION_MODEL_USAGE_COLUMN_END; col += 1) {
+ const cell = row.cells[col];
+ if (!cell) continue;
+ const checkbox = cell.querySelector('input[type="checkbox"]');
+ if (!checkbox || !checkbox.checked) continue;
+ handleUsageCheckboxToggle(col, checkbox);
+ }
}
updateUndeployButtonState(row);
}
- function initializeFeatureCheckbox(checkbox, columnIndex) {
+ function initializeUsageCheckbox(checkbox, col) {
if (!checkbox) return;
- checkbox.dataset.featureColumn = String(columnIndex);
+ checkbox.dataset.featureColumn = String(col);
if (checkbox.dataset.listenerAttached === "true") {
return;
}
checkbox.addEventListener("change", event => {
- handleFeatureCheckboxToggle(columnIndex, event.currentTarget);
+ handleUsageCheckboxToggle(col, event.currentTarget);
});
checkbox.dataset.listenerAttached = "true";
}
- function handleFeatureCheckboxToggle(columnIndex, checkbox) {
+ function handleUsageCheckboxToggle(columnIndex, checkbox) {
if (!checkbox) return;
const tbody = getProductionTableBody();
if (!tbody) return;
@@ -655,17 +655,6 @@
persistProductionModels();
}
- function enforceFeatureExclusivityForRow(row) {
- if (!row) return;
- for (let col = PRODUCTION_MODEL_FEATURE_COLUMN_START; col <= PRODUCTION_MODEL_FEATURE_COLUMN_END; col += 1) {
- const cell = row.cells[col];
- if (!cell) continue;
- const checkbox = cell.querySelector('input[type="checkbox"]');
- if (!checkbox || !checkbox.checked) continue;
- handleFeatureCheckboxToggle(col, checkbox);
- }
- }
-
function ensureProductionRowActionButton(row) {
if (!row || row.cells.length < PRODUCTION_MODEL_TOTAL_COLUMNS) {
return;
@@ -691,23 +680,18 @@
if (!row) return;
const button = row.querySelector('button[data-action="undeploy"]');
if (!button) return;
- const hasFeatureChecked = isAnyFeatureCheckboxChecked(row);
- button.disabled = hasFeatureChecked;
- button.title = hasFeatureChecked
- ? "Disable the assigned features before undeploying."
- : "";
- }
-
- function isAnyFeatureCheckboxChecked(row) {
- for (let col = PRODUCTION_MODEL_FEATURE_COLUMN_START; col <= PRODUCTION_MODEL_FEATURE_COLUMN_END; col += 1) {
+ hasFeatureChecked = false;
+ for (let col = PRODUCTION_MODEL_USAGE_COLUMN_START; col <= PRODUCTION_MODEL_USAGE_COLUMN_END; col += 1) {
const cell = row.cells[col];
if (!cell) continue;
const checkbox = cell.querySelector('input[type="checkbox"]');
if (checkbox && checkbox.checked) {
- return true;
+ hasFeatureChecked = true;
+ break;
}
}
- return false;
+ button.disabled = hasFeatureChecked;
+ button.title = hasFeatureChecked ? "Disable the assigned features before undeploying." : "";
}
function persistProductionModels() {
@@ -721,10 +705,8 @@
}
const rowData = {};
PRODUCTION_MODEL_COLUMN_NAMES.forEach((columnName, index) => {
- if (index >= PRODUCTION_MODEL_FEATURE_COLUMN_START && index <= PRODUCTION_MODEL_FEATURE_COLUMN_END) {
- const checkbox = row.cells[index]
- ? row.cells[index].querySelector('input[type="checkbox"]')
- : null;
+ if (index >= PRODUCTION_MODEL_USAGE_COLUMN_START && index <= PRODUCTION_MODEL_FEATURE_COLUMN_END) {
+ const checkbox = row.cells[index] ? row.cells[index].querySelector('input[type="checkbox"]') : null;
rowData[columnName] = checkbox ? checkbox.checked : false;
} else {
rowData[columnName] = row.cells[index] ? row.cells[index].textContent.trim() : "";
@@ -845,7 +827,7 @@
<fieldset id="productionModelsContainer" style="display: block;"><legend>Production Models</legend>
<table class="table table-striped" id="productionModelsTable">
<thead class="thead-dark">
- <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>
+ <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><i>tooling</i></td><td><i>vision</i></td><td>Actions</td></tr>
</thead>
<tbody>
#{productionmodels}#
@@ -857,7 +839,8 @@
<td><input type="checkbox" #(qa-generation)#::checked=true#(/qa-generation)#></td>
<td><input type="checkbox" #(classification)#::checked=true#(/classification)#></td>
<td><input type="checkbox" #(tldr-shortener)#::checked=true#(/tldr-shortener)#></td>
- <td><input type="checkbox" #(vision)#::checked=true#(/vision)#></td>
+ <td><input type="checkbox" #(tooling)#::checked=true#(/tooling)# disabled=true></td>
+ <td><input type="checkbox" #(vision)#::checked=true#(/vision)# disabled=true></td>
<td></td>
</tr>
#{/productionmodels}#