mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-10 17:23:25 +08:00
feat(models): add toggle to hide chat model selector
Expose a model-management setting to control chat-page model dropdown visibility, persist it in app settings, and apply it in chat UI rendering. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
61d2b87a30
commit
6628cd1220
3 changed files with 52 additions and 0 deletions
|
|
@ -322,6 +322,8 @@ const I18N = {
|
|||
"models.sectionBindings": "智能体与模型绑定",
|
||||
"models.sectionNew": "新建模型配置",
|
||||
"models.sectionApi": "API 与密钥",
|
||||
"models.chatModelSelector": "聊天页模型选择器",
|
||||
"models.chatModelSelectorHint": "控制 Chat 页底部的模型下拉是否显示。",
|
||||
"models.sectionEval": "Agent Evaluation",
|
||||
"models.pickModel": "选用配置",
|
||||
"models.agentBindingHelp": "可为不同角色指定模型配置;留空则跟随上方全局选用。",
|
||||
|
|
@ -763,6 +765,8 @@ const I18N = {
|
|||
"models.sectionBindings": "Agent ↔ profile bindings",
|
||||
"models.sectionNew": "New profile",
|
||||
"models.sectionApi": "API & secret",
|
||||
"models.chatModelSelector": "Chat model selector",
|
||||
"models.chatModelSelectorHint": "Control whether the model dropdown is visible on the Chat page.",
|
||||
"models.sectionEval": "Agent Evaluation",
|
||||
"models.pickModel": "Profile",
|
||||
"models.agentBindingHelp": "Optional per-role profile; leave empty to use the global selection above.",
|
||||
|
|
@ -3460,6 +3464,7 @@ async function renderApiGrants() {
|
|||
async function renderModels() {
|
||||
const canPickActive = hasPermission("admin:read");
|
||||
const canConfigureBindings = canPickActive;
|
||||
const canConfigureChatUi = hasPermission("admin:tenant:write");
|
||||
const status = el("div", { class: "muted", text: "" });
|
||||
const isAdminPool = String((authSession && authSession.username) || "").trim().toLowerCase() === "administrator";
|
||||
// 与后端 _require_models_mutate 对齐:administrator 写全局池需 tenant:write;普通用户写自己的配置只需 admin:read
|
||||
|
|
@ -3469,6 +3474,7 @@ async function renderModels() {
|
|||
text: isAdminPool && !canMutateProfiles ? t("models.readonly") : "",
|
||||
});
|
||||
const activeSelect = el("select", { class: "input", disabled: !canPickActive });
|
||||
const chatModelSelectorVisibleCb = el("input", { type: "checkbox", disabled: !canConfigureChatUi });
|
||||
const bindingWrap = el("div", {});
|
||||
const opsAiSpecialistSelect = el("select", { class: "input", disabled: !canConfigureBindings });
|
||||
const opsAiProfileSelect = el("select", { class: "input", disabled: !canConfigureBindings });
|
||||
|
|
@ -3882,6 +3888,8 @@ async function renderModels() {
|
|||
const aid = String(state.active_llm_profile_id || "");
|
||||
if (profiles.some((p) => String(p.id) === aid)) activeSelect.value = aid;
|
||||
activeSelect.disabled = !canPickActive;
|
||||
chatModelSelectorVisibleCb.disabled = !canConfigureChatUi;
|
||||
chatModelSelectorVisibleCb.checked = state.chat_model_selector_visible !== false;
|
||||
opsAiSpecialistSelect.disabled = !canConfigureBindings;
|
||||
opsAiProfileSelect.disabled = !canConfigureBindings;
|
||||
|
||||
|
|
@ -3963,6 +3971,17 @@ async function renderModels() {
|
|||
status.textContent = String(e.message || e);
|
||||
}
|
||||
});
|
||||
chatModelSelectorVisibleCb.addEventListener("change", async () => {
|
||||
if (!canConfigureChatUi) return;
|
||||
try {
|
||||
await apiPost("/admin/api/models/chat-ui", {
|
||||
chat_model_selector_visible: !!chatModelSelectorVisibleCb.checked,
|
||||
});
|
||||
await refresh();
|
||||
} catch (e) {
|
||||
status.textContent = String(e.message || e);
|
||||
}
|
||||
});
|
||||
opsAiSpecialistSelect.addEventListener("change", async () => {
|
||||
paintOpsAiBindings();
|
||||
});
|
||||
|
|
@ -4206,6 +4225,8 @@ async function renderModels() {
|
|||
el("div", { class: "page-grid page-grid--two" }, [
|
||||
renderSectionCard(t("models.sectionActive"), "", [
|
||||
el("div", { class: "row" }, [el("label", { text: t("models.pickModel") }), activeSelect]),
|
||||
el("div", { class: "row" }, [el("label", { text: t("models.chatModelSelector") }), chatModelSelectorVisibleCb]),
|
||||
el("div", { class: "muted", text: t("models.chatModelSelectorHint") }),
|
||||
], { id: "models-overview" }),
|
||||
renderSectionCard(t("models.sectionNew"), "", [
|
||||
el("div", { class: "row" }, [el("label", { text: t("models.profileName") }), newName]),
|
||||
|
|
|
|||
|
|
@ -2845,9 +2845,15 @@ async function renderChatUi() {
|
|||
};
|
||||
await loadSpecialistCatalog();
|
||||
const activeModelText = el("span", { class: "muted", text: `${t("chat.activeModelLabel")}: -` });
|
||||
const applyModelSelectorVisibility = (modelsState) => {
|
||||
const show = !(modelsState && modelsState.chat_model_selector_visible === false);
|
||||
modelSelect.style.display = show ? "" : "none";
|
||||
modelSelect.disabled = !show;
|
||||
};
|
||||
const refreshActiveModelText = async () => {
|
||||
try {
|
||||
const ms = await apiGet("/admin/api/models");
|
||||
applyModelSelectorVisibility(ms);
|
||||
const profiles = Array.isArray(ms.profiles) ? ms.profiles : [];
|
||||
const aid = String(ms.active_llm_profile_id || "");
|
||||
const p = profiles.find((x) => String(x.id || "") === aid) || null;
|
||||
|
|
@ -2883,6 +2889,7 @@ async function renderChatUi() {
|
|||
modelSelect.value = String(modelSelect.options[0].value || "");
|
||||
}
|
||||
} catch (_) {
|
||||
applyModelSelectorVisibility(null);
|
||||
activeModelText.textContent = `${t("chat.activeModelLabel")}: -`;
|
||||
modelSelect.innerHTML = "";
|
||||
modelSelect.appendChild(el("option", { value: "", text: "-" }));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue