diff --git a/web/src/components/message-history-window-size-item.tsx b/web/src/components/message-history-window-size-item.tsx index bab9f4ccb..2f2043f3c 100644 --- a/web/src/components/message-history-window-size-item.tsx +++ b/web/src/components/message-history-window-size-item.tsx @@ -1,4 +1,5 @@ import { Form, InputNumber } from 'antd'; +import { useMemo } from 'react'; import { useFormContext } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { @@ -8,7 +9,7 @@ import { FormLabel, FormMessage, } from './ui/form'; -import { Input } from './ui/input'; +import { BlurInput, Input } from './ui/input'; const MessageHistoryWindowSizeItem = ({ initialValue, @@ -31,10 +32,20 @@ const MessageHistoryWindowSizeItem = ({ export default MessageHistoryWindowSizeItem; -export function MessageHistoryWindowSizeFormField() { +type MessageHistoryWindowSizeFormFieldProps = { + useBlurInput?: boolean; +}; + +export function MessageHistoryWindowSizeFormField({ + useBlurInput = false, +}: MessageHistoryWindowSizeFormFieldProps) { const form = useFormContext(); const { t } = useTranslation(); + const NextInput = useMemo(() => { + return useBlurInput ? BlurInput : Input; + }, [useBlurInput]); + return ( - + diff --git a/web/src/components/originui/select-with-search.tsx b/web/src/components/originui/select-with-search.tsx index c5b0c853e..cf0e2b469 100644 --- a/web/src/components/originui/select-with-search.tsx +++ b/web/src/components/originui/select-with-search.tsx @@ -1,7 +1,14 @@ 'use client'; import { CheckIcon, ChevronDownIcon } from 'lucide-react'; -import { Fragment, useCallback, useEffect, useId, useState } from 'react'; +import { + Fragment, + forwardRef, + useCallback, + useEffect, + useId, + useState, +} from 'react'; import { Button } from '@/components/ui/button'; import { @@ -72,11 +79,10 @@ export type SelectWithSearchFlagProps = { onChange?(value: string): void; }; -export function SelectWithSearch({ - value: val = '', - onChange, - options = countries, -}: SelectWithSearchFlagProps) { +export const SelectWithSearch = forwardRef< + React.ElementRef, + SelectWithSearchFlagProps +>(({ value: val = '', onChange, options = countries }, ref) => { const id = useId(); const [open, setOpen] = useState(false); const [value, setValue] = useState(''); @@ -102,6 +108,7 @@ export function SelectWithSearch({ variant="outline" role="combobox" aria-expanded={open} + ref={ref} className="bg-background hover:bg-background border-input w-full justify-between px-3 font-normal outline-offset-0 outline-none focus-visible:outline-[3px]" > {value ? ( @@ -160,4 +167,4 @@ export function SelectWithSearch({ ); -} +}); diff --git a/web/src/components/ui/input.tsx b/web/src/components/ui/input.tsx index 309d3848e..c05dece03 100644 --- a/web/src/components/ui/input.tsx +++ b/web/src/components/ui/input.tsx @@ -67,4 +67,42 @@ const SearchInput = (props: InputProps) => { ); }; +type Value = string | readonly string[] | number | undefined; + +export const InnerBlurInput = React.forwardRef< + HTMLInputElement, + InputProps & { value: Value; onChange(value: Value): void } +>(({ value, onChange, ...props }, ref) => { + const [val, setVal] = React.useState(); + + const handleChange: React.ChangeEventHandler = + React.useCallback((e) => { + setVal(e.target.value); + }, []); + + const handleBlur: React.FocusEventHandler = + React.useCallback( + (e) => { + onChange?.(e.target.value); + }, + [onChange], + ); + + React.useEffect(() => { + setVal(value); + }, [value]); + + return ( + + ); +}); + +export const BlurInput = React.memo(InnerBlurInput); + export { ExpandedInput, Input, SearchInput }; diff --git a/web/src/components/ui/textarea.tsx b/web/src/components/ui/textarea.tsx index 5956859d4..f42863caf 100644 --- a/web/src/components/ui/textarea.tsx +++ b/web/src/components/ui/textarea.tsx @@ -20,3 +20,42 @@ const Textarea = React.forwardRef< Textarea.displayName = 'Textarea'; export { Textarea }; + +type Value = string | readonly string[] | number | undefined; + +export const BlurTextarea = React.forwardRef< + HTMLTextAreaElement, + React.ComponentProps<'textarea'> & { + value: Value; + onChange(value: Value): void; + } +>(({ value, onChange, ...props }, ref) => { + const [val, setVal] = React.useState(); + + const handleChange: React.ChangeEventHandler = + React.useCallback((e) => { + setVal(e.target.value); + }, []); + + const handleBlur: React.FocusEventHandler = + React.useCallback( + (e) => { + onChange?.(e.target.value); + }, + [onChange], + ); + + React.useEffect(() => { + setVal(value); + }, [value]); + + return ( + + ); +}); diff --git a/web/src/pages/agent/form-sheet/next.tsx b/web/src/pages/agent/form-sheet/next.tsx index df390fdf3..0ebb55ab5 100644 --- a/web/src/pages/agent/form-sheet/next.tsx +++ b/web/src/pages/agent/form-sheet/next.tsx @@ -11,6 +11,7 @@ import { RAGFlowNodeType } from '@/interfaces/database/flow'; import { cn } from '@/lib/utils'; import { zodResolver } from '@hookform/resolvers/zod'; import { get, isPlainObject, lowerFirst } from 'lodash'; +import omit from 'lodash/omit'; import { Play, X } from 'lucide-react'; import { useEffect, useRef } from 'react'; import { useForm } from 'react-hook-form'; @@ -54,7 +55,7 @@ const FormSheet = ({ const OperatorForm = currentFormMap.component ?? EmptyContent; const form = useForm({ - defaultValues: currentFormMap.defaultValues, + values: currentFormMap.defaultValues, resolver: zodResolver(currentFormMap.schema), }); @@ -89,10 +90,16 @@ const FormSheet = ({ if (isPlainObject(formData)) { // form.setFieldsValue({ ...formData, items }); console.info('xxx'); - form.reset({ ...formData, items }); + const nextValues = { + ...omit(formData, 'category_description'), + items, + }; + // Object.entries(nextValues).forEach(([key, value]) => { + // form.setValue(key, value, { shouldDirty: false }); + // }); + form.reset(nextValues); } - } - if (operatorName === Operator.Message) { + } else if (operatorName === Operator.Message) { form.reset({ ...formData, content: convertToObjectArray(formData.content), diff --git a/web/src/pages/agent/form-sheet/use-form-config-map.tsx b/web/src/pages/agent/form-sheet/use-form-config-map.tsx index 134361e76..082140eed 100644 --- a/web/src/pages/agent/form-sheet/use-form-config-map.tsx +++ b/web/src/pages/agent/form-sheet/use-form-config-map.tsx @@ -124,15 +124,26 @@ export function useFormConfigMap() { presencePenaltyEnabled: true, frequencyPenaltyEnabled: true, maxTokensEnabled: true, + items: [], }, schema: z.object({ parameter: z.string().optional(), ...LlmSettingSchema, - message_history_window_size: z.number(), + message_history_window_size: z.coerce.number(), items: z.array( - z.object({ - name: z.string().min(1, t('flow.nameMessage')).trim(), - }), + z + .object({ + name: z.string().min(1, t('flow.nameMessage')).trim(), + description: z.string().optional(), + // examples: z + // .array( + // z.object({ + // value: z.string(), + // }), + // ) + // .optional(), + }) + .optional(), ), }), }, @@ -180,6 +191,12 @@ export function useFormConfigMap() { arguments: z.array( z.object({ name: z.string(), component_id: z.string() }), ), + return: z.union([ + z + .array(z.object({ name: z.string(), component_id: z.string() })) + .optional(), + z.object({ name: z.string(), component_id: z.string() }), + ]), }), }, [Operator.WaitingDialogue]: { diff --git a/web/src/pages/agent/form/categorize-form/dynamic-categorize.tsx b/web/src/pages/agent/form/categorize-form/dynamic-categorize.tsx index 6302e032a..5b4712053 100644 --- a/web/src/pages/agent/form/categorize-form/dynamic-categorize.tsx +++ b/web/src/pages/agent/form/categorize-form/dynamic-categorize.tsx @@ -12,8 +12,7 @@ import { FormMessage, } from '@/components/ui/form'; import { Input } from '@/components/ui/input'; -import { RAGFlowSelect } from '@/components/ui/select'; -import { Textarea } from '@/components/ui/textarea'; +import { BlurTextarea } from '@/components/ui/textarea'; import { useTranslate } from '@/hooks/common-hooks'; import { PlusOutlined } from '@ant-design/icons'; import { useUpdateNodeInternals } from '@xyflow/react'; @@ -23,6 +22,7 @@ import { ChevronsUpDown, X } from 'lucide-react'; import { ChangeEventHandler, FocusEventHandler, + memo, useCallback, useEffect, useState, @@ -104,7 +104,7 @@ const NameInput = ({ ); }; -const FormSet = ({ nodeId, index }: IProps & { index: number }) => { +const InnerFormSet = ({ nodeId, index }: IProps & { index: number }) => { const form = useFormContext(); const { t } = useTranslate('flow'); const buildCategorizeToOptions = useBuildFormSelectOptions( @@ -152,13 +152,13 @@ const FormSet = ({ nodeId, index }: IProps & { index: number }) => { {t('description')} -