mirror of
https://github.com/yacy/yacy_search_server.git
synced 2026-10-25 05:54:15 +00:00
refactoring - both model lists now tables
This commit is contained in:
1 parent
2b9d6413a7
commit
fa037b467f
1 file changed
+121
-107
+121
-107
@@ -35,7 +35,15 @@
|
||||
["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"]
|
||||
];
|
||||
|
||||
|
||||
const MODEL_TABLE_HEADERS = ["Model", "Ranking", "Size", "Description", "Provider", "License", "Actions"];
|
||||
const RECOMMENDED_MODEL_MAP = new Map(
|
||||
RECOMMENDED_MODELS.map(([name, ranking, size, description, provider, license]) => [
|
||||
name,
|
||||
{ ranking, size, description, provider, license }
|
||||
])
|
||||
);
|
||||
|
||||
|
||||
/***
|
||||
*** API functions to access Ollama or OpenAI endpoints (list/load/delete models)
|
||||
***/
|
||||
@@ -284,83 +292,64 @@
|
||||
}
|
||||
|
||||
function renderAvailableModels(provider, payload) {
|
||||
// called when the model list was loaded
|
||||
const availableModelsContainer = document.getElementById("availableModelsContainer");
|
||||
if (!availableModelsContainer) return;
|
||||
|
||||
availableModelsContainer.innerHTML = "<legend>Available Models</legend>";
|
||||
availableModelsContainer.style.display = "none";
|
||||
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 frag = document.createDocumentFragment();
|
||||
|
||||
const table = document.createElement("table");
|
||||
table.className = "table table-striped";
|
||||
|
||||
const thead = document.createElement("thead");
|
||||
thead.className = "thead-dark";
|
||||
const headerRow = document.createElement("tr");
|
||||
["Model", "Actions"].forEach(h => {
|
||||
const th = document.createElement("th");
|
||||
th.textContent = h;
|
||||
headerRow.appendChild(th);
|
||||
});
|
||||
thead.appendChild(headerRow);
|
||||
table.appendChild(thead);
|
||||
|
||||
const tbody = document.createElement("tbody");
|
||||
const rows = [];
|
||||
|
||||
models.forEach(m => {
|
||||
const id = getId(m);
|
||||
if (!id) return;
|
||||
availableModels.push(id);
|
||||
|
||||
const tr = document.createElement("tr");
|
||||
|
||||
const tdMain = document.createElement("td");
|
||||
tdMain.textContent = id;
|
||||
tr.appendChild(tdMain);
|
||||
|
||||
const actionsTd = document.createElement("td");
|
||||
const deleteBtn = document.createElement("button");
|
||||
deleteBtn.type = "button";
|
||||
deleteBtn.className = "btn btn-danger btn-sm";
|
||||
deleteBtn.textContent = "Delete";
|
||||
deleteBtn.style.padding = "2px 8px";
|
||||
deleteBtn.style.lineHeight = "1.2";
|
||||
if (provider === "OLLAMA") {
|
||||
deleteBtn.addEventListener("click", () => handleModelDelete(id, deleteBtn));
|
||||
} else {
|
||||
deleteBtn.disabled = true;
|
||||
deleteBtn.title = "Model management is only supported for Ollama.";
|
||||
}
|
||||
actionsTd.appendChild(deleteBtn);
|
||||
tr.appendChild(actionsTd);
|
||||
|
||||
tbody.appendChild(tr);
|
||||
const info = getRecommendedModelInfo(id) || {};
|
||||
rows.push({
|
||||
model: id,
|
||||
ranking: info.ranking || "",
|
||||
size: info.size || "",
|
||||
description: info.description || "",
|
||||
provider: info.provider || "",
|
||||
license: info.license || "",
|
||||
renderActions: () => createDeleteButton(provider, id)
|
||||
});
|
||||
});
|
||||
|
||||
if (tbody.hasChildNodes()) {
|
||||
table.appendChild(tbody);
|
||||
frag.appendChild(table);
|
||||
}
|
||||
|
||||
const hasModels = tbody.hasChildNodes();
|
||||
availableModelsContainer.appendChild(frag);
|
||||
availableModelsContainer.style.display = hasModels ? "block" : "none";
|
||||
renderModelTable(container, "Available Models", rows);
|
||||
}
|
||||
|
||||
function renderRecommendedModels(hoststub) {
|
||||
// called when the model list was loaded and the diff to the recommended model list is displayed
|
||||
const loadModelContainer = document.getElementById("loadModelContainer");
|
||||
if (!loadModelContainer) return;
|
||||
|
||||
loadModelContainer.innerHTML = "<legend>Recommended Models</legend>";
|
||||
loadModelContainer.style.display = "none";
|
||||
|
||||
const downloadableModels = RECOMMENDED_MODELS.filter(m => m && !availableModels.includes(m[0]));
|
||||
if (!downloadableModels.length) return;
|
||||
const rows = downloadableModels.map(m => {
|
||||
const info = getRecommendedModelInfo(m[0]) || {};
|
||||
return {
|
||||
model: m[0],
|
||||
ranking: info.ranking || "",
|
||||
size: info.size || "",
|
||||
description: info.description || "",
|
||||
provider: info.provider || "",
|
||||
license: info.license || "",
|
||||
renderActions: () => createDownloadButton(hoststub, m[0])
|
||||
};
|
||||
});
|
||||
|
||||
renderModelTable(loadModelContainer, "Recommended Models", rows);
|
||||
}
|
||||
|
||||
function getRecommendedModelInfo(modelName) {
|
||||
return RECOMMENDED_MODEL_MAP.get(modelName) || null;
|
||||
}
|
||||
|
||||
function renderModelTable(container, title, rows) {
|
||||
if (!container) return;
|
||||
container.innerHTML = `<legend>${title}</legend>`;
|
||||
if (!rows || !rows.length) {
|
||||
container.style.display = "none";
|
||||
return;
|
||||
}
|
||||
|
||||
const table = document.createElement("table");
|
||||
table.className = "table table-striped";
|
||||
@@ -368,7 +357,7 @@
|
||||
const thead = document.createElement("thead");
|
||||
thead.className = "thead-dark";
|
||||
const headerRow = document.createElement("tr");
|
||||
["Model", "Ranking", "Size", "Description", "Provider", "License", "Actions"].forEach(h => {
|
||||
MODEL_TABLE_HEADERS.forEach(h => {
|
||||
const th = document.createElement("th");
|
||||
th.textContent = h;
|
||||
headerRow.appendChild(th);
|
||||
@@ -376,63 +365,88 @@
|
||||
thead.appendChild(headerRow);
|
||||
table.appendChild(thead);
|
||||
|
||||
const frag = document.createDocumentFragment();
|
||||
downloadableModels.forEach(m => {
|
||||
const modelName = m[0];
|
||||
const tbody = document.createElement("tbody");
|
||||
rows.forEach(row => {
|
||||
const tr = document.createElement("tr");
|
||||
|
||||
const tdMain = document.createElement("td");
|
||||
tdMain.textContent = modelName;
|
||||
tr.appendChild(tdMain);
|
||||
|
||||
[m[1], m[2], m[3], m[4], m[5]].forEach(text => {
|
||||
const columnValues = [
|
||||
row.model || "",
|
||||
row.ranking || "",
|
||||
row.size || "",
|
||||
row.description || "",
|
||||
row.provider || "",
|
||||
row.license || ""
|
||||
];
|
||||
columnValues.forEach(value => {
|
||||
const td = document.createElement("td");
|
||||
td.textContent = text;
|
||||
td.textContent = value;
|
||||
tr.appendChild(td);
|
||||
});
|
||||
|
||||
const actionsTd = document.createElement("td");
|
||||
const downloadBtn = document.createElement("button");
|
||||
downloadBtn.type = "button";
|
||||
downloadBtn.className = "btn btn-primary btn-sm";
|
||||
downloadBtn.textContent = "Download";
|
||||
downloadBtn.addEventListener("click", async () => {
|
||||
const activityId = addDownloadActivity(modelName);
|
||||
downloadBtn.disabled = true;
|
||||
try {
|
||||
await downloadOllamaModel(hoststub, modelName);
|
||||
console.log(`Model ${modelName} is now available on server ${hoststub}.`);
|
||||
} catch (err) {
|
||||
const status = err && typeof err.status === "number" ? err.status : null;
|
||||
const message = status
|
||||
? `Failed to download model ${modelName}. HTTP status: ${status}`
|
||||
: `Error pulling model ${modelName} from server ${hoststub}. Check the console for details.`;
|
||||
alert(message);
|
||||
console.error("Error during model pull request:", err);
|
||||
} finally {
|
||||
downloadBtn.disabled = false;
|
||||
if (activityId) {
|
||||
removeDownloadActivity(activityId);
|
||||
}
|
||||
try {
|
||||
await loadModelList();
|
||||
} catch (refreshError) {
|
||||
console.error("Failed to refresh models after download:", refreshError);
|
||||
}
|
||||
if (typeof row.renderActions === "function") {
|
||||
const actionContent = row.renderActions();
|
||||
if (Array.isArray(actionContent)) {
|
||||
actionContent.forEach(node => node && actionsTd.appendChild(node));
|
||||
} else if (actionContent instanceof Node) {
|
||||
actionsTd.appendChild(actionContent);
|
||||
}
|
||||
});
|
||||
actionsTd.appendChild(downloadBtn);
|
||||
}
|
||||
tr.appendChild(actionsTd);
|
||||
|
||||
frag.appendChild(tr);
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
|
||||
const tbody = document.createElement("tbody");
|
||||
tbody.appendChild(frag);
|
||||
table.appendChild(tbody);
|
||||
container.appendChild(table);
|
||||
container.style.display = "block";
|
||||
}
|
||||
|
||||
loadModelContainer.appendChild(table);
|
||||
loadModelContainer.style.display = "block";
|
||||
function createDeleteButton(provider, modelId) {
|
||||
const deleteBtn = document.createElement("button");
|
||||
deleteBtn.type = "button";
|
||||
deleteBtn.className = "btn btn-danger btn-sm";
|
||||
deleteBtn.textContent = "Delete";
|
||||
deleteBtn.style.padding = "2px 8px";
|
||||
deleteBtn.style.lineHeight = "1.2";
|
||||
if (provider === "OLLAMA") {
|
||||
deleteBtn.addEventListener("click", () => handleModelDelete(modelId, deleteBtn));
|
||||
} else {
|
||||
deleteBtn.disabled = true;
|
||||
deleteBtn.title = "Model management is only supported for Ollama.";
|
||||
}
|
||||
return deleteBtn;
|
||||
}
|
||||
|
||||
function createDownloadButton(hoststub, modelName) {
|
||||
const downloadBtn = document.createElement("button");
|
||||
downloadBtn.type = "button";
|
||||
downloadBtn.className = "btn btn-primary btn-sm";
|
||||
downloadBtn.textContent = "Download";
|
||||
downloadBtn.addEventListener("click", async () => {
|
||||
const activityId = addDownloadActivity(modelName);
|
||||
downloadBtn.disabled = true;
|
||||
try {
|
||||
await downloadOllamaModel(hoststub, modelName);
|
||||
console.log(`Model ${modelName} is now available on server ${hoststub}.`);
|
||||
} catch (err) {
|
||||
const status = err && typeof err.status === "number" ? err.status : null;
|
||||
const message = status
|
||||
? `Failed to download model ${modelName}. HTTP status: ${status}`
|
||||
: `Error pulling model ${modelName} from server ${hoststub}. Check the console for details.`;
|
||||
alert(message);
|
||||
console.error("Error during model pull request:", err);
|
||||
} finally {
|
||||
downloadBtn.disabled = false;
|
||||
if (activityId) {
|
||||
removeDownloadActivity(activityId);
|
||||
}
|
||||
try {
|
||||
await loadModelList();
|
||||
} catch (refreshError) {
|
||||
console.error("Failed to refresh models after download:", refreshError);
|
||||
}
|
||||
}
|
||||
});
|
||||
return downloadBtn;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
Reference in new issue
Block a user