feat(hermes): add base model config

This commit is contained in:
晴天
2026-05-26 05:25:30 +08:00
parent 1e56a54aeb
commit 66375d2807
8 changed files with 558 additions and 3 deletions

View File

@@ -83,6 +83,12 @@ const QUICK_COMMANDS_DEFAULTS = {
quickCommandsJson: '{}',
}
const MODEL_DEFAULTS = {
modelDefault: '',
modelProvider: 'auto',
modelBaseUrl: '',
}
const MODEL_ALIASES_DEFAULTS = {
modelAliasesJson: '{}',
}
@@ -300,6 +306,7 @@ export function render() {
let memoryValues = { ...MEMORY_DEFAULTS }
let skillsValues = { ...SKILLS_DEFAULTS }
let quickCommandsValues = { ...QUICK_COMMANDS_DEFAULTS }
let modelValues = { ...MODEL_DEFAULTS }
let modelAliasesValues = { ...MODEL_ALIASES_DEFAULTS }
let hooksValues = { ...HOOKS_DEFAULTS }
let providerOverridesValues = { ...PROVIDER_OVERRIDES_DEFAULTS }
@@ -333,6 +340,7 @@ export function render() {
let memoryLoading = true
let skillsLoading = true
let quickCommandsLoading = true
let modelLoading = true
let modelAliasesLoading = true
let hooksLoading = true
let providerOverridesLoading = true
@@ -366,6 +374,7 @@ export function render() {
let memorySaving = false
let skillsSaving = false
let quickCommandsSaving = false
let modelSaving = false
let modelAliasesSaving = false
let hooksSaving = false
let providerOverridesSaving = false
@@ -399,6 +408,7 @@ export function render() {
let memoryError = null
let skillsError = null
let quickCommandsError = null
let modelError = null
let modelAliasesError = null
let hooksError = null
let providerOverridesError = null
@@ -431,7 +441,7 @@ export function render() {
}
function isBusy() {
return loading || runtimeLoading || compressionLoading || promptCachingLoading || openrouterCacheLoading || providerRoutingLoading || auxiliaryLoading || toolGuardrailsLoading || memoryLoading || skillsLoading || quickCommandsLoading || modelAliasesLoading || hooksLoading || providerOverridesLoading || mcpServersLoading || agentToolsetsLoading || platformToolsetsLoading || agentRuntimeLoading || unauthorizedDmLoading || securityLoading || displayLoading || humanDelayLoading || streamingLoading || executionLimitsLoading || ioSafetyLoading || checkpointsLoading || cronLoading || loggingLoading || approvalsLoading || privacyLoading || browserLoading || sttLoading || terminalLoading || saving || runtimeSaving || compressionSaving || promptCachingSaving || openrouterCacheSaving || providerRoutingSaving || auxiliarySaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || modelAliasesSaving || hooksSaving || providerOverridesSaving || mcpServersSaving || agentToolsetsSaving || platformToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || securitySaving || displaySaving || humanDelaySaving || streamingSaving || executionLimitsSaving || ioSafetySaving || checkpointsSaving || cronSaving || loggingSaving || approvalsSaving || privacySaving || browserSaving || sttSaving || terminalSaving
return loading || runtimeLoading || compressionLoading || promptCachingLoading || openrouterCacheLoading || providerRoutingLoading || auxiliaryLoading || toolGuardrailsLoading || memoryLoading || skillsLoading || quickCommandsLoading || modelLoading || modelAliasesLoading || hooksLoading || providerOverridesLoading || mcpServersLoading || agentToolsetsLoading || platformToolsetsLoading || agentRuntimeLoading || unauthorizedDmLoading || securityLoading || displayLoading || humanDelayLoading || streamingLoading || executionLimitsLoading || ioSafetyLoading || checkpointsLoading || cronLoading || loggingLoading || approvalsLoading || privacyLoading || browserLoading || sttLoading || terminalLoading || saving || runtimeSaving || compressionSaving || promptCachingSaving || openrouterCacheSaving || providerRoutingSaving || auxiliarySaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || modelSaving || modelAliasesSaving || hooksSaving || providerOverridesSaving || mcpServersSaving || agentToolsetsSaving || platformToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || securitySaving || displaySaving || humanDelaySaving || streamingSaving || executionLimitsSaving || ioSafetySaving || checkpointsSaving || cronSaving || loggingSaving || approvalsSaving || privacySaving || browserSaving || sttSaving || terminalSaving
}
function option(labelKey, value, selected) {
@@ -893,7 +903,7 @@ export function render() {
}
function renderQuickCommandsConfigPanel() {
const disabled = loading || saving || quickCommandsLoading || quickCommandsSaving || modelAliasesSaving || hooksSaving || providerOverridesSaving || mcpServersSaving || agentToolsetsSaving || agentRuntimeSaving || runtimeSaving || compressionSaving || promptCachingSaving || openrouterCacheSaving || providerRoutingSaving || auxiliarySaving || toolGuardrailsSaving || memorySaving || skillsSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || cronSaving || loggingSaving || approvalsSaving || terminalSaving
const disabled = loading || saving || quickCommandsLoading || quickCommandsSaving || modelSaving || modelAliasesSaving || hooksSaving || providerOverridesSaving || mcpServersSaving || agentToolsetsSaving || agentRuntimeSaving || runtimeSaving || compressionSaving || promptCachingSaving || openrouterCacheSaving || providerRoutingSaving || auxiliarySaving || toolGuardrailsSaving || memorySaving || skillsSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || cronSaving || loggingSaving || approvalsSaving || terminalSaving
return `
<div class="hm-panel hm-config-runtime-panel hm-config-quick-commands-panel">
<div class="hm-panel-header">
@@ -918,8 +928,44 @@ export function render() {
`
}
function renderModelConfigPanel() {
const disabled = loading || saving || modelLoading || modelSaving || quickCommandsSaving || modelAliasesSaving || hooksSaving || providerOverridesSaving || mcpServersSaving || agentToolsetsSaving || agentRuntimeSaving || runtimeSaving || compressionSaving || promptCachingSaving || openrouterCacheSaving || providerRoutingSaving || auxiliarySaving || toolGuardrailsSaving || memorySaving || skillsSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || cronSaving || loggingSaving || approvalsSaving || terminalSaving
return `
<div class="hm-panel hm-config-runtime-panel hm-config-model-panel">
<div class="hm-panel-header">
<div>
<div class="hm-panel-title">${t('engine.hermesModelConfigTitle')}</div>
<div class="hm-channel-panel-desc">${t('engine.hermesModelConfigDesc')}</div>
</div>
<div class="hm-panel-actions">
<span class="hm-muted">${modelSaving ? t('engine.hermesConfigStatusSaving') : modelLoading ? t('engine.hermesConfigStatusLoading') : t('engine.hermesModelConfigStatusReady')}</span>
<button class="hm-btn hm-btn--cta hm-btn--sm" id="hm-model-config-save" ${disabled ? 'disabled' : ''}>${t('engine.hermesModelConfigSave')}</button>
</div>
</div>
<div class="hm-panel-body">
${renderError(modelError)}
<div class="hm-config-runtime-grid">
<label class="hm-field">
<span class="hm-field-label">${t('engine.hermesModelConfigDefault')}</span>
<input id="hm-model-default" class="hm-input" value="${esc(modelValues.modelDefault)}" placeholder="anthropic/claude-opus-4.6" ${disabled ? 'disabled' : ''}>
</label>
<label class="hm-field">
<span class="hm-field-label">${t('engine.hermesModelConfigProvider')}</span>
<input id="hm-model-provider" class="hm-input" value="${esc(modelValues.modelProvider)}" placeholder="auto" ${disabled ? 'disabled' : ''}>
</label>
<label class="hm-field hm-field--wide">
<span class="hm-field-label">${t('engine.hermesModelConfigBaseUrl')}</span>
<input id="hm-model-base-url" class="hm-input" value="${esc(modelValues.modelBaseUrl)}" placeholder="https://openrouter.ai/api/v1" ${disabled ? 'disabled' : ''}>
</label>
</div>
<div class="hm-channel-footnote">${t('engine.hermesModelConfigFootnote')}</div>
</div>
</div>
`
}
function renderModelAliasesConfigPanel() {
const disabled = loading || saving || modelAliasesLoading || modelAliasesSaving || quickCommandsSaving || hooksSaving || providerOverridesSaving || mcpServersSaving || agentToolsetsSaving || agentRuntimeSaving || runtimeSaving || compressionSaving || promptCachingSaving || openrouterCacheSaving || providerRoutingSaving || auxiliarySaving || toolGuardrailsSaving || memorySaving || skillsSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || cronSaving || loggingSaving || approvalsSaving || terminalSaving
const disabled = loading || saving || modelAliasesLoading || modelAliasesSaving || quickCommandsSaving || modelSaving || hooksSaving || providerOverridesSaving || mcpServersSaving || agentToolsetsSaving || agentRuntimeSaving || runtimeSaving || compressionSaving || promptCachingSaving || openrouterCacheSaving || providerRoutingSaving || auxiliarySaving || toolGuardrailsSaving || memorySaving || skillsSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || cronSaving || loggingSaving || approvalsSaving || terminalSaving
return `
<div class="hm-panel hm-config-runtime-panel hm-config-model-aliases-panel">
<div class="hm-panel-header">
@@ -1974,6 +2020,7 @@ export function render() {
${renderMemoryPanel()}
${renderSkillsConfigPanel()}
${renderQuickCommandsConfigPanel()}
${renderModelConfigPanel()}
${renderModelAliasesConfigPanel()}
${renderHooksConfigPanel()}
${renderProviderOverridesConfigPanel()}
@@ -2014,6 +2061,7 @@ export function render() {
el.querySelector('#hm-memory-save')?.addEventListener('click', saveMemory)
el.querySelector('#hm-skills-config-save')?.addEventListener('click', saveSkillsConfig)
el.querySelector('#hm-quick-commands-save')?.addEventListener('click', saveQuickCommandsConfig)
el.querySelector('#hm-model-config-save')?.addEventListener('click', saveModelConfig)
el.querySelector('#hm-model-aliases-save')?.addEventListener('click', saveModelAliasesConfig)
el.querySelector('#hm-hooks-save')?.addEventListener('click', saveHooksConfig)
el.querySelector('#hm-provider-overrides-save')?.addEventListener('click', saveProviderOverridesConfig)
@@ -2093,6 +2141,11 @@ export function render() {
quickCommandsValues = { ...QUICK_COMMANDS_DEFAULTS, ...(data?.values || {}) }
}
async function loadModelConfig() {
const data = await api.hermesModelConfigRead()
modelValues = { ...MODEL_DEFAULTS, ...(data?.values || {}) }
}
async function loadModelAliasesConfig() {
const data = await api.hermesModelAliasesConfigRead()
modelAliasesValues = { ...MODEL_ALIASES_DEFAULTS, ...(data?.values || {}) }
@@ -2215,6 +2268,7 @@ export function render() {
memoryLoading = true
skillsLoading = true
quickCommandsLoading = true
modelLoading = true
modelAliasesLoading = true
hooksLoading = true
providerOverridesLoading = true
@@ -2248,6 +2302,7 @@ export function render() {
memoryError = null
skillsError = null
quickCommandsError = null
modelError = null
modelAliasesError = null
hooksError = null
providerOverridesError = null
@@ -2446,6 +2501,14 @@ export function render() {
quickCommandsLoading = false
draw()
}
try {
await loadModelConfig()
} catch (err) {
modelError = humanizeError(err, t('engine.hermesModelConfigLoadFailed') || 'Load model config failed')
} finally {
modelLoading = false
draw()
}
try {
await loadModelAliasesConfig()
} catch (err) {
@@ -2944,6 +3007,33 @@ export function render() {
}
}
async function saveModelConfig() {
const form = {
modelDefault: el.querySelector('#hm-model-default')?.value || '',
modelProvider: el.querySelector('#hm-model-provider')?.value || 'auto',
modelBaseUrl: el.querySelector('#hm-model-base-url')?.value || '',
}
modelSaving = true
modelError = null
draw()
try {
const result = await api.hermesModelConfigSave(form)
modelValues = { ...MODEL_DEFAULTS, ...(result?.values || form) }
await refreshRawAfterStructuredSave()
const backup = result?.backup || ''
toast({
message: t('engine.hermesModelConfigSaveSuccess'),
hint: backup ? t('engine.hermesConfigBackupHint', { path: backup }) : '',
}, 'success')
} catch (err) {
modelError = humanizeError(err, t('engine.hermesModelConfigSaveFailed') || 'Save model config failed')
toast(modelError, 'error')
} finally {
modelSaving = false
draw()
}
}
async function saveModelAliasesConfig() {
const form = {
modelAliasesJson: el.querySelector('#hm-model-aliases-json')?.value || '{}',

View File

@@ -529,6 +529,8 @@ export const api = {
hermesSkillsConfigSave: (form) => invoke('hermes_skills_config_save', { form }),
hermesQuickCommandsConfigRead: () => invoke('hermes_quick_commands_config_read'),
hermesQuickCommandsConfigSave: (form) => invoke('hermes_quick_commands_config_save', { form }),
hermesModelConfigRead: () => invoke('hermes_model_config_read'),
hermesModelConfigSave: (form) => invoke('hermes_model_config_save', { form }),
hermesModelAliasesConfigRead: () => invoke('hermes_model_aliases_config_read'),
hermesModelAliasesConfigSave: (form) => invoke('hermes_model_aliases_config_save', { form }),
hermesHooksConfigRead: () => invoke('hermes_hooks_config_read'),

View File

@@ -823,6 +823,17 @@ export default {
hermesQuickCommandsConfigSaveFailed: _('保存快捷命令失败', 'Save quick commands failed', '儲存快捷命令失敗'),
hermesQuickCommandsConfigJson: _('quick_commands JSON 映射', 'quick_commands JSON map', 'quick_commands JSON 映射'),
hermesQuickCommandsConfigFootnote: _('键名会变成斜杠命令,例如 status 对应 /status。每个命令必须是对象type 只能为 exec 或 aliasexec 需要 commandalias 的 target 必须以 / 开头。', 'Keys become slash commands, for example status maps to /status. Each command must be an object with type exec or alias; exec needs command, and alias target must start with /.', '鍵名會變成斜線命令,例如 status 對應 /status。每個命令必須是物件type 只能是 exec 或 aliasexec 需要 commandalias 的 target 必須以 / 開頭。'),
hermesModelConfigTitle: _('基础模型', 'Base model', '基礎模型'),
hermesModelConfigDesc: _('配置 Hermes 默认使用的模型、provider 和兼容接口地址。API Key 仍在安装向导或环境变量中管理,这里不会展示密钥。', 'Configure the default model, provider, and compatible API base URL for Hermes. API keys still live in setup or environment variables and are not shown here.', '設定 Hermes 預設使用的模型、provider 和相容介面位址。API Key 仍在安裝精靈或環境變數中管理,這裡不會顯示金鑰。'),
hermesModelConfigStatusReady: _('结构化表单', 'structured form', '結構化表單'),
hermesModelConfigSave: _('保存基础模型', 'Save base model', '儲存基礎模型'),
hermesModelConfigSaveSuccess: _('基础模型已保存,建议重启 Hermes Gateway 生效', 'Base model saved. Restart Hermes Gateway to take effect.', '基礎模型已儲存,建議重啟 Hermes Gateway 生效'),
hermesModelConfigLoadFailed: _('加载基础模型失败', 'Load base model failed', '載入基礎模型失敗'),
hermesModelConfigSaveFailed: _('保存基础模型失败', 'Save base model failed', '儲存基礎模型失敗'),
hermesModelConfigDefault: _('默认模型', 'Default model', '預設模型'),
hermesModelConfigProvider: _('Provider', 'Provider', 'Provider'),
hermesModelConfigBaseUrl: _('兼容接口地址(可选)', 'Compatible API base URL (optional)', '相容介面位址(可選)'),
hermesModelConfigFootnote: _('默认模型不能为空provider 为空时建议填 auto。兼容接口地址留空会移除 model.base_url并保留 model 下其它高级字段。', 'Default model is required; use auto when no provider is pinned. Leaving the base URL blank removes model.base_url while preserving other advanced model fields.', '預設模型不可為空;未固定 provider 時建議填 auto。相容介面位址留空會移除 model.base_url並保留 model 下其他進階欄位。'),
hermesModelAliasesConfigTitle: _('模型别名', 'Model aliases', '模型別名'),
hermesModelAliasesConfigDesc: _('配置 /model 命令可用的短别名把常用模型、provider 和自定义 base_url 固定下来,减少手输错误。', 'Configure short aliases for the /model command, pinning common models, providers, and custom base_url values to reduce manual input errors.', '設定 /model 命令可用的短別名把常用模型、provider 和自訂 base_url 固定下來,減少手動輸入錯誤。'),
hermesModelAliasesConfigStatusReady: _('结构化 JSON', 'structured JSON', '結構化 JSON'),