diff options
| author | Michael Peter Christen <mc@yacy.net> | 2025-11-10 00:27:11 +0100 |
|---|---|---|
| committer | Michael Peter Christen <mc@yacy.net> | 2025-11-10 00:27:11 +0100 |
| commit | 37c26bddf399fcf94e34cec110af023c8e35abbd (patch) | |
| tree | cf253b341ffa3b6e535e60322e01b12cd43d6941 /htroot/LLMSelection_p.html | |
| parent | 4fcf4d5eaa7544294cde0e9145e33a7e010fcb05 (diff) | |
added persistence to production models table
Diffstat (limited to 'htroot/LLMSelection_p.html')
| -rw-r--r-- | htroot/LLMSelection_p.html | 137 |
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> 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"/> 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"/> (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> 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> 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> </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> |
