mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-06-07 08:40:16 +08:00
feat(hermes): add terminal execution config form
This commit is contained in:
@@ -64,9 +64,23 @@ const EXECUTION_LIMITS_DEFAULTS = {
|
||||
delegationInheritMcpToolsets: true,
|
||||
}
|
||||
|
||||
const TERMINAL_DEFAULTS = {
|
||||
terminalBackend: 'local',
|
||||
terminalCwd: '.',
|
||||
terminalTimeout: 180,
|
||||
terminalLifetimeSeconds: 300,
|
||||
terminalDockerMountCwdToWorkspace: false,
|
||||
terminalDockerRunAsHostUser: false,
|
||||
terminalContainerCpu: 1,
|
||||
terminalContainerMemory: 5120,
|
||||
terminalContainerDisk: 51200,
|
||||
terminalContainerPersistent: true,
|
||||
}
|
||||
|
||||
const SESSION_RESET_MODES = ['both', 'idle', 'daily', 'none']
|
||||
const STREAMING_TRANSPORTS = ['edit', 'auto', 'draft', 'off']
|
||||
const CODE_EXECUTION_MODES = ['project', 'strict']
|
||||
const TERMINAL_BACKENDS = ['local', 'ssh', 'docker', 'singularity', 'modal', 'daytona', 'vercel_sandbox']
|
||||
|
||||
export function render() {
|
||||
const el = document.createElement('div')
|
||||
@@ -79,6 +93,7 @@ export function render() {
|
||||
let memoryValues = { ...MEMORY_DEFAULTS }
|
||||
let streamingValues = { ...STREAMING_DEFAULTS }
|
||||
let executionLimitsValues = { ...EXECUTION_LIMITS_DEFAULTS }
|
||||
let terminalValues = { ...TERMINAL_DEFAULTS }
|
||||
let loading = true
|
||||
let runtimeLoading = true
|
||||
let compressionLoading = true
|
||||
@@ -86,6 +101,7 @@ export function render() {
|
||||
let memoryLoading = true
|
||||
let streamingLoading = true
|
||||
let executionLimitsLoading = true
|
||||
let terminalLoading = true
|
||||
let saving = false
|
||||
let runtimeSaving = false
|
||||
let compressionSaving = false
|
||||
@@ -93,6 +109,7 @@ export function render() {
|
||||
let memorySaving = false
|
||||
let streamingSaving = false
|
||||
let executionLimitsSaving = false
|
||||
let terminalSaving = false
|
||||
let error = null
|
||||
let runtimeError = null
|
||||
let compressionError = null
|
||||
@@ -100,6 +117,7 @@ export function render() {
|
||||
let memoryError = null
|
||||
let streamingError = null
|
||||
let executionLimitsError = null
|
||||
let terminalError = null
|
||||
|
||||
function esc(value) {
|
||||
return String(value ?? '')
|
||||
@@ -110,7 +128,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function isBusy() {
|
||||
return loading || runtimeLoading || compressionLoading || toolGuardrailsLoading || memoryLoading || streamingLoading || executionLimitsLoading || saving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || streamingSaving || executionLimitsSaving
|
||||
return loading || runtimeLoading || compressionLoading || toolGuardrailsLoading || memoryLoading || streamingLoading || executionLimitsLoading || terminalLoading || saving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || streamingSaving || executionLimitsSaving || terminalSaving
|
||||
}
|
||||
|
||||
function option(labelKey, value, selected) {
|
||||
@@ -127,7 +145,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function renderRuntimePanel() {
|
||||
const disabled = loading || saving || runtimeLoading || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || streamingSaving || executionLimitsSaving
|
||||
const disabled = loading || saving || runtimeLoading || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || streamingSaving || executionLimitsSaving || terminalSaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel">
|
||||
<div class="hm-panel-header">
|
||||
@@ -175,7 +193,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function renderCompressionPanel() {
|
||||
const disabled = loading || saving || compressionLoading || compressionSaving || runtimeSaving || toolGuardrailsSaving || memorySaving || streamingSaving || executionLimitsSaving
|
||||
const disabled = loading || saving || compressionLoading || compressionSaving || runtimeSaving || toolGuardrailsSaving || memorySaving || streamingSaving || executionLimitsSaving || terminalSaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-compression-panel">
|
||||
<div class="hm-panel-header">
|
||||
@@ -225,7 +243,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function renderToolGuardrailsPanel() {
|
||||
const disabled = loading || saving || toolGuardrailsLoading || toolGuardrailsSaving || runtimeSaving || compressionSaving || memorySaving || streamingSaving || executionLimitsSaving
|
||||
const disabled = loading || saving || toolGuardrailsLoading || toolGuardrailsSaving || runtimeSaving || compressionSaving || memorySaving || streamingSaving || executionLimitsSaving || terminalSaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-guardrails-panel">
|
||||
<div class="hm-panel-header">
|
||||
@@ -287,7 +305,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function renderMemoryPanel() {
|
||||
const disabled = loading || saving || memoryLoading || memorySaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || streamingSaving || executionLimitsSaving
|
||||
const disabled = loading || saving || memoryLoading || memorySaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || streamingSaving || executionLimitsSaving || terminalSaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-memory-panel">
|
||||
<div class="hm-panel-header">
|
||||
@@ -333,7 +351,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function renderStreamingPanel() {
|
||||
const disabled = loading || saving || streamingLoading || streamingSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || executionLimitsSaving
|
||||
const disabled = loading || saving || streamingLoading || streamingSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || executionLimitsSaving || terminalSaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-streaming-panel">
|
||||
<div class="hm-panel-header">
|
||||
@@ -385,7 +403,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function renderExecutionLimitsPanel() {
|
||||
const disabled = loading || saving || executionLimitsLoading || executionLimitsSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || streamingSaving
|
||||
const disabled = loading || saving || executionLimitsLoading || executionLimitsSaving || terminalSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || streamingSaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-execution-limits-panel">
|
||||
<div class="hm-panel-header">
|
||||
@@ -456,6 +474,77 @@ export function render() {
|
||||
`
|
||||
}
|
||||
|
||||
function renderTerminalPanel() {
|
||||
const disabled = loading || saving || terminalLoading || terminalSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || streamingSaving || executionLimitsSaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-terminal-panel">
|
||||
<div class="hm-panel-header">
|
||||
<div>
|
||||
<div class="hm-panel-title">${t('engine.hermesTerminalConfigTitle')}</div>
|
||||
<div class="hm-channel-panel-desc">${t('engine.hermesTerminalConfigDesc')}</div>
|
||||
</div>
|
||||
<div class="hm-panel-actions">
|
||||
<span class="hm-muted">${terminalSaving ? t('engine.hermesConfigStatusSaving') : terminalLoading ? t('engine.hermesConfigStatusLoading') : t('engine.hermesTerminalConfigStatusReady')}</span>
|
||||
<button class="hm-btn hm-btn--cta hm-btn--sm" id="hm-terminal-save" ${disabled ? 'disabled' : ''}>${t('engine.hermesTerminalConfigSave')}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hm-panel-body">
|
||||
${renderError(terminalError)}
|
||||
<div class="hm-config-runtime-grid hm-config-terminal-grid">
|
||||
<label class="hm-field">
|
||||
<span class="hm-field-label">${t('engine.hermesTerminalConfigBackend')}</span>
|
||||
<select id="hm-terminal-backend" class="hm-input" ${disabled ? 'disabled' : ''}>
|
||||
${TERMINAL_BACKENDS.map(mode => option(`engine.hermesTerminalConfigBackend_${mode}`, mode, terminalValues.terminalBackend)).join('')}
|
||||
</select>
|
||||
</label>
|
||||
<label class="hm-field">
|
||||
<span class="hm-field-label">${t('engine.hermesTerminalConfigCwd')}</span>
|
||||
<input id="hm-terminal-cwd" class="hm-input" value="${esc(terminalValues.terminalCwd)}" ${disabled ? 'disabled' : ''}>
|
||||
</label>
|
||||
<label class="hm-field">
|
||||
<span class="hm-field-label">${t('engine.hermesTerminalConfigTimeout')}</span>
|
||||
<input id="hm-terminal-timeout" class="hm-input" type="number" inputmode="numeric" min="1" max="86400" step="1" value="${esc(terminalValues.terminalTimeout)}" ${disabled ? 'disabled' : ''}>
|
||||
</label>
|
||||
<label class="hm-field">
|
||||
<span class="hm-field-label">${t('engine.hermesTerminalConfigLifetimeSeconds')}</span>
|
||||
<input id="hm-terminal-lifetime-seconds" class="hm-input" type="number" inputmode="numeric" min="0" max="86400" step="1" value="${esc(terminalValues.terminalLifetimeSeconds)}" ${disabled ? 'disabled' : ''}>
|
||||
</label>
|
||||
</div>
|
||||
<div class="hm-config-check-grid">
|
||||
<label class="hm-channel-check hm-channel-check--danger">
|
||||
<input id="hm-terminal-docker-mount-cwd-to-workspace" type="checkbox" ${terminalValues.terminalDockerMountCwdToWorkspace ? 'checked' : ''} ${disabled ? 'disabled' : ''}>
|
||||
<span>${t('engine.hermesTerminalConfigDockerMountCwd')}</span>
|
||||
</label>
|
||||
<label class="hm-channel-check">
|
||||
<input id="hm-terminal-docker-run-as-host-user" type="checkbox" ${terminalValues.terminalDockerRunAsHostUser ? 'checked' : ''} ${disabled ? 'disabled' : ''}>
|
||||
<span>${t('engine.hermesTerminalConfigDockerRunAsHostUser')}</span>
|
||||
</label>
|
||||
<label class="hm-channel-check">
|
||||
<input id="hm-terminal-container-persistent" type="checkbox" ${terminalValues.terminalContainerPersistent ? 'checked' : ''} ${disabled ? 'disabled' : ''}>
|
||||
<span>${t('engine.hermesTerminalConfigContainerPersistent')}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="hm-config-subtitle">${t('engine.hermesTerminalConfigContainerTitle')}</div>
|
||||
<div class="hm-config-runtime-grid hm-config-terminal-grid">
|
||||
<label class="hm-field">
|
||||
<span class="hm-field-label">${t('engine.hermesTerminalConfigContainerCpu')}</span>
|
||||
<input id="hm-terminal-container-cpu" class="hm-input" type="number" inputmode="numeric" min="1" max="64" step="1" value="${esc(terminalValues.terminalContainerCpu)}" ${disabled ? 'disabled' : ''}>
|
||||
</label>
|
||||
<label class="hm-field">
|
||||
<span class="hm-field-label">${t('engine.hermesTerminalConfigContainerMemory')}</span>
|
||||
<input id="hm-terminal-container-memory" class="hm-input" type="number" inputmode="numeric" min="128" max="1048576" step="128" value="${esc(terminalValues.terminalContainerMemory)}" ${disabled ? 'disabled' : ''}>
|
||||
</label>
|
||||
<label class="hm-field">
|
||||
<span class="hm-field-label">${t('engine.hermesTerminalConfigContainerDisk')}</span>
|
||||
<input id="hm-terminal-container-disk" class="hm-input" type="number" inputmode="numeric" min="1024" max="10485760" step="1024" value="${esc(terminalValues.terminalContainerDisk)}" ${disabled ? 'disabled' : ''}>
|
||||
</label>
|
||||
</div>
|
||||
<div class="hm-channel-footnote">${t('engine.hermesTerminalConfigFootnote')}</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
function draw() {
|
||||
el.innerHTML = `
|
||||
<div class="hm-hero">
|
||||
@@ -471,6 +560,7 @@ export function render() {
|
||||
</div>
|
||||
|
||||
${renderRuntimePanel()}
|
||||
${renderTerminalPanel()}
|
||||
${renderStreamingPanel()}
|
||||
${renderExecutionLimitsPanel()}
|
||||
${renderCompressionPanel()}
|
||||
@@ -501,6 +591,7 @@ export function render() {
|
||||
el.querySelector('#hm-memory-save')?.addEventListener('click', saveMemory)
|
||||
el.querySelector('#hm-streaming-save')?.addEventListener('click', saveStreaming)
|
||||
el.querySelector('#hm-execution-limits-save')?.addEventListener('click', saveExecutionLimits)
|
||||
el.querySelector('#hm-terminal-save')?.addEventListener('click', saveTerminal)
|
||||
}
|
||||
|
||||
async function loadRaw() {
|
||||
@@ -538,6 +629,11 @@ export function render() {
|
||||
executionLimitsValues = { ...EXECUTION_LIMITS_DEFAULTS, ...(data?.values || {}) }
|
||||
}
|
||||
|
||||
async function loadTerminal() {
|
||||
const data = await api.hermesTerminalConfigRead()
|
||||
terminalValues = { ...TERMINAL_DEFAULTS, ...(data?.values || {}) }
|
||||
}
|
||||
|
||||
async function load() {
|
||||
loading = true
|
||||
runtimeLoading = true
|
||||
@@ -546,6 +642,7 @@ export function render() {
|
||||
memoryLoading = true
|
||||
streamingLoading = true
|
||||
executionLimitsLoading = true
|
||||
terminalLoading = true
|
||||
error = null
|
||||
runtimeError = null
|
||||
compressionError = null
|
||||
@@ -553,6 +650,7 @@ export function render() {
|
||||
memoryError = null
|
||||
streamingError = null
|
||||
executionLimitsError = null
|
||||
terminalError = null
|
||||
draw()
|
||||
try {
|
||||
await loadRaw()
|
||||
@@ -601,6 +699,14 @@ export function render() {
|
||||
executionLimitsLoading = false
|
||||
draw()
|
||||
}
|
||||
try {
|
||||
await loadTerminal()
|
||||
} catch (err) {
|
||||
terminalError = humanizeError(err, t('engine.hermesTerminalConfigLoadFailed') || 'Load terminal config failed')
|
||||
} finally {
|
||||
terminalLoading = false
|
||||
draw()
|
||||
}
|
||||
try {
|
||||
await loadMemory()
|
||||
} catch (err) {
|
||||
@@ -648,6 +754,9 @@ export function render() {
|
||||
try {
|
||||
await loadExecutionLimits()
|
||||
} catch {}
|
||||
try {
|
||||
await loadTerminal()
|
||||
} catch {}
|
||||
} catch (err) {
|
||||
error = humanizeError(err, t('engine.hermesConfigSaveFailed') || 'Save failed')
|
||||
toast(error, 'error')
|
||||
@@ -841,6 +950,40 @@ export function render() {
|
||||
}
|
||||
}
|
||||
|
||||
async function saveTerminal() {
|
||||
const form = {
|
||||
terminalBackend: el.querySelector('#hm-terminal-backend')?.value || 'local',
|
||||
terminalCwd: el.querySelector('#hm-terminal-cwd')?.value || '.',
|
||||
terminalTimeout: el.querySelector('#hm-terminal-timeout')?.value || '180',
|
||||
terminalLifetimeSeconds: el.querySelector('#hm-terminal-lifetime-seconds')?.value || '300',
|
||||
terminalDockerMountCwdToWorkspace: !!el.querySelector('#hm-terminal-docker-mount-cwd-to-workspace')?.checked,
|
||||
terminalDockerRunAsHostUser: !!el.querySelector('#hm-terminal-docker-run-as-host-user')?.checked,
|
||||
terminalContainerCpu: el.querySelector('#hm-terminal-container-cpu')?.value || '1',
|
||||
terminalContainerMemory: el.querySelector('#hm-terminal-container-memory')?.value || '5120',
|
||||
terminalContainerDisk: el.querySelector('#hm-terminal-container-disk')?.value || '51200',
|
||||
terminalContainerPersistent: !!el.querySelector('#hm-terminal-container-persistent')?.checked,
|
||||
}
|
||||
terminalSaving = true
|
||||
terminalError = null
|
||||
draw()
|
||||
try {
|
||||
const result = await api.hermesTerminalConfigSave(form)
|
||||
terminalValues = { ...TERMINAL_DEFAULTS, ...(result?.values || form) }
|
||||
await refreshRawAfterStructuredSave()
|
||||
const backup = result?.backup || ''
|
||||
toast({
|
||||
message: t('engine.hermesTerminalConfigSaveSuccess'),
|
||||
hint: backup ? t('engine.hermesConfigBackupHint', { path: backup }) : '',
|
||||
}, 'success')
|
||||
} catch (err) {
|
||||
terminalError = humanizeError(err, t('engine.hermesTerminalConfigSaveFailed') || 'Save terminal config failed')
|
||||
toast(terminalError, 'error')
|
||||
} finally {
|
||||
terminalSaving = false
|
||||
draw()
|
||||
}
|
||||
}
|
||||
|
||||
draw()
|
||||
load()
|
||||
return el
|
||||
|
||||
@@ -521,6 +521,8 @@ export const api = {
|
||||
hermesStreamingConfigSave: (form) => invoke('hermes_streaming_config_save', { form }),
|
||||
hermesExecutionLimitsConfigRead: () => invoke('hermes_execution_limits_config_read'),
|
||||
hermesExecutionLimitsConfigSave: (form) => invoke('hermes_execution_limits_config_save', { form }),
|
||||
hermesTerminalConfigRead: () => invoke('hermes_terminal_config_read'),
|
||||
hermesTerminalConfigSave: (form) => invoke('hermes_terminal_config_save', { form }),
|
||||
hermesLazyDepsFeatures: () => cachedInvoke('hermes_lazy_deps_features', {}, 600000),
|
||||
hermesLazyDepsStatus: (features) => invoke('hermes_lazy_deps_status', { features }),
|
||||
hermesLazyDepsEnsure: (feature) => invoke('hermes_lazy_deps_ensure', { feature }),
|
||||
|
||||
@@ -498,6 +498,32 @@ export default {
|
||||
hermesGroupSessionsPerUser: _('群聊按成员隔离会话', 'Isolate group sessions per user', '群聊依成員隔離會話'),
|
||||
hermesThreadSessionsPerUser: _('线程也按成员隔离', 'Isolate thread sessions per user', '討論串也依成員隔離'),
|
||||
hermesSessionRuntimeFootnote: _('推荐保持群聊隔离开启。关闭后,同一群/频道会共用上下文和中断状态。', 'Keeping group isolation on is recommended. Turning it off shares context and interrupt state across the same group or channel.', '建議保持群聊隔離開啟。關閉後,同一群組/頻道會共用上下文和中斷狀態。'),
|
||||
hermesTerminalConfigTitle: _('终端执行', 'Terminal execution', '終端執行'),
|
||||
hermesTerminalConfigDesc: _('控制 Hermes 工具命令的执行环境、工作目录、超时和容器资源,避免长任务卡死或沙箱范围误配。', 'Control command execution backend, working directory, timeouts, and container resources to avoid stuck runs or sandbox misconfiguration.', '控制 Hermes 工具命令的執行環境、工作目錄、逾時和容器資源,避免長任務卡死或沙箱範圍誤配。'),
|
||||
hermesTerminalConfigStatusReady: _('结构化配置', 'structured settings', '結構化設定'),
|
||||
hermesTerminalConfigSave: _('保存终端配置', 'Save terminal settings', '儲存終端設定'),
|
||||
hermesTerminalConfigSaveSuccess: _('终端执行配置已保存,建议重启 Hermes Gateway 生效', 'Terminal execution settings saved. Restart Hermes Gateway to take effect.', '終端執行設定已儲存,建議重啟 Hermes Gateway 生效'),
|
||||
hermesTerminalConfigLoadFailed: _('加载终端执行配置失败', 'Load terminal execution settings failed', '載入終端執行設定失敗'),
|
||||
hermesTerminalConfigSaveFailed: _('保存终端执行配置失败', 'Save terminal execution settings failed', '儲存終端執行設定失敗'),
|
||||
hermesTerminalConfigBackend: _('执行后端', 'Execution backend', '執行後端'),
|
||||
hermesTerminalConfigBackend_local: _('本机', 'Local machine', '本機'),
|
||||
hermesTerminalConfigBackend_ssh: _('SSH 远程', 'SSH remote', 'SSH 遠端'),
|
||||
hermesTerminalConfigBackend_docker: _('Docker 容器', 'Docker container', 'Docker 容器'),
|
||||
hermesTerminalConfigBackend_singularity: _('Singularity / Apptainer', 'Singularity / Apptainer', 'Singularity / Apptainer'),
|
||||
hermesTerminalConfigBackend_modal: _('Modal 云沙箱', 'Modal cloud sandbox', 'Modal 雲端沙箱'),
|
||||
hermesTerminalConfigBackend_daytona: _('Daytona 云沙箱', 'Daytona cloud sandbox', 'Daytona 雲端沙箱'),
|
||||
hermesTerminalConfigBackend_vercel_sandbox: _('Vercel 沙箱', 'Vercel sandbox', 'Vercel 沙箱'),
|
||||
hermesTerminalConfigCwd: _('工作目录', 'Working directory', '工作目錄'),
|
||||
hermesTerminalConfigTimeout: _('命令超时秒数', 'Command timeout seconds', '命令逾時秒數'),
|
||||
hermesTerminalConfigLifetimeSeconds: _('沙箱生命周期秒数', 'Sandbox lifetime seconds', '沙箱生命週期秒數'),
|
||||
hermesTerminalConfigDockerMountCwd: _('Docker 挂载启动目录到 /workspace', 'Mount launch cwd into Docker /workspace', 'Docker 掛載啟動目錄到 /workspace'),
|
||||
hermesTerminalConfigDockerRunAsHostUser: _('Docker 使用宿主用户运行', 'Run Docker as host user', 'Docker 使用宿主使用者執行'),
|
||||
hermesTerminalConfigContainerPersistent: _('容器文件系统持久化', 'Persist container filesystem', '容器檔案系統持久化'),
|
||||
hermesTerminalConfigContainerTitle: _('容器资源限制', 'Container resource limits', '容器資源限制'),
|
||||
hermesTerminalConfigContainerCpu: _('CPU 核数', 'CPU cores', 'CPU 核心數'),
|
||||
hermesTerminalConfigContainerMemory: _('内存 MB', 'Memory MB', '記憶體 MB'),
|
||||
hermesTerminalConfigContainerDisk: _('磁盘 MB', 'Disk MB', '磁碟 MB'),
|
||||
hermesTerminalConfigFootnote: _('Docker 挂载启动目录会把宿主目录暴露给沙箱,仅在可信项目和无人值守任务中开启;SSH、镜像、环境变量等高级参数仍可在 raw YAML 中编辑。', 'Mounting the launch cwd exposes host files to the sandbox. Enable it only for trusted projects or unattended jobs. SSH, image, and env advanced fields remain editable in raw YAML.', 'Docker 掛載啟動目錄會把宿主目錄暴露給沙箱,僅在可信專案和無人值守任務中開啟;SSH、映像、環境變數等進階參數仍可在 raw YAML 中編輯。'),
|
||||
hermesStreamingConfigTitle: _('网关流式输出', 'Gateway streaming output', '閘道流式輸出'),
|
||||
hermesStreamingConfigDesc: _('控制 Hermes Gateway 回复时是否边生成边更新消息,以及消息刷新节奏。适合需要更快看到长回复进度的渠道。', 'Control whether Hermes Gateway updates messages while replies are generated, plus the refresh cadence. Useful when channels need quicker progress for long replies.', '控制 Hermes Gateway 回覆時是否邊生成邊更新訊息,以及訊息刷新節奏。適合需要更快看到長回覆進度的渠道。'),
|
||||
hermesStreamingConfigStatusReady: _('结构化配置', 'structured settings', '結構化設定'),
|
||||
|
||||
Reference in New Issue
Block a user