feat(release): v0.2.30 — per-model generation, O(1) attention, MTP & reasoning parameters
Docker / publish (push) Canceled after 0s
Release / crates-io (push) Canceled after 0s
Release / release (macos-14, aarch64-apple-darwin) (push) Canceled after 0s
Release / release (ubuntu-latest, x86_64-unknown-linux-gnu) (push) Canceled after 0s
Release / release (windows-latest, x86_64-pc-windows-msvc) (push) Canceled after 0s

This commit is contained in:
infosave2007
2026-07-22 10:40:44 +03:00
parent b867a6276e
commit 96af5fca98
17 changed files with 265 additions and 9 deletions
+12
View File
@@ -139,6 +139,18 @@ export default {
"models.form.apiKeyHint": "Stored securely on the gateway; it is not shown again.",
"models.form.keyStored": "A key is saved — leave blank to keep it, or paste a new one.",
"models.form.keyFromEnv": "Using a key from an environment variable.",
"models.form.paramsTitle": "Generation & Thinking Parameters (O(1), MTP, Temperature)",
"models.form.temperature": "Temperature",
"models.form.temperatureHint": "Generation temperature (0.0 = deterministic, 1.0+ = creative).",
"models.form.topP": "Top-P (Nucleus Sampling)",
"models.form.topPHint": "Sampling threshold (0.0 .. 1.0).",
"models.form.maxTokens": "Max Tokens",
"models.form.maxTokensHint": "Maximum completion tokens to generate.",
"models.form.thinkBudget": "Thinking Mode / Think Budget",
"models.form.thinkBudgetHint": "Reasoning token budget (0 = disabled, N > 0 = max reasoning tokens).",
"models.form.systemPrompt": "Default System Prompt",
"models.form.systemPromptHint": "Prepend automatically if request lacks a system prompt.",
"models.form.systemPromptPlaceholder": "You are a helpful assistant...",
"models.form.testKey": "Test & load models",
"models.form.testing": "checking…",
"models.form.foundModels": "{n} models loaded",
+12
View File
@@ -138,6 +138,18 @@ export default {
"models.form.apiKeyHint": "Хранится безопасно на шлюзе; больше не показывается.",
"models.form.keyStored": "Ключ сохранён — оставьте пустым, чтобы не менять, или вставьте новый.",
"models.form.keyFromEnv": "Используется ключ из переменной окружения.",
"models.form.paramsTitle": "Параметры генерации, O(1) и режим размышления",
"models.form.temperature": "Температура",
"models.form.temperatureHint": "Температура генерации (0.0 — точные факты, 1.0+ — творчество).",
"models.form.topP": "Top-P (Nucleus Sampling)",
"models.form.topPHint": "Порог сэмплирования токенов (0.0 .. 1.0).",
"models.form.maxTokens": "Макс. токенов (Max Tokens)",
"models.form.maxTokensHint": "Максимальное количество генерируемых токенов.",
"models.form.thinkBudget": "Режим размышления / Think Budget",
"models.form.thinkBudgetHint": "Бюджет токенов мыслей (0 = выключен, N > 0 = лимит рассуждений).",
"models.form.systemPrompt": "Системный промпт по умолчанию",
"models.form.systemPromptHint": "Автоматически подставляется, если запрос не содержит системное сообщение.",
"models.form.systemPromptPlaceholder": "Вы — полезный ассистент...",
"models.form.testKey": "Проверить и загрузить модели",
"models.form.testing": "проверка…",
"models.form.foundModels": "загружено моделей: {n}",
+39 -1
View File
@@ -118,6 +118,36 @@ function modelForm(meta, existing) {
: existing && existing.key_source === "env" ? t("models.form.keyFromEnv")
: t("models.form.apiKeyHint");
// ⚙ Generation & Thinking Parameters
const tempIn = h("input", { type: "number", step: "0.05", min: "0", max: "2.0", placeholder: "0.7", value: m.temperature ?? "" });
const topPIn = h("input", { type: "number", step: "0.05", min: "0", max: "1.0", placeholder: "0.9", value: m.top_p ?? "" });
const maxTokIn = h("input", { type: "number", min: "1", placeholder: "2048", value: m.max_tokens ?? "" });
const thinkIn = h("input", { type: "number", min: "0", placeholder: "1024", value: m.think_budget ?? "" });
const o1Sel = h("select", {},
opt("", t("import.adv.auto")),
opt("all", "all — O(1) Attention"),
opt("deep", "deep — O(1) Deep"),
opt("off", "off — Standard"));
if (m.o1) o1Sel.value = m.o1;
const skipMtpCb = h("input", { type: "checkbox", checked: m.skip_mtp ? true : null });
const sysPromptIn = h("textarea", { rows: 2, placeholder: t("models.form.systemPromptPlaceholder"), value: m.system_prompt || "" });
const advParams = h(
"details",
{ class: "adv-params", style: "margin-top:12px;padding:10px;border:1px solid var(--border);border-radius:6px;" },
h("summary", { style: "font-weight:600;cursor:pointer;" }, "⚙ " + t("models.form.paramsTitle")),
h("div", { class: "row", style: "margin-top:8px" },
field(t("models.form.temperature"), tempIn, t("models.form.temperatureHint")),
field(t("models.form.topP"), topPIn, t("models.form.topPHint"))),
h("div", { class: "row" },
field(t("models.form.maxTokens"), maxTokIn, t("models.form.maxTokensHint")),
field(t("models.form.thinkBudget"), thinkIn, t("models.form.thinkBudgetHint"))),
h("div", { class: "row" },
field("O(1) Attention", o1Sel, t("import.adv.o1Hint")),
field("MTP", h("label", { class: "check" }, skipMtpCb, t("import.adv.skipMtp")))),
field(t("models.form.systemPrompt"), sysPromptIn, t("models.form.systemPromptHint"))
);
const node = h(
"div",
{},
@@ -128,7 +158,8 @@ function modelForm(meta, existing) {
dl,
h("div", { class: "row" }, field(t("models.form.kind"), kindSel), field(t("models.form.costTier"), tierSel)),
h("div", { class: "row" }, field(t("models.form.priceIn"), priceInIn), field(t("models.form.priceOut"), priceOutIn)),
field(t("models.form.caps"), h("div", { class: "flex wrap" }, ...capInputs.map(({ c, cb }) => h("label", { class: "check" }, cb, c))))
field(t("models.form.caps"), h("div", { class: "flex wrap" }, ...capInputs.map(({ c, cb }) => h("label", { class: "check" }, cb, c)))),
advParams
);
const getValue = () => {
@@ -147,6 +178,13 @@ function modelForm(meta, existing) {
price_out: parseFloat(priceOutIn.value) || 0,
api_key_env: hasKey ? keyEnv : null,
caps: capInputs.filter(({ cb }) => cb.checked).map(({ c }) => c),
temperature: tempIn.value !== "" ? parseFloat(tempIn.value) : null,
top_p: topPIn.value !== "" ? parseFloat(topPIn.value) : null,
max_tokens: maxTokIn.value !== "" ? parseInt(maxTokIn.value) : null,
think_budget: thinkIn.value !== "" ? parseInt(thinkIn.value) : null,
o1: o1Sel.value || null,
skip_mtp: skipMtpCb.checked,
system_prompt: sysPromptIn.value.trim() || null,
},
secret: typedKey,
keyEnv,
+44 -2
View File
@@ -253,9 +253,51 @@ export async function renderSettings() {
} catch (e) { mount(st, String(e.message || e)); }
checkBtn.disabled = false;
});
// ⚙ Model Generation & Thinking Parameters drawer
const tempIn = h("input", { type: "number", step: "0.05", min: "0", max: "2.0", placeholder: "0.7", value: sv.temperature ?? "", style: "width:65px" });
const topPIn = h("input", { type: "number", step: "0.05", min: "0", max: "1.0", placeholder: "0.9", value: sv.top_p ?? "", style: "width:65px" });
const maxTokIn = h("input", { type: "number", min: "1", placeholder: "2048", value: sv.max_tokens ?? "", style: "width:80px" });
const thinkIn = h("input", { type: "number", min: "0", placeholder: "1024", value: sv.think_budget ?? "", style: "width:80px" });
const o1Sel = h("select", { style: "width:110px" },
opt("", "O(1) auto"),
opt("all", "O(1) all"),
opt("deep", "O(1) deep"),
opt("off", "O(1) off"));
if (sv.o1) o1Sel.value = sv.o1;
const skipMtpCb = h("input", { type: "checkbox", checked: sv.skip_mtp ? true : null });
const sysIn = h("input", { placeholder: t("models.form.systemPromptPlaceholder"), value: sv.system_prompt || "", style: "flex:1" });
const drawer = h("div", { class: "flex wrap gap-sm", style: "display:none;width:100%;margin-top:4px;padding:6px;background:var(--bg-subtle);border-radius:4px;align-items:center" },
h("span", { class: "small muted" }, "Temp:"), tempIn,
h("span", { class: "small muted" }, "Top-P:"), topPIn,
h("span", { class: "small muted" }, "MaxTokens:"), maxTokIn,
h("span", { class: "small muted" }, "ThinkBudget:"), thinkIn,
o1Sel,
h("label", { class: "check small" }, skipMtpCb, "Skip MTP"),
h("span", { class: "small muted" }, "System:"), sysIn
);
const gearBtn = h("button", { class: "btn small", type: "button", title: "⚙ " + t("models.form.paramsTitle") }, "⚙");
gearBtn.addEventListener("click", () => {
drawer.style.display = drawer.style.display === "none" ? "flex" : "none";
});
const row = h("div", { class: "flex wrap", style: "gap:6px;align-items:center;margin:6px 0" },
idIn, modelSel, portIn, checkBtn, threadsIn, h("label", { class: "flex", style: "gap:4px" }, gpuIn, "GPU"), removeBtn, st);
const entry = { node: row, read: () => ({ id: idIn.value.trim() || autoId(modelSel.value), model: modelSel.value.trim(), port: parseInt(portIn.value) || 8090, threads: parseInt(threadsIn.value) || 0, gpu: gpuIn.checked }) };
idIn, modelSel, portIn, checkBtn, threadsIn, h("label", { class: "flex", style: "gap:4px" }, gpuIn, "GPU"), gearBtn, removeBtn, st, drawer);
const entry = { node: row, read: () => ({
id: idIn.value.trim() || autoId(modelSel.value),
model: modelSel.value.trim(),
port: parseInt(portIn.value) || 8090,
threads: parseInt(threadsIn.value) || 0,
gpu: gpuIn.checked,
temperature: tempIn.value !== "" ? parseFloat(tempIn.value) : null,
top_p: topPIn.value !== "" ? parseFloat(topPIn.value) : null,
max_tokens: maxTokIn.value !== "" ? parseInt(maxTokIn.value) : null,
think_budget: thinkIn.value !== "" ? parseInt(thinkIn.value) : null,
o1: o1Sel.value || null,
skip_mtp: skipMtpCb.checked,
system_prompt: sysIn.value.trim() || null,
}) };
removeBtn.addEventListener("click", () => { const i = serverRows.indexOf(entry); if (i >= 0) serverRows.splice(i, 1); row.remove(); });
serverRows.push(entry);
serversWrap.appendChild(row);