fix: address PR review feedback for config UI controls

- Fix subturn_max_depth_hint: clarify that 0 uses default (3), not disable
- Fix subturn_default_timeout_hint: clarify that 0 uses system default (5 min)
- Add fatal log level option to i18n translations (en + zh)
- Extract select .find() lookups to component-level variables
- Add validation: routing enabled requires non-empty light_model
- Only parse filterMinLength when filter_sensitive_data is enabled
This commit is contained in:
Kristjan Kruus 2026-03-25 17:15:20 +02:00
parent c6e99f86a2
commit adb66052d5
4 changed files with 50 additions and 48 deletions

View file

@ -213,6 +213,9 @@ export function ConfigPage() {
) )
// Routing fields // Routing fields
if (form.routingEnabled && !form.routingLightModel.trim()) {
throw new Error("Light Model is required when Smart Routing is enabled.")
}
const routingThreshold = form.routingEnabled const routingThreshold = form.routingEnabled
? parseFloatField(form.routingThreshold, "Routing threshold", { ? parseFloatField(form.routingThreshold, "Routing threshold", {
min: 0, min: 0,
@ -230,11 +233,12 @@ export function ConfigPage() {
const maxMediaSize = form.maxMediaSize.trim() const maxMediaSize = form.maxMediaSize.trim()
? parseIntField(form.maxMediaSize, "Max media size", { min: 0 }) ? parseIntField(form.maxMediaSize, "Max media size", { min: 0 })
: undefined : undefined
const filterMinLength = parseIntField( const filterMinLength =
form.filterMinLength, form.filterSensitiveData && form.filterMinLength.trim()
"Filter min length", ? parseIntField(form.filterMinLength, "Filter min length", {
{ min: 0 }, min: 0,
) })
: undefined
const execConfigPatch: Record<string, unknown> = { const execConfigPatch: Record<string, unknown> = {
enabled: form.execEnabled, enabled: form.execEnabled,
@ -287,7 +291,7 @@ export function ConfigPage() {
routing: form.routingEnabled routing: form.routingEnabled
? { ? {
enabled: true, enabled: true,
light_model: form.routingLightModel, light_model: form.routingLightModel.trim() || undefined,
threshold: routingThreshold, threshold: routingThreshold,
} }
: { enabled: false }, : { enabled: false },

View file

@ -72,6 +72,9 @@ export function AgentDefaultsSection({
onFieldChange, onFieldChange,
}: AgentDefaultsSectionProps) { }: AgentDefaultsSectionProps) {
const { t } = useTranslation() const { t } = useTranslation()
const selectedSteeringModeOption = STEERING_MODE_OPTIONS.find(
(o) => o.value === form.steeringMode,
)
return ( return (
<ConfigSectionCard title={t("pages.config.sections.agent")}> <ConfigSectionCard title={t("pages.config.sections.agent")}>
@ -210,36 +213,28 @@ export function AgentDefaultsSection({
hint={t("pages.config.steering_mode_hint")} hint={t("pages.config.steering_mode_hint")}
layout="setting-row" layout="setting-row"
> >
{(() => { <Select
const selectedSteeringModeOption = STEERING_MODE_OPTIONS.find( value={form.steeringMode}
(o) => o.value === form.steeringMode, onValueChange={(value) => onFieldChange("steeringMode", value)}
) >
<SelectTrigger className="w-full">
return ( <SelectValue>
<Select {selectedSteeringModeOption
value={form.steeringMode} ? t(
onValueChange={(value) => onFieldChange("steeringMode", value)} selectedSteeringModeOption.labelKey,
> selectedSteeringModeOption.labelDefault,
<SelectTrigger className="w-full"> )
<SelectValue> : form.steeringMode}
{selectedSteeringModeOption </SelectValue>
? t( </SelectTrigger>
selectedSteeringModeOption.labelKey, <SelectContent>
selectedSteeringModeOption.labelDefault, {STEERING_MODE_OPTIONS.map((opt) => (
) <SelectItem key={opt.value} value={opt.value}>
: form.steeringMode} {t(opt.labelKey, opt.labelDefault)}
</SelectValue> </SelectItem>
</SelectTrigger> ))}
<SelectContent> </SelectContent>
{STEERING_MODE_OPTIONS.map((opt) => ( </Select>
<SelectItem key={opt.value} value={opt.value}>
{t(opt.labelKey, opt.labelDefault)}
</SelectItem>
))}
</SelectContent>
</Select>
)
})()}
</Field> </Field>
<Field <Field
@ -618,6 +613,9 @@ interface RuntimeSectionProps {
export function RuntimeSection({ form, onFieldChange }: RuntimeSectionProps) { export function RuntimeSection({ form, onFieldChange }: RuntimeSectionProps) {
const { t } = useTranslation() const { t } = useTranslation()
const selectedLogLevelOption = LOG_LEVEL_OPTIONS.find(
(o) => o.value === form.gatewayLogLevel,
)
const selectedDmScopeOption = DM_SCOPE_OPTIONS.find( const selectedDmScopeOption = DM_SCOPE_OPTIONS.find(
(scope) => scope.value === form.dmScope, (scope) => scope.value === form.dmScope,
) )
@ -704,14 +702,12 @@ export function RuntimeSection({ form, onFieldChange }: RuntimeSectionProps) {
> >
<SelectTrigger className="w-full"> <SelectTrigger className="w-full">
<SelectValue> <SelectValue>
{(() => { {selectedLogLevelOption
const selected = LOG_LEVEL_OPTIONS.find( ? t(
(o) => o.value === form.gatewayLogLevel, selectedLogLevelOption.labelKey,
) selectedLogLevelOption.labelDefault,
return selected )
? t(selected.labelKey, selected.labelDefault) : form.gatewayLogLevel}
: form.gatewayLogLevel
})()}
</SelectValue> </SelectValue>
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>

View file

@ -513,11 +513,11 @@
"max_media_size_hint": "Maximum upload file size in bytes. Leave empty for the default (20 MB).", "max_media_size_hint": "Maximum upload file size in bytes. Leave empty for the default (20 MB).",
"subturn_description": "Configure sub-turn spawning for background tasks and parallel agent work.", "subturn_description": "Configure sub-turn spawning for background tasks and parallel agent work.",
"subturn_max_depth": "Max Depth", "subturn_max_depth": "Max Depth",
"subturn_max_depth_hint": "Maximum nesting depth for spawned sub-turns. Set to 0 to disable spawning.", "subturn_max_depth_hint": "Maximum nesting depth for spawned sub-turns. Values \u2264 0 use the default (3).",
"subturn_max_concurrent": "Max Concurrent", "subturn_max_concurrent": "Max Concurrent",
"subturn_max_concurrent_hint": "Maximum number of sub-turns that can run in parallel.", "subturn_max_concurrent_hint": "Maximum number of sub-turns that can run in parallel.",
"subturn_default_timeout": "Default Timeout (minutes)", "subturn_default_timeout": "Default Timeout (minutes)",
"subturn_default_timeout_hint": "Default timeout in minutes for spawned sub-turns. Set to 0 for no timeout.", "subturn_default_timeout_hint": "Default timeout in minutes for spawned sub-turns. Leave empty or set to 0 to use the system default (5 minutes).",
"subturn_token_budget": "Token Budget", "subturn_token_budget": "Token Budget",
"subturn_token_budget_hint": "Default token budget for each sub-turn. Set to 0 for unlimited.", "subturn_token_budget_hint": "Default token budget for each sub-turn. Set to 0 for unlimited.",
"subturn_concurrency_timeout": "Concurrency Timeout (seconds)", "subturn_concurrency_timeout": "Concurrency Timeout (seconds)",
@ -541,6 +541,7 @@
"log_level_info": "Info", "log_level_info": "Info",
"log_level_warn": "Warn", "log_level_warn": "Warn",
"log_level_error": "Error", "log_level_error": "Error",
"log_level_fatal": "Fatal",
"sections": { "sections": {
"agent": "Agent", "agent": "Agent",
"runtime": "Runtime", "runtime": "Runtime",

View file

@ -513,11 +513,11 @@
"max_media_size_hint": "上传文件的最大字节数。留空使用默认值20 MB。", "max_media_size_hint": "上传文件的最大字节数。留空使用默认值20 MB。",
"subturn_description": "配置子轮次的后台任务派生与并行工作。", "subturn_description": "配置子轮次的后台任务派生与并行工作。",
"subturn_max_depth": "最大深度", "subturn_max_depth": "最大深度",
"subturn_max_depth_hint": "派生子轮次的最大嵌套深度。设为 0 禁用派生。", "subturn_max_depth_hint": "派生子轮次的最大嵌套深度。设为 0 或留空将使用默认值3。",
"subturn_max_concurrent": "最大并发数", "subturn_max_concurrent": "最大并发数",
"subturn_max_concurrent_hint": "可同时运行的子轮次最大数量。", "subturn_max_concurrent_hint": "可同时运行的子轮次最大数量。",
"subturn_default_timeout": "默认超时(分钟)", "subturn_default_timeout": "默认超时(分钟)",
"subturn_default_timeout_hint": "派生子轮次的默认超时时间(分钟)。设为 0 表示无超时。", "subturn_default_timeout_hint": "派生子轮次的默认超时时间(分钟)。留空或设为 0 使用系统默认值5 分钟)。",
"subturn_token_budget": "Token 预算", "subturn_token_budget": "Token 预算",
"subturn_token_budget_hint": "每个子轮次的默认 Token 预算。设为 0 表示不限制。", "subturn_token_budget_hint": "每个子轮次的默认 Token 预算。设为 0 表示不限制。",
"subturn_concurrency_timeout": "并发超时(秒)", "subturn_concurrency_timeout": "并发超时(秒)",
@ -541,6 +541,7 @@
"log_level_info": "信息", "log_level_info": "信息",
"log_level_warn": "警告", "log_level_warn": "警告",
"log_level_error": "错误", "log_level_error": "错误",
"log_level_fatal": "致命",
"sections": { "sections": {
"agent": "智能体", "agent": "智能体",
"runtime": "运行时", "runtime": "运行时",