### What problem does this PR solve? Feat: Add the SelectWithSearch component #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.19.1
| } from '@/components/ui/form'; | } from '@/components/ui/form'; | ||||
| import { useFormContext } from 'react-hook-form'; | import { useFormContext } from 'react-hook-form'; | ||||
| import { useTranslation } from 'react-i18next'; | import { useTranslation } from 'react-i18next'; | ||||
| import { NextLLMSelect } from './llm-select'; | |||||
| import { NextLLMSelect } from './llm-select/next'; | |||||
| export function LargeModelFormField() { | export function LargeModelFormField() { | ||||
| const form = useFormContext(); | const form = useFormContext(); |
| import { LlmModelType } from '@/constants/knowledge'; | import { LlmModelType } from '@/constants/knowledge'; | ||||
| import { useComposeLlmOptionsByModelTypes } from '@/hooks/llm-hooks'; | import { useComposeLlmOptionsByModelTypes } from '@/hooks/llm-hooks'; | ||||
| import * as SelectPrimitive from '@radix-ui/react-select'; | |||||
| import { Popover as AntPopover, Select as AntSelect } from 'antd'; | import { Popover as AntPopover, Select as AntSelect } from 'antd'; | ||||
| import { forwardRef, useState } from 'react'; | |||||
| import LlmSettingItems from '../llm-setting-items'; | import LlmSettingItems from '../llm-setting-items'; | ||||
| import { LlmSettingFieldItems } from '../llm-setting-items/next'; | |||||
| import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover'; | |||||
| import { Select, SelectTrigger, SelectValue } from '../ui/select'; | |||||
| interface IProps { | interface IProps { | ||||
| id?: string; | id?: string; | ||||
| disabled?: boolean; | disabled?: boolean; | ||||
| } | } | ||||
| const LLMSelect = ({ id, value, onInitialValue, onChange, disabled }: IProps) => { | |||||
| const LLMSelect = ({ | |||||
| id, | |||||
| value, | |||||
| onInitialValue, | |||||
| onChange, | |||||
| disabled, | |||||
| }: IProps) => { | |||||
| const modelOptions = useComposeLlmOptionsByModelTypes([ | const modelOptions = useComposeLlmOptionsByModelTypes([ | ||||
| LlmModelType.Chat, | LlmModelType.Chat, | ||||
| LlmModelType.Image2text, | LlmModelType.Image2text, | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| } | |||||
| const content = ( | const content = ( | ||||
| <div style={{ width: 400 }}> | <div style={{ width: 400 }}> | ||||
| <LlmSettingItems onChange={onChange} | |||||
| <LlmSettingItems | |||||
| onChange={onChange} | |||||
| formItemLayout={{ labelCol: { span: 10 }, wrapperCol: { span: 14 } }} | formItemLayout={{ labelCol: { span: 10 }, wrapperCol: { span: 14 } }} | ||||
| ></LlmSettingItems> | ></LlmSettingItems> | ||||
| </div> | </div> | ||||
| }; | }; | ||||
| export default LLMSelect; | export default LLMSelect; | ||||
| export const NextLLMSelect = forwardRef< | |||||
| React.ElementRef<typeof SelectPrimitive.Trigger>, | |||||
| IProps | |||||
| >(({ value, disabled }, ref) => { | |||||
| const [isPopoverOpen, setIsPopoverOpen] = useState(false); | |||||
| const modelOptions = useComposeLlmOptionsByModelTypes([ | |||||
| LlmModelType.Chat, | |||||
| LlmModelType.Image2text, | |||||
| ]); | |||||
| return ( | |||||
| <Select disabled={disabled} value={value}> | |||||
| <Popover open={isPopoverOpen} onOpenChange={setIsPopoverOpen}> | |||||
| <PopoverTrigger asChild> | |||||
| <SelectTrigger | |||||
| onClick={(e) => { | |||||
| e.preventDefault(); | |||||
| setIsPopoverOpen(true); | |||||
| }} | |||||
| ref={ref} | |||||
| > | |||||
| <SelectValue> | |||||
| { | |||||
| modelOptions | |||||
| .flatMap((x) => x.options) | |||||
| .find((x) => x.value === value)?.label | |||||
| } | |||||
| </SelectValue> | |||||
| </SelectTrigger> | |||||
| </PopoverTrigger> | |||||
| <PopoverContent side={'left'}> | |||||
| <LlmSettingFieldItems></LlmSettingFieldItems> | |||||
| </PopoverContent> | |||||
| </Popover> | |||||
| </Select> | |||||
| ); | |||||
| }); | |||||
| NextLLMSelect.displayName = 'LLMSelect'; |
| import { LlmModelType } from '@/constants/knowledge'; | |||||
| import { useComposeLlmOptionsByModelTypes } from '@/hooks/llm-hooks'; | |||||
| import * as SelectPrimitive from '@radix-ui/react-select'; | |||||
| import { forwardRef, useState } from 'react'; | |||||
| import { LlmSettingFieldItems } from '../llm-setting-items/next'; | |||||
| import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover'; | |||||
| import { Select, SelectTrigger, SelectValue } from '../ui/select'; | |||||
| interface IProps { | |||||
| id?: string; | |||||
| value?: string; | |||||
| onInitialValue?: (value: string, option: any) => void; | |||||
| onChange?: (value: string, option: any) => void; | |||||
| disabled?: boolean; | |||||
| } | |||||
| export const NextLLMSelect = forwardRef< | |||||
| React.ElementRef<typeof SelectPrimitive.Trigger>, | |||||
| IProps | |||||
| >(({ value, disabled }, ref) => { | |||||
| const [isPopoverOpen, setIsPopoverOpen] = useState(false); | |||||
| const modelOptions = useComposeLlmOptionsByModelTypes([ | |||||
| LlmModelType.Chat, | |||||
| LlmModelType.Image2text, | |||||
| ]); | |||||
| return ( | |||||
| <Select disabled={disabled} value={value}> | |||||
| <Popover open={isPopoverOpen} onOpenChange={setIsPopoverOpen}> | |||||
| <PopoverTrigger asChild> | |||||
| <SelectTrigger | |||||
| onClick={(e) => { | |||||
| e.preventDefault(); | |||||
| setIsPopoverOpen(true); | |||||
| }} | |||||
| ref={ref} | |||||
| > | |||||
| <SelectValue> | |||||
| { | |||||
| modelOptions | |||||
| .flatMap((x) => x.options) | |||||
| .find((x) => x.value === value)?.label | |||||
| } | |||||
| </SelectValue> | |||||
| </SelectTrigger> | |||||
| </PopoverTrigger> | |||||
| <PopoverContent side={'left'}> | |||||
| <LlmSettingFieldItems></LlmSettingFieldItems> | |||||
| </PopoverContent> | |||||
| </Popover> | |||||
| </Select> | |||||
| ); | |||||
| }); | |||||
| NextLLMSelect.displayName = 'LLMSelect'; |
| import { camelCase } from 'lodash'; | import { camelCase } from 'lodash'; | ||||
| import { useCallback } from 'react'; | import { useCallback } from 'react'; | ||||
| import { useFormContext } from 'react-hook-form'; | import { useFormContext } from 'react-hook-form'; | ||||
| import { z } from 'zod'; | |||||
| import { | import { | ||||
| FormControl, | FormControl, | ||||
| FormField, | FormField, | ||||
| FormLabel, | FormLabel, | ||||
| FormMessage, | FormMessage, | ||||
| } from '../ui/form'; | } from '../ui/form'; | ||||
| import { Input } from '../ui/input'; | |||||
| import { | import { | ||||
| Select, | Select, | ||||
| SelectContent, | SelectContent, | ||||
| SelectTrigger, | SelectTrigger, | ||||
| SelectValue, | SelectValue, | ||||
| } from '../ui/select'; | } from '../ui/select'; | ||||
| import { FormSlider } from '../ui/slider'; | |||||
| import { Switch } from '../ui/switch'; | |||||
| interface SliderWithInputNumberFormFieldProps { | |||||
| name: string; | |||||
| label: string; | |||||
| checkName: string; | |||||
| max: number; | |||||
| min?: number; | |||||
| step?: number; | |||||
| } | |||||
| function SliderWithInputNumberFormField({ | |||||
| name, | |||||
| label, | |||||
| checkName, | |||||
| max, | |||||
| min = 0, | |||||
| step = 1, | |||||
| }: SliderWithInputNumberFormFieldProps) { | |||||
| const { control, watch } = useFormContext(); | |||||
| const { t } = useTranslate('chat'); | |||||
| const disabled = !watch(checkName); | |||||
| return ( | |||||
| <FormField | |||||
| control={control} | |||||
| name={name} | |||||
| render={({ field }) => ( | |||||
| <FormItem> | |||||
| <div className="flex items-center justify-between"> | |||||
| <FormLabel>{t(label)}</FormLabel> | |||||
| <FormField | |||||
| control={control} | |||||
| name={checkName} | |||||
| render={({ field }) => ( | |||||
| <FormItem> | |||||
| <FormControl> | |||||
| <Switch | |||||
| {...field} | |||||
| checked={field.value} | |||||
| onCheckedChange={field.onChange} | |||||
| ></Switch> | |||||
| </FormControl> | |||||
| <FormMessage /> | |||||
| </FormItem> | |||||
| )} | |||||
| /> | |||||
| </div> | |||||
| <FormControl> | |||||
| <div className="flex w-full items-center space-x-2"> | |||||
| <FormSlider | |||||
| {...field} | |||||
| disabled={disabled} | |||||
| max={max} | |||||
| min={min} | |||||
| step={step} | |||||
| ></FormSlider> | |||||
| <Input | |||||
| type={'number'} | |||||
| className="w-2/5" | |||||
| {...field} | |||||
| disabled={disabled} | |||||
| max={max} | |||||
| min={min} | |||||
| step={step} | |||||
| /> | |||||
| </div> | |||||
| </FormControl> | |||||
| <FormMessage /> | |||||
| </FormItem> | |||||
| )} | |||||
| /> | |||||
| ); | |||||
| } | |||||
| import { SliderInputSwitchFormField } from './slider'; | |||||
| interface LlmSettingFieldItemsProps { | interface LlmSettingFieldItemsProps { | ||||
| prefix?: string; | prefix?: string; | ||||
| } | } | ||||
| export const LlmSettingSchema = { | |||||
| llm_id: z.string(), | |||||
| temperature: z.coerce.number(), | |||||
| top_p: z.string(), | |||||
| presence_penalty: z.coerce.number(), | |||||
| frequency_penalty: z.coerce.number(), | |||||
| }; | |||||
| export function LlmSettingFieldItems({ prefix }: LlmSettingFieldItemsProps) { | export function LlmSettingFieldItems({ prefix }: LlmSettingFieldItemsProps) { | ||||
| const form = useFormContext(); | const form = useFormContext(); | ||||
| const { t } = useTranslate('chat'); | const { t } = useTranslate('chat'); | ||||
| ); | ); | ||||
| return ( | return ( | ||||
| <div className="space-y-8"> | |||||
| <div className="space-y-5"> | |||||
| <FormField | <FormField | ||||
| control={form.control} | control={form.control} | ||||
| name={'llm_id'} | name={'llm_id'} | ||||
| </FormItem> | </FormItem> | ||||
| )} | )} | ||||
| /> | /> | ||||
| <SliderWithInputNumberFormField | |||||
| <SliderInputSwitchFormField | |||||
| name={getFieldWithPrefix('temperature')} | name={getFieldWithPrefix('temperature')} | ||||
| checkName="temperatureEnabled" | checkName="temperatureEnabled" | ||||
| label="temperature" | label="temperature" | ||||
| max={1} | max={1} | ||||
| step={0.01} | step={0.01} | ||||
| ></SliderWithInputNumberFormField> | |||||
| <SliderWithInputNumberFormField | |||||
| ></SliderInputSwitchFormField> | |||||
| <SliderInputSwitchFormField | |||||
| name={getFieldWithPrefix('top_p')} | name={getFieldWithPrefix('top_p')} | ||||
| checkName="topPEnabled" | checkName="topPEnabled" | ||||
| label="topP" | label="topP" | ||||
| max={1} | max={1} | ||||
| step={0.01} | step={0.01} | ||||
| ></SliderWithInputNumberFormField> | |||||
| <SliderWithInputNumberFormField | |||||
| ></SliderInputSwitchFormField> | |||||
| <SliderInputSwitchFormField | |||||
| name={getFieldWithPrefix('presence_penalty')} | name={getFieldWithPrefix('presence_penalty')} | ||||
| checkName="presencePenaltyEnabled" | checkName="presencePenaltyEnabled" | ||||
| label="presencePenalty" | label="presencePenalty" | ||||
| max={1} | max={1} | ||||
| step={0.01} | step={0.01} | ||||
| ></SliderWithInputNumberFormField> | |||||
| <SliderWithInputNumberFormField | |||||
| ></SliderInputSwitchFormField> | |||||
| <SliderInputSwitchFormField | |||||
| name={getFieldWithPrefix('frequency_penalty')} | name={getFieldWithPrefix('frequency_penalty')} | ||||
| checkName="frequencyPenaltyEnabled" | checkName="frequencyPenaltyEnabled" | ||||
| label="frequencyPenalty" | label="frequencyPenalty" | ||||
| max={1} | max={1} | ||||
| step={0.01} | step={0.01} | ||||
| ></SliderWithInputNumberFormField> | |||||
| <SliderWithInputNumberFormField | |||||
| ></SliderInputSwitchFormField> | |||||
| <SliderInputSwitchFormField | |||||
| name={getFieldWithPrefix('max_tokens')} | name={getFieldWithPrefix('max_tokens')} | ||||
| checkName="maxTokensEnabled" | checkName="maxTokensEnabled" | ||||
| label="maxTokens" | label="maxTokens" | ||||
| max={128000} | max={128000} | ||||
| ></SliderWithInputNumberFormField> | |||||
| ></SliderInputSwitchFormField> | |||||
| </div> | </div> | ||||
| ); | ); | ||||
| } | } |
| import { useTranslate } from '@/hooks/common-hooks'; | |||||
| import { cn } from '@/lib/utils'; | |||||
| import { useFormContext } from 'react-hook-form'; | |||||
| import { SingleFormSlider } from '../ui/dual-range-slider'; | |||||
| import { | |||||
| FormControl, | |||||
| FormField, | |||||
| FormItem, | |||||
| FormLabel, | |||||
| FormMessage, | |||||
| } from '../ui/form'; | |||||
| import { Input } from '../ui/input'; | |||||
| import { Switch } from '../ui/switch'; | |||||
| type SliderInputSwitchFormFieldProps = { | |||||
| max?: number; | |||||
| min?: number; | |||||
| step?: number; | |||||
| name: string; | |||||
| label: string; | |||||
| defaultValue?: number; | |||||
| className?: string; | |||||
| checkName: string; | |||||
| }; | |||||
| export function SliderInputSwitchFormField({ | |||||
| max, | |||||
| min, | |||||
| step, | |||||
| label, | |||||
| name, | |||||
| defaultValue, | |||||
| className, | |||||
| checkName, | |||||
| }: SliderInputSwitchFormFieldProps) { | |||||
| const form = useFormContext(); | |||||
| const disabled = !form.watch(checkName); | |||||
| const { t } = useTranslate('chat'); | |||||
| return ( | |||||
| <FormField | |||||
| control={form.control} | |||||
| name={name} | |||||
| defaultValue={defaultValue} | |||||
| render={({ field }) => ( | |||||
| <FormItem> | |||||
| <FormLabel tooltip={t(`${label}Tip`)}>{t(label)}</FormLabel> | |||||
| <div | |||||
| className={cn('flex items-center gap-4 justify-between', className)} | |||||
| > | |||||
| <FormField | |||||
| control={form.control} | |||||
| name={checkName} | |||||
| render={({ field }) => ( | |||||
| <FormItem> | |||||
| <FormControl> | |||||
| <Switch | |||||
| checked={field.value} | |||||
| onCheckedChange={field.onChange} | |||||
| /> | |||||
| </FormControl> | |||||
| <FormMessage /> | |||||
| </FormItem> | |||||
| )} | |||||
| /> | |||||
| <FormControl> | |||||
| <SingleFormSlider | |||||
| {...field} | |||||
| max={max} | |||||
| min={min} | |||||
| step={step} | |||||
| disabled={disabled} | |||||
| ></SingleFormSlider> | |||||
| </FormControl> | |||||
| <FormControl> | |||||
| <Input | |||||
| disabled={disabled} | |||||
| type={'number'} | |||||
| className="h-7 w-20" | |||||
| max={max} | |||||
| min={min} | |||||
| step={step} | |||||
| {...field} | |||||
| ></Input> | |||||
| </FormControl> | |||||
| </div> | |||||
| <FormMessage /> | |||||
| </FormItem> | |||||
| )} | |||||
| /> | |||||
| ); | |||||
| } |
| 'use client'; | |||||
| import { CheckIcon, ChevronDownIcon } from 'lucide-react'; | |||||
| import { Fragment, useCallback, useEffect, useId, useState } from 'react'; | |||||
| import { Button } from '@/components/ui/button'; | |||||
| import { | |||||
| Command, | |||||
| CommandEmpty, | |||||
| CommandGroup, | |||||
| CommandInput, | |||||
| CommandItem, | |||||
| CommandList, | |||||
| } from '@/components/ui/command'; | |||||
| import { | |||||
| Popover, | |||||
| PopoverContent, | |||||
| PopoverTrigger, | |||||
| } from '@/components/ui/popover'; | |||||
| import { RAGFlowSelectOptionType } from '../ui/select'; | |||||
| const countries = [ | |||||
| { | |||||
| label: 'America', | |||||
| options: [ | |||||
| { value: 'United States', label: '🇺🇸' }, | |||||
| { value: 'Canada', label: '🇨🇦' }, | |||||
| { value: 'Mexico', label: '🇲🇽' }, | |||||
| ], | |||||
| }, | |||||
| { | |||||
| label: 'Africa', | |||||
| options: [ | |||||
| { value: 'South Africa', label: '🇿🇦' }, | |||||
| { value: 'Nigeria', label: '🇳🇬' }, | |||||
| { value: 'Morocco', label: '🇲🇦' }, | |||||
| ], | |||||
| }, | |||||
| { | |||||
| label: 'Asia', | |||||
| options: [ | |||||
| { value: 'China', label: '🇨🇳' }, | |||||
| { value: 'Japan', label: '🇯🇵' }, | |||||
| { value: 'India', label: '🇮🇳' }, | |||||
| ], | |||||
| }, | |||||
| { | |||||
| label: 'Europe', | |||||
| options: [ | |||||
| { value: 'United Kingdom', label: '🇬🇧' }, | |||||
| { value: 'France', label: '🇫🇷' }, | |||||
| { value: 'Germany', label: '🇩🇪' }, | |||||
| ], | |||||
| }, | |||||
| { | |||||
| label: 'Oceania', | |||||
| options: [ | |||||
| { value: 'Australia', label: '🇦🇺' }, | |||||
| { value: 'New Zealand', label: '🇳🇿' }, | |||||
| ], | |||||
| }, | |||||
| ]; | |||||
| export type SelectWithSearchFlagOptionType = { | |||||
| label: string; | |||||
| options: RAGFlowSelectOptionType[]; | |||||
| }; | |||||
| export type SelectWithSearchFlagProps = { | |||||
| options?: SelectWithSearchFlagOptionType[]; | |||||
| value?: string; | |||||
| onChange?(value: string): void; | |||||
| }; | |||||
| export function SelectWithSearch({ | |||||
| value: val = '', | |||||
| onChange, | |||||
| options = countries, | |||||
| }: SelectWithSearchFlagProps) { | |||||
| const id = useId(); | |||||
| const [open, setOpen] = useState<boolean>(false); | |||||
| const [value, setValue] = useState<string>(''); | |||||
| const handleSelect = useCallback( | |||||
| (val: string) => { | |||||
| setValue(val); | |||||
| setOpen(false); | |||||
| onChange?.(val); | |||||
| }, | |||||
| [onChange], | |||||
| ); | |||||
| useEffect(() => { | |||||
| setValue(val); | |||||
| }, [val]); | |||||
| return ( | |||||
| <Popover open={open} onOpenChange={setOpen}> | |||||
| <PopoverTrigger asChild> | |||||
| <Button | |||||
| id={id} | |||||
| variant="outline" | |||||
| role="combobox" | |||||
| aria-expanded={open} | |||||
| 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 ? ( | |||||
| <span className="flex min-w-0 options-center gap-2"> | |||||
| <span className="text-lg leading-none truncate"> | |||||
| { | |||||
| options | |||||
| .map((group) => | |||||
| group.options.find((item) => item.value === value), | |||||
| ) | |||||
| .filter(Boolean)[0]?.label | |||||
| } | |||||
| </span> | |||||
| </span> | |||||
| ) : ( | |||||
| <span className="text-muted-foreground">Select value</span> | |||||
| )} | |||||
| <ChevronDownIcon | |||||
| size={16} | |||||
| className="text-muted-foreground/80 shrink-0" | |||||
| aria-hidden="true" | |||||
| /> | |||||
| </Button> | |||||
| </PopoverTrigger> | |||||
| <PopoverContent | |||||
| className="border-input w-full min-w-[var(--radix-popper-anchor-width)] p-0" | |||||
| align="start" | |||||
| > | |||||
| <Command> | |||||
| <CommandInput placeholder="Search ..." /> | |||||
| <CommandList> | |||||
| <CommandEmpty>No data found.</CommandEmpty> | |||||
| {options.map((group) => ( | |||||
| <Fragment key={group.label}> | |||||
| <CommandGroup heading={group.label}> | |||||
| {group.options.map((option) => ( | |||||
| <CommandItem | |||||
| key={option.value} | |||||
| value={option.value} | |||||
| onSelect={handleSelect} | |||||
| > | |||||
| <span className="text-lg leading-none"> | |||||
| {option.label} | |||||
| </span> | |||||
| {option.value} | |||||
| {value === option.value && ( | |||||
| <CheckIcon size={16} className="ml-auto" /> | |||||
| )} | |||||
| </CommandItem> | |||||
| ))} | |||||
| </CommandGroup> | |||||
| </Fragment> | |||||
| ))} | |||||
| </CommandList> | |||||
| </Command> | |||||
| </PopoverContent> | |||||
| </Popover> | |||||
| ); | |||||
| } |
| import { LlmSettingSchema } from '@/components/llm-setting-items/next'; | |||||
| import { CodeTemplateStrMap, ProgrammingLanguage } from '@/constants/agent'; | import { CodeTemplateStrMap, ProgrammingLanguage } from '@/constants/agent'; | ||||
| import { useTranslation } from 'react-i18next'; | import { useTranslation } from 'react-i18next'; | ||||
| import { z } from 'zod'; | import { z } from 'zod'; | ||||
| component: CategorizeForm, | component: CategorizeForm, | ||||
| defaultValues: { message_history_window_size: 1 }, | defaultValues: { message_history_window_size: 1 }, | ||||
| schema: z.object({ | schema: z.object({ | ||||
| ...LlmSettingSchema, | |||||
| message_history_window_size: z.number(), | message_history_window_size: z.number(), | ||||
| items: z.array( | items: z.array( | ||||
| z.object({ | z.object({ |
| import { LargeModelFormField } from '@/components/large-model-form-field'; | import { LargeModelFormField } from '@/components/large-model-form-field'; | ||||
| import { MessageHistoryWindowSizeFormField } from '@/components/message-history-window-size-item'; | import { MessageHistoryWindowSizeFormField } from '@/components/message-history-window-size-item'; | ||||
| import { Form } from '@/components/ui/form'; | |||||
| import { SelectWithSearch } from '@/components/originui/select-with-search'; | |||||
| import { | |||||
| Form, | |||||
| FormControl, | |||||
| FormField, | |||||
| FormItem, | |||||
| FormLabel, | |||||
| FormMessage, | |||||
| } from '@/components/ui/form'; | |||||
| import { useTranslation } from 'react-i18next'; | |||||
| import { INextOperatorForm } from '../../interface'; | import { INextOperatorForm } from '../../interface'; | ||||
| import { DynamicInputVariable } from '../components/next-dynamic-input-variable'; | |||||
| import DynamicCategorize from './dynamic-categorize'; | import DynamicCategorize from './dynamic-categorize'; | ||||
| const CategorizeForm = ({ form, node }: INextOperatorForm) => { | const CategorizeForm = ({ form, node }: INextOperatorForm) => { | ||||
| const { t } = useTranslation(); | |||||
| return ( | return ( | ||||
| <Form {...form}> | <Form {...form}> | ||||
| <form | <form | ||||
| className="space-y-6 p-5 overflow-auto max-h-[76vh]" | |||||
| className="space-y-6 p-5 " | |||||
| onSubmit={(e) => { | onSubmit={(e) => { | ||||
| e.preventDefault(); | e.preventDefault(); | ||||
| }} | }} | ||||
| > | > | ||||
| <DynamicInputVariable node={node}></DynamicInputVariable> | |||||
| <FormField | |||||
| control={form.control} | |||||
| name="input" | |||||
| render={({ field }) => ( | |||||
| <FormItem> | |||||
| <FormLabel tooltip={t('chat.modelTip')}> | |||||
| {t('chat.input')} | |||||
| </FormLabel> | |||||
| <FormControl> | |||||
| <SelectWithSearch {...field}></SelectWithSearch> | |||||
| </FormControl> | |||||
| <FormMessage /> | |||||
| </FormItem> | |||||
| )} | |||||
| /> | |||||
| <LargeModelFormField></LargeModelFormField> | <LargeModelFormField></LargeModelFormField> | ||||
| <MessageHistoryWindowSizeFormField></MessageHistoryWindowSizeFormField> | <MessageHistoryWindowSizeFormField></MessageHistoryWindowSizeFormField> | ||||
| <DynamicCategorize nodeId={node?.id}></DynamicCategorize> | <DynamicCategorize nodeId={node?.id}></DynamicCategorize> |
| import { NextLLMSelect } from '@/components/llm-select'; | |||||
| import { NextLLMSelect } from '@/components/llm-select/next'; | |||||
| import { MessageHistoryWindowSizeFormField } from '@/components/message-history-window-size-item'; | import { MessageHistoryWindowSizeFormField } from '@/components/message-history-window-size-item'; | ||||
| import { PromptEditor } from '@/components/prompt-editor'; | import { PromptEditor } from '@/components/prompt-editor'; | ||||
| import { | import { |
| import { NextLLMSelect } from '@/components/llm-select'; | |||||
| import { NextLLMSelect } from '@/components/llm-select/next'; | |||||
| import { TopNFormField } from '@/components/top-n-item'; | import { TopNFormField } from '@/components/top-n-item'; | ||||
| import { | import { | ||||
| Form, | Form, |
| import { NextLLMSelect } from '@/components/llm-select'; | |||||
| import { NextLLMSelect } from '@/components/llm-select/next'; | |||||
| import { MessageHistoryWindowSizeFormField } from '@/components/message-history-window-size-item'; | import { MessageHistoryWindowSizeFormField } from '@/components/message-history-window-size-item'; | ||||
| import { | import { | ||||
| Form, | Form, |
| const blob = new Blob([JSON.stringify(data)], { type: FileMimeType.Json }); | const blob = new Blob([JSON.stringify(data)], { type: FileMimeType.Json }); | ||||
| downloadFileFromBlob(blob, fileName); | downloadFileFromBlob(blob, fileName); | ||||
| }; | }; | ||||
| export function transformBase64ToFileWithPreview( | |||||
| dataUrl: string, | |||||
| filename: string = 'file', | |||||
| ) { | |||||
| const file = transformBase64ToFile(dataUrl, filename); | |||||
| (file as any).preview = dataUrl; | |||||
| return file; | |||||
| } | |||||
| export const getBase64FromFileList = async (fileList?: File[]) => { | |||||
| if (Array.isArray(fileList) && fileList.length > 0) { | |||||
| const file = fileList[0]; | |||||
| if (file) { | |||||
| const base64 = await transformFile2Base64(file); | |||||
| return base64; | |||||
| } | |||||
| } | |||||
| return ''; | |||||
| }; |