diff options
| author | Michael Peter Christen <mc@yacy.net> | 2025-11-11 23:15:40 +0100 |
|---|---|---|
| committer | Michael Peter Christen <mc@yacy.net> | 2025-11-11 23:15:40 +0100 |
| commit | 0887619a1f0ddc697dfc188aca60115783612037 (patch) | |
| tree | ad9e0e1f424336874f0b9d7011ebeb750365a99d /htroot/LLMSelection_p.html | |
| parent | 576d3b26574efc6032c9ddf138caee561d7a4c9f (diff) | |
distinguish between usage columns and feature columns, added tooling
Diffstat (limited to 'htroot/LLMSelection_p.html')
| -rw-r--r-- | htroot/LLMSelection_p.html | 101 |
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}# |
