From ba93ac3bd790dd6ced82de60928da97f80ff25e4 Mon Sep 17 00:00:00 2001 From: balibabu Date: Mon, 15 Jun 2026 19:09:19 +0800 Subject: [PATCH] Feat: Move less important chat settings into a collapsible panel. (#16024) ### What problem does this PR solve? Feat: Move less important chat settings into a collapsible panel. ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/components/collapse.tsx | 10 +- web/src/components/llm-setting-items/next.tsx | 206 +++++++++-------- web/src/pages/agent/form/agent-form/index.tsx | 2 +- web/src/pages/agent/form/begin-form/index.tsx | 24 +- .../pages/agent/form/retrieval-form/next.tsx | 2 +- .../form/tool-form/retrieval-form/index.tsx | 2 +- .../chat/app-settings/chat-basic-settings.tsx | 154 ++----------- .../chat/app-settings/chat-model-settings.tsx | 12 - .../chat/app-settings/chat-prompt-engine.tsx | 211 +++++++++++++++--- .../chat/app-settings/chat-settings.tsx | 5 - 10 files changed, 323 insertions(+), 305 deletions(-) delete mode 100644 web/src/pages/next-chats/chat/app-settings/chat-model-settings.tsx diff --git a/web/src/components/collapse.tsx b/web/src/components/collapse.tsx index 463ff57cc5..e0d2d59240 100644 --- a/web/src/components/collapse.tsx +++ b/web/src/components/collapse.tsx @@ -29,7 +29,7 @@ export function Collapse({ title, children, rightContent, - open = true, + open, defaultOpen = false, onOpenChange, disabled, @@ -37,8 +37,12 @@ export function Collapse({ const [currentOpen, setCurrentOpen] = useState(open); useEffect(() => { - setCurrentOpen(open); - }, [open]); + if (typeof open === 'boolean') { + setCurrentOpen(open); + } else { + setCurrentOpen(defaultOpen); + } + }, [defaultOpen, open]); const handleOpenChange = useCallback( (open: boolean) => { diff --git a/web/src/components/llm-setting-items/next.tsx b/web/src/components/llm-setting-items/next.tsx index 34c3c8d55f..66344a4cf8 100644 --- a/web/src/components/llm-setting-items/next.tsx +++ b/web/src/components/llm-setting-items/next.tsx @@ -4,9 +4,10 @@ import { } from '@/constants/knowledge'; import { useTranslate } from '@/hooks/common-hooks'; import { camelCase, isEqual } from 'lodash'; -import { useCallback } from 'react'; +import React, { useCallback } from 'react'; import { useFormContext } from 'react-hook-form'; import { z } from 'zod'; +import { Collapse } from '../collapse'; import { FormControl, FormField, @@ -38,6 +39,7 @@ interface LlmSettingFieldItemsProps { | 'frequency_penalty' | 'max_tokens' >; + showCollapse?: boolean; } export const LLMIdFormField = { @@ -80,10 +82,18 @@ export function LlmSettingFieldItems({ 'max_tokens', ], llmId, + showCollapse = false, }: LlmSettingFieldItemsProps) { const form = useFormContext(); const { t } = useTranslate('chat'); + const CollapseComponent = showCollapse ? Collapse : React.Fragment; + const collapseProps = showCollapse + ? { + title: t('modelSetting'), + } + : {}; + const getFieldWithPrefix = useCallback( (name: string) => { return prefix ? `${prefix}.${name}` : name; @@ -142,101 +152,105 @@ export function LlmSettingFieldItems({ testId={llmSelectTestId} optionTestIdPrefix={llmOptionTestIdPrefix} > - ( - - {t('freedom')} - - - - - - )} - /> - {showFields.some((item) => item === 'temperature') && ( - { - checkParameterIsEqual(); - }} - > - )} - {showFields.some((item) => item === 'top_p') && ( - { - checkParameterIsEqual(); - }} - > - )} - {showFields.some((item) => item === 'presence_penalty') && ( - { - checkParameterIsEqual(); - }} - > - )} - {showFields.some((item) => item === 'frequency_penalty') && ( - { - checkParameterIsEqual(); - }} - > - )} - {showFields.some((item) => item === 'max_tokens') && ( - { - checkParameterIsEqual(); - }} - > - )} + +
+ ( + + {t('freedom')} + + + + + + )} + /> + {showFields.some((item) => item === 'temperature') && ( + { + checkParameterIsEqual(); + }} + > + )} + {showFields.some((item) => item === 'top_p') && ( + { + checkParameterIsEqual(); + }} + > + )} + {showFields.some((item) => item === 'presence_penalty') && ( + { + checkParameterIsEqual(); + }} + > + )} + {showFields.some((item) => item === 'frequency_penalty') && ( + { + checkParameterIsEqual(); + }} + > + )} + {showFields.some((item) => item === 'max_tokens') && ( + { + checkParameterIsEqual(); + }} + > + )} +
+
); } diff --git a/web/src/pages/agent/form/agent-form/index.tsx b/web/src/pages/agent/form/agent-form/index.tsx index 17ecb17605..7bdd16ad6d 100644 --- a/web/src/pages/agent/form/agent-form/index.tsx +++ b/web/src/pages/agent/form/agent-form/index.tsx @@ -207,7 +207,7 @@ function AgentForm({ node }: INextOperatorForm) { - {t('flow.advancedSettings')}}> + {t('flow.advancedSettings')}}>
({ + control: form.control, + name: 'inputs', + }); const mode = useWatch({ control: form.control, name: 'mode' }); const hasFileInput = useMemo( @@ -89,6 +92,10 @@ function BeginForm({ node }: INextOperatorForm) { node, }); + const handleAddQuery = useCallback(() => { + showModal(); + }, [showModal]); + return (
@@ -167,6 +174,7 @@ function BeginForm({ node }: INextOperatorForm) { render={() =>
} /> {t('flow.input')} @@ -174,19 +182,13 @@ function BeginForm({ node }: INextOperatorForm) { } rightContent={ - } > diff --git a/web/src/pages/agent/form/retrieval-form/next.tsx b/web/src/pages/agent/form/retrieval-form/next.tsx index 6e831cedf2..9adf93c5b3 100644 --- a/web/src/pages/agent/form/retrieval-form/next.tsx +++ b/web/src/pages/agent/form/retrieval-form/next.tsx @@ -166,7 +166,7 @@ function RetrievalForm({ node }: INextOperatorForm) { - {t('flow.advancedSettings')}}> + {t('flow.advancedSettings')}}>
{ - {t('flow.advancedSettings')}}> + {t('flow.advancedSettings')}}> (rawDatasetIds || []) as string[], - [rawDatasetIds], - ); - const metadataInclude = useWatch({ - control: form.control, - name: 'prompt_config.reference_metadata.include', - }); - const { data: metadataKeys, loading: metadataKeysLoading } = - useFetchKnowledgeMetadataKeys(kbIds); - const metadataFieldOptions = useMemo(() => { - return (metadataKeys || []).map((key) => ({ - label: key, - value: key, - })); - }, [metadataKeys]); - useEffect(() => { - const currentFields = form.getValues( - 'prompt_config.reference_metadata.fields', - ); - if ( - metadataInclude && - Array.isArray(currentFields) && - currentFields.length > 0 && - metadataKeys - ) { - const validFields = currentFields.filter((field) => - metadataKeys.includes(field), - ); - if (validFields.length !== currentFields.length) { - form.setValue('prompt_config.reference_metadata.fields', validFields); - } - } else if (!metadataInclude) { - form.setValue('prompt_config.reference_metadata.fields', undefined); - } - }, [kbIds, metadataKeys, metadataKeysLoading, metadataInclude, form]); + const prologueValue = useWatch({ + control: form.control, + name: 'prompt_config.prologue', + }); return (
- ( - - - {t('emptyResponse')} - - - - - - - )} - /> + + )} /> - - - - - + - - ( - - - { - field.onChange(value); - if (!value) { - form.setValue( - 'prompt_config.reference_metadata.fields', - undefined, - ); - } - }} - /> - - - Show chunk metadata - - - )} - /> - {metadataInclude && ( - ( - - - {t('metadataKeys')} - - - - - - - )} - /> - )}
); -} \ No newline at end of file +} diff --git a/web/src/pages/next-chats/chat/app-settings/chat-model-settings.tsx b/web/src/pages/next-chats/chat/app-settings/chat-model-settings.tsx deleted file mode 100644 index 3fb3b2a274..0000000000 --- a/web/src/pages/next-chats/chat/app-settings/chat-model-settings.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { LlmSettingFieldItems } from '@/components/llm-setting-items/next'; - -export function ChatModelSettings() { - return ( -
- -
- ); -} diff --git a/web/src/pages/next-chats/chat/app-settings/chat-prompt-engine.tsx b/web/src/pages/next-chats/chat/app-settings/chat-prompt-engine.tsx index a94b323e1f..b504325e4d 100644 --- a/web/src/pages/next-chats/chat/app-settings/chat-prompt-engine.tsx +++ b/web/src/pages/next-chats/chat/app-settings/chat-prompt-engine.tsx @@ -1,9 +1,13 @@ 'use client'; +import { Collapse } from '@/components/collapse'; import { CrossLanguageFormField } from '@/components/cross-language-form-field'; +import { MetadataFilter } from '@/components/metadata-filter'; import { RerankFormFields } from '@/components/rerank'; import { SimilaritySliderFormField } from '@/components/similarity-slider'; import { SwitchFormField } from '@/components/switch-fom-field'; +import { TavilyFormField } from '@/components/tavily-form-field'; +import { TOCEnhanceFormField } from '@/components/toc-enhance-form-field'; import { TopNFormField } from '@/components/top-n-item'; import { FormControl, @@ -12,50 +16,189 @@ import { FormLabel, FormMessage, } from '@/components/ui/form'; +import { MultiSelect } from '@/components/ui/multi-select'; +import { Switch } from '@/components/ui/switch'; import { Textarea } from '@/components/ui/textarea'; import { UseKnowledgeGraphFormField } from '@/components/use-knowledge-graph-item'; -import { useTranslate } from '@/hooks/common-hooks'; +import { useFetchKnowledgeMetadataKeys } from '@/hooks/use-knowledge-request'; import { getDirAttribute } from '@/utils/text-direction'; -import { useFormContext } from 'react-hook-form'; +import { useEffect, useMemo } from 'react'; +import { useFormContext, useWatch } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; import { DynamicVariableForm } from './dynamic-variable'; export function ChatPromptEngine() { - const { t } = useTranslate('chat'); + const { t } = useTranslation(); const form = useFormContext(); const systemPromptValue = form.watch('prompt_config.system'); + const emptyResponseValue = form.watch('prompt_config.empty_response'); + const rawDatasetIds = useWatch({ + control: form.control, + name: 'dataset_ids', + }); + const kbIds = useMemo( + () => (rawDatasetIds || []) as string[], + [rawDatasetIds], + ); + const metadataInclude = useWatch({ + control: form.control, + name: 'prompt_config.reference_metadata.include', + }); + const { data: metadataKeys, loading: metadataKeysLoading } = + useFetchKnowledgeMetadataKeys(kbIds); + const metadataFieldOptions = useMemo(() => { + return (metadataKeys || []).map((key) => ({ + label: key, + value: key, + })); + }, [metadataKeys]); + + useEffect(() => { + const currentFields = form.getValues( + 'prompt_config.reference_metadata.fields', + ); + if ( + metadataInclude && + Array.isArray(currentFields) && + currentFields.length > 0 && + metadataKeys + ) { + const validFields = currentFields.filter((field) => + metadataKeys.includes(field), + ); + if (validFields.length !== currentFields.length) { + form.setValue('prompt_config.reference_metadata.fields', validFields); + } + } else if (!metadataInclude) { + form.setValue('prompt_config.reference_metadata.fields', undefined); + } + }, [kbIds, metadataKeys, metadataKeysLoading, metadataInclude, form]); + return ( -
- ( - - {t('system')} - - + + + + )} + /> + + + + + + + ( + + + { + field.onChange(value); + if (!value) { + form.setValue( + 'prompt_config.reference_metadata.fields', + undefined, + ); + } + }} + /> + + + Show chunk metadata + + + )} + /> + {metadataInclude && ( + ( + + + {t('chat.metadataKeys')} + + + + + + + )} + /> )} - /> - - - - - - - -
+ ( + + {t('chat.system')} + +