mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-07-10 06:52:31 +08:00
feat(hermes): add web tool backend controls
This commit is contained in:
@@ -294,6 +294,12 @@ const BROWSER_DEFAULTS = {
|
||||
browserDialogTimeout: 300,
|
||||
}
|
||||
|
||||
const WEB_DEFAULTS = {
|
||||
webBackend: '',
|
||||
webSearchBackend: '',
|
||||
webExtractBackend: '',
|
||||
}
|
||||
|
||||
const STT_DEFAULTS = {
|
||||
sttEnabled: true,
|
||||
sttProvider: 'auto',
|
||||
@@ -361,6 +367,7 @@ const TERMINAL_MODAL_MODES = ['auto', 'managed', 'direct']
|
||||
const TERMINAL_VERCEL_RUNTIMES = ['node24', 'node22', 'python3.13']
|
||||
const BROWSER_ENGINES = ['auto', 'lightpanda', 'chrome']
|
||||
const BROWSER_DIALOG_POLICIES = ['must_respond', 'auto_dismiss', 'auto_accept']
|
||||
const WEB_BACKENDS = ['', 'tavily', 'firecrawl', 'parallel', 'exa', 'searxng', 'brave', 'brave_free', 'ddgs', 'xai', 'native']
|
||||
const STT_PROVIDERS = ['auto', 'local', 'groq', 'openai', 'mistral']
|
||||
const STT_LOCAL_MODELS = ['tiny', 'base', 'small', 'medium', 'large-v3', 'turbo']
|
||||
const STT_OPENAI_MODELS = ['whisper-1', 'gpt-4o-mini-transcribe', 'gpt-4o-transcribe']
|
||||
@@ -426,6 +433,7 @@ export function render() {
|
||||
let approvalsValues = { ...APPROVALS_DEFAULTS }
|
||||
let privacyValues = { ...PRIVACY_DEFAULTS }
|
||||
let browserValues = { ...BROWSER_DEFAULTS }
|
||||
let webValues = { ...WEB_DEFAULTS }
|
||||
let sttValues = { ...STT_DEFAULTS }
|
||||
let ttsVoiceValues = { ...TTS_VOICE_DEFAULTS }
|
||||
let terminalValues = { ...TERMINAL_DEFAULTS }
|
||||
@@ -465,6 +473,7 @@ export function render() {
|
||||
let approvalsLoading = true
|
||||
let privacyLoading = true
|
||||
let browserLoading = true
|
||||
let webLoading = true
|
||||
let sttLoading = true
|
||||
let ttsVoiceLoading = true
|
||||
let terminalLoading = true
|
||||
@@ -504,6 +513,7 @@ export function render() {
|
||||
let approvalsSaving = false
|
||||
let privacySaving = false
|
||||
let browserSaving = false
|
||||
let webSaving = false
|
||||
let sttSaving = false
|
||||
let ttsVoiceSaving = false
|
||||
let terminalSaving = false
|
||||
@@ -543,6 +553,7 @@ export function render() {
|
||||
let approvalsError = null
|
||||
let privacyError = null
|
||||
let browserError = null
|
||||
let webError = null
|
||||
let sttError = null
|
||||
let ttsVoiceError = null
|
||||
let terminalError = null
|
||||
@@ -556,7 +567,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function isBusy() {
|
||||
return loading || runtimeLoading || sessionsMaintenanceLoading || updatesLoading || compressionLoading || promptCachingLoading || openrouterCacheLoading || providerRoutingLoading || auxiliaryLoading || toolGuardrailsLoading || memoryLoading || skillsLoading || curatorLoading || quickCommandsLoading || modelLoading || modelAliasesLoading || hooksLoading || providerOverridesLoading || mcpServersLoading || agentToolsetsLoading || platformToolsetsLoading || agentRuntimeLoading || unauthorizedDmLoading || securityLoading || displayLoading || humanDelayLoading || kanbanLoading || streamingLoading || executionLimitsLoading || ioSafetyLoading || checkpointsLoading || cronLoading || loggingLoading || approvalsLoading || privacyLoading || browserLoading || sttLoading || ttsVoiceLoading || terminalLoading || saving || runtimeSaving || sessionsMaintenanceSaving || updatesSaving || compressionSaving || promptCachingSaving || openrouterCacheSaving || providerRoutingSaving || auxiliarySaving || toolGuardrailsSaving || memorySaving || skillsSaving || curatorSaving || quickCommandsSaving || modelSaving || modelAliasesSaving || hooksSaving || providerOverridesSaving || mcpServersSaving || agentToolsetsSaving || platformToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || securitySaving || displaySaving || humanDelaySaving || kanbanSaving || streamingSaving || executionLimitsSaving || ioSafetySaving || checkpointsSaving || cronSaving || loggingSaving || approvalsSaving || privacySaving || browserSaving || sttSaving || ttsVoiceSaving || terminalSaving
|
||||
return loading || runtimeLoading || sessionsMaintenanceLoading || updatesLoading || compressionLoading || promptCachingLoading || openrouterCacheLoading || providerRoutingLoading || auxiliaryLoading || toolGuardrailsLoading || memoryLoading || skillsLoading || curatorLoading || quickCommandsLoading || modelLoading || modelAliasesLoading || hooksLoading || providerOverridesLoading || mcpServersLoading || agentToolsetsLoading || platformToolsetsLoading || agentRuntimeLoading || unauthorizedDmLoading || securityLoading || displayLoading || humanDelayLoading || kanbanLoading || streamingLoading || executionLimitsLoading || ioSafetyLoading || checkpointsLoading || cronLoading || loggingLoading || approvalsLoading || privacyLoading || browserLoading || webLoading || sttLoading || ttsVoiceLoading || terminalLoading || saving || runtimeSaving || sessionsMaintenanceSaving || updatesSaving || compressionSaving || promptCachingSaving || openrouterCacheSaving || providerRoutingSaving || auxiliarySaving || toolGuardrailsSaving || memorySaving || skillsSaving || curatorSaving || quickCommandsSaving || modelSaving || modelAliasesSaving || hooksSaving || providerOverridesSaving || mcpServersSaving || agentToolsetsSaving || platformToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || securitySaving || displaySaving || humanDelaySaving || kanbanSaving || streamingSaving || executionLimitsSaving || ioSafetySaving || checkpointsSaving || cronSaving || loggingSaving || approvalsSaving || privacySaving || browserSaving || webSaving || sttSaving || ttsVoiceSaving || terminalSaving
|
||||
}
|
||||
|
||||
function option(labelKey, value, selected) {
|
||||
@@ -2180,7 +2191,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function renderBrowserPanel() {
|
||||
const disabled = loading || saving || browserLoading || browserSaving || approvalsSaving || cronSaving || loggingSaving || privacySaving || sttSaving || terminalSaving || runtimeSaving || compressionSaving || promptCachingSaving || openrouterCacheSaving || providerRoutingSaving || auxiliarySaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || providerOverridesSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || ioSafetySaving || checkpointsSaving
|
||||
const disabled = loading || saving || browserLoading || browserSaving || webSaving || approvalsSaving || cronSaving || loggingSaving || privacySaving || sttSaving || terminalSaving || runtimeSaving || compressionSaving || promptCachingSaving || openrouterCacheSaving || providerRoutingSaving || auxiliarySaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || providerOverridesSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || ioSafetySaving || checkpointsSaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-browser-panel">
|
||||
<div class="hm-panel-header">
|
||||
@@ -2245,8 +2256,50 @@ export function render() {
|
||||
`
|
||||
}
|
||||
|
||||
function renderWebConfigPanel() {
|
||||
const disabled = loading || saving || webLoading || webSaving || browserSaving || approvalsSaving || cronSaving || loggingSaving || privacySaving || sttSaving || terminalSaving || runtimeSaving || compressionSaving || promptCachingSaving || openrouterCacheSaving || providerRoutingSaving || auxiliarySaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || providerOverridesSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || ioSafetySaving || checkpointsSaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-web-panel">
|
||||
<div class="hm-panel-header">
|
||||
<div>
|
||||
<div class="hm-panel-title">${t('engine.hermesWebConfigTitle')}</div>
|
||||
<div class="hm-channel-panel-desc">${t('engine.hermesWebConfigDesc')}</div>
|
||||
</div>
|
||||
<div class="hm-panel-actions">
|
||||
<span class="hm-muted">${webSaving ? t('engine.hermesConfigStatusSaving') : webLoading ? t('engine.hermesConfigStatusLoading') : t('engine.hermesWebConfigStatusReady')}</span>
|
||||
<button class="hm-btn hm-btn--cta hm-btn--sm" id="hm-web-config-save" ${disabled ? 'disabled' : ''}>${t('engine.hermesWebConfigSave')}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hm-panel-body">
|
||||
${renderError(webError)}
|
||||
<div class="hm-config-runtime-grid hm-config-web-grid">
|
||||
<label class="hm-field">
|
||||
<span class="hm-field-label">${t('engine.hermesWebConfigBackend')}</span>
|
||||
<select id="hm-web-backend" class="hm-input" ${disabled ? 'disabled' : ''}>
|
||||
${WEB_BACKENDS.map(backend => option(`engine.hermesWebConfigBackend_${backend || 'auto'}`, backend, webValues.webBackend)).join('')}
|
||||
</select>
|
||||
</label>
|
||||
<label class="hm-field">
|
||||
<span class="hm-field-label">${t('engine.hermesWebConfigSearchBackend')}</span>
|
||||
<select id="hm-web-search-backend" class="hm-input" ${disabled ? 'disabled' : ''}>
|
||||
${WEB_BACKENDS.map(backend => option(`engine.hermesWebConfigBackend_${backend || 'auto'}`, backend, webValues.webSearchBackend)).join('')}
|
||||
</select>
|
||||
</label>
|
||||
<label class="hm-field">
|
||||
<span class="hm-field-label">${t('engine.hermesWebConfigExtractBackend')}</span>
|
||||
<select id="hm-web-extract-backend" class="hm-input" ${disabled ? 'disabled' : ''}>
|
||||
${WEB_BACKENDS.map(backend => option(`engine.hermesWebConfigBackend_${backend || 'auto'}`, backend, webValues.webExtractBackend)).join('')}
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<div class="hm-channel-footnote">${t('engine.hermesWebConfigFootnote')}</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
function renderSttPanel() {
|
||||
const disabled = loading || saving || sttLoading || sttSaving || approvalsSaving || cronSaving || loggingSaving || privacySaving || browserSaving || terminalSaving || runtimeSaving || compressionSaving || promptCachingSaving || openrouterCacheSaving || providerRoutingSaving || auxiliarySaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || providerOverridesSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || ioSafetySaving || checkpointsSaving
|
||||
const disabled = loading || saving || sttLoading || sttSaving || webSaving || approvalsSaving || cronSaving || loggingSaving || privacySaving || browserSaving || terminalSaving || runtimeSaving || compressionSaving || promptCachingSaving || openrouterCacheSaving || providerRoutingSaving || auxiliarySaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || providerOverridesSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || ioSafetySaving || checkpointsSaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-stt-panel">
|
||||
<div class="hm-panel-header">
|
||||
@@ -2574,6 +2627,7 @@ export function render() {
|
||||
${renderApprovalsPanel()}
|
||||
${renderPrivacyPanel()}
|
||||
${renderBrowserPanel()}
|
||||
${renderWebConfigPanel()}
|
||||
${renderSttPanel()}
|
||||
${renderTtsVoicePanel()}
|
||||
${renderCompressionPanel()}
|
||||
@@ -2653,6 +2707,7 @@ export function render() {
|
||||
el.querySelector('#hm-approvals-save')?.addEventListener('click', saveApprovalsConfig)
|
||||
el.querySelector('#hm-privacy-save')?.addEventListener('click', savePrivacyConfig)
|
||||
el.querySelector('#hm-browser-save')?.addEventListener('click', saveBrowserConfig)
|
||||
el.querySelector('#hm-web-config-save')?.addEventListener('click', saveWebConfig)
|
||||
el.querySelector('#hm-stt-save')?.addEventListener('click', saveSttConfig)
|
||||
el.querySelector('#hm-tts-voice-save')?.addEventListener('click', saveTtsVoiceConfig)
|
||||
el.querySelector('#hm-terminal-save')?.addEventListener('click', saveTerminal)
|
||||
@@ -2838,6 +2893,11 @@ export function render() {
|
||||
browserValues = { ...BROWSER_DEFAULTS, ...(data?.values || {}) }
|
||||
}
|
||||
|
||||
async function loadWebConfig() {
|
||||
const data = await api.hermesWebConfigRead()
|
||||
webValues = { ...WEB_DEFAULTS, ...(data?.values || {}) }
|
||||
}
|
||||
|
||||
async function loadSttConfig() {
|
||||
const data = await api.hermesSttConfigRead()
|
||||
sttValues = { ...STT_DEFAULTS, ...(data?.values || {}) }
|
||||
@@ -2890,6 +2950,7 @@ export function render() {
|
||||
approvalsLoading = true
|
||||
privacyLoading = true
|
||||
browserLoading = true
|
||||
webLoading = true
|
||||
sttLoading = true
|
||||
ttsVoiceLoading = true
|
||||
terminalLoading = true
|
||||
@@ -2928,6 +2989,7 @@ export function render() {
|
||||
approvalsError = null
|
||||
privacyError = null
|
||||
browserError = null
|
||||
webError = null
|
||||
sttError = null
|
||||
ttsVoiceError = null
|
||||
terminalError = null
|
||||
@@ -3083,6 +3145,14 @@ export function render() {
|
||||
browserLoading = false
|
||||
draw()
|
||||
}
|
||||
try {
|
||||
await loadWebConfig()
|
||||
} catch (err) {
|
||||
webError = humanizeError(err, t('engine.hermesWebConfigLoadFailed') || 'Load web tool config failed')
|
||||
} finally {
|
||||
webLoading = false
|
||||
draw()
|
||||
}
|
||||
try {
|
||||
await loadSttConfig()
|
||||
} catch (err) {
|
||||
@@ -4401,6 +4471,33 @@ export function render() {
|
||||
}
|
||||
}
|
||||
|
||||
async function saveWebConfig() {
|
||||
const form = {
|
||||
webBackend: el.querySelector('#hm-web-backend')?.value || '',
|
||||
webSearchBackend: el.querySelector('#hm-web-search-backend')?.value || '',
|
||||
webExtractBackend: el.querySelector('#hm-web-extract-backend')?.value || '',
|
||||
}
|
||||
webSaving = true
|
||||
webError = null
|
||||
draw()
|
||||
try {
|
||||
const result = await api.hermesWebConfigSave(form)
|
||||
webValues = { ...WEB_DEFAULTS, ...(result?.values || form) }
|
||||
await refreshRawAfterStructuredSave()
|
||||
const backup = result?.backup || ''
|
||||
toast({
|
||||
message: t('engine.hermesWebConfigSaveSuccess'),
|
||||
hint: backup ? t('engine.hermesConfigBackupHint', { path: backup }) : '',
|
||||
}, 'success')
|
||||
} catch (err) {
|
||||
webError = humanizeError(err, t('engine.hermesWebConfigSaveFailed') || 'Save web tool config failed')
|
||||
toast(webError, 'error')
|
||||
} finally {
|
||||
webSaving = false
|
||||
draw()
|
||||
}
|
||||
}
|
||||
|
||||
async function saveSttConfig() {
|
||||
const form = {
|
||||
sttEnabled: !!el.querySelector('#hm-stt-enabled')?.checked,
|
||||
|
||||
@@ -579,6 +579,8 @@ export const api = {
|
||||
hermesPrivacyConfigSave: (form) => invoke('hermes_privacy_config_save', { form }),
|
||||
hermesBrowserConfigRead: () => invoke('hermes_browser_config_read'),
|
||||
hermesBrowserConfigSave: (form) => invoke('hermes_browser_config_save', { form }),
|
||||
hermesWebConfigRead: () => invoke('hermes_web_config_read'),
|
||||
hermesWebConfigSave: (form) => invoke('hermes_web_config_save', { form }),
|
||||
hermesSttConfigRead: () => invoke('hermes_stt_config_read'),
|
||||
hermesSttConfigSave: (form) => invoke('hermes_stt_config_save', { form }),
|
||||
hermesTtsVoiceConfigRead: () => invoke('hermes_tts_voice_config_read'),
|
||||
|
||||
@@ -717,6 +717,28 @@ export default {
|
||||
hermesBrowserConfigDialogPolicy_auto_accept: _('自动确认弹窗', 'Auto accept dialogs', '自動確認彈窗'),
|
||||
hermesBrowserConfigDialogTimeout: _('弹窗等待超时秒数', 'Dialog wait timeout seconds', '彈窗等待逾時秒數'),
|
||||
hermesBrowserConfigFootnote: _('Lightpanda 导航更快但不支持截图;录制会把 WebM 写入 Hermes browser_recordings 目录,请只在需要审计时开启。允许私网地址会放开 localhost / 192.168 等目标;CDP 地址留空表示自动创建浏览器;Camofox 嵌套高级字段会保留在 raw YAML 中。', 'Lightpanda navigates faster but does not support screenshots. Recording writes WebM files into the Hermes browser_recordings directory, so enable it only for audits. Allowing private URLs opens localhost / 192.168-style targets. Empty CDP URL means Hermes launches a browser automatically. Nested Camofox advanced fields stay in raw YAML.', 'Lightpanda 導覽更快但不支援截圖;錄製會把 WebM 寫入 Hermes browser_recordings 目錄,請只在需要稽核時開啟。允許私網位址會放開 localhost / 192.168 等目標;CDP 位址留空表示自動建立瀏覽器;Camofox 巢狀進階欄位會保留在 raw YAML 中。'),
|
||||
hermesWebConfigTitle: _('Web 工具后端', 'Web tool backends', 'Web 工具後端'),
|
||||
hermesWebConfigDesc: _('控制 web_search / web_extract 的默认和分能力后端,只保存后端选择,不保存 API Key。', 'Control default and per-capability backends for web_search / web_extract. This stores backend choices only, not API keys.', '控制 web_search / web_extract 的預設和分能力後端,只儲存後端選擇,不儲存 API Key。'),
|
||||
hermesWebConfigStatusReady: _('结构化配置', 'structured settings', '結構化設定'),
|
||||
hermesWebConfigSave: _('保存 Web 工具配置', 'Save web tool settings', '儲存 Web 工具設定'),
|
||||
hermesWebConfigSaveSuccess: _('Web 工具后端已保存,建议重启 Hermes Gateway 生效', 'Web tool backend settings saved. Restart Hermes Gateway to take effect.', 'Web 工具後端已儲存,建議重啟 Hermes Gateway 生效'),
|
||||
hermesWebConfigLoadFailed: _('加载 Web 工具配置失败', 'Load web tool settings failed', '載入 Web 工具設定失敗'),
|
||||
hermesWebConfigSaveFailed: _('保存 Web 工具配置失败', 'Save web tool settings failed', '儲存 Web 工具設定失敗'),
|
||||
hermesWebConfigBackend: _('默认后端', 'Default backend', '預設後端'),
|
||||
hermesWebConfigSearchBackend: _('搜索后端', 'Search backend', '搜尋後端'),
|
||||
hermesWebConfigExtractBackend: _('提取后端', 'Extract backend', '提取後端'),
|
||||
hermesWebConfigBackend_auto: _('自动选择', 'Auto select', '自動選擇'),
|
||||
hermesWebConfigBackend_tavily: _('Tavily', 'Tavily', 'Tavily'),
|
||||
hermesWebConfigBackend_firecrawl: _('Firecrawl', 'Firecrawl', 'Firecrawl'),
|
||||
hermesWebConfigBackend_parallel: _('Parallel', 'Parallel', 'Parallel'),
|
||||
hermesWebConfigBackend_exa: _('Exa', 'Exa', 'Exa'),
|
||||
hermesWebConfigBackend_searxng: _('SearXNG', 'SearXNG', 'SearXNG'),
|
||||
hermesWebConfigBackend_brave: _('Brave Search', 'Brave Search', 'Brave Search'),
|
||||
hermesWebConfigBackend_brave_free: _('Brave Free', 'Brave Free', 'Brave Free'),
|
||||
hermesWebConfigBackend_ddgs: _('DuckDuckGo', 'DuckDuckGo', 'DuckDuckGo'),
|
||||
hermesWebConfigBackend_xai: _('xAI Search', 'xAI Search', 'xAI Search'),
|
||||
hermesWebConfigBackend_native: _('原生提取', 'Native extraction', '原生提取'),
|
||||
hermesWebConfigFootnote: _('这里写入 web.backend、web.search_backend 和 web.extract_backend。留空会删除覆盖,让 Hermes 按密钥、可用 provider 和上游默认策略自动选择;未知 web 字段会保留在 raw YAML 中。', 'This writes web.backend, web.search_backend, and web.extract_backend. Leaving a value empty removes the override so Hermes can auto-select based on keys, available providers, and upstream defaults. Unknown web fields stay in raw YAML.', '這裡寫入 web.backend、web.search_backend 和 web.extract_backend。留空會刪除覆蓋,讓 Hermes 按密鑰、可用 provider 和上游預設策略自動選擇;未知 web 欄位會保留在 raw YAML 中。'),
|
||||
hermesSttConfigTitle: _('语音转写', 'Speech transcription', '語音轉寫'),
|
||||
hermesSttConfigDesc: _('控制消息平台语音消息是否自动转写,以及本地、OpenAI 和 Mistral 转写模型。适合需要处理语音反馈的渠道。', 'Control automatic voice-message transcription for messaging platforms, plus local, OpenAI, and Mistral transcription models. Useful for channels that receive voice feedback.', '控制訊息平台語音訊息是否自動轉寫,以及本機、OpenAI 和 Mistral 轉寫模型。適合需要處理語音回饋的渠道。'),
|
||||
hermesSttConfigStatusReady: _('结构化配置', 'structured settings', '結構化設定'),
|
||||
|
||||
Reference in New Issue
Block a user