diff --git a/web/app/components/app/configuration/debug/debug-with-multiple-model/debug-item.tsx b/web/app/components/app/configuration/debug/debug-with-multiple-model/debug-item.tsx index 89d5eb501c..30185bce9a 100644 --- a/web/app/components/app/configuration/debug/debug-with-multiple-model/debug-item.tsx +++ b/web/app/components/app/configuration/debug/debug-with-multiple-model/debug-item.tsx @@ -98,7 +98,7 @@ const DebugItem: FC = ({ ? [ { value: 'remove', - text: t('common.operation.remove'), + text: t('common.operation.remove') as string, }, ] : undefined diff --git a/web/app/components/base/dropdown/index.tsx b/web/app/components/base/dropdown/index.tsx index a799380d32..3825fd8aea 100644 --- a/web/app/components/base/dropdown/index.tsx +++ b/web/app/components/base/dropdown/index.tsx @@ -8,23 +8,30 @@ import { } from '@/app/components/base/portal-to-follow-elem' export type Item = { - value: string - text: string + value: string | number + text: string | JSX.Element } type DropdownProps = { items: Item[] secondItems?: Item[] onSelect: (item: Item) => void renderTrigger?: (open: boolean) => React.ReactNode + popupClassName?: string } const Dropdown: FC = ({ items, onSelect, secondItems, renderTrigger, + popupClassName, }) => { const [open, setOpen] = useState(false) + const handleSelect = (item: Item) => { + setOpen(false) + onSelect(item) + } + return ( = ({ ) } - +
{ !!items.length && ( @@ -57,7 +64,7 @@ const Dropdown: FC = ({
onSelect(item)} + onClick={() => handleSelect(item)} > {item.text}
@@ -79,7 +86,7 @@ const Dropdown: FC = ({
onSelect(item)} + onClick={() => handleSelect(item)} > {item.text}
diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx index 4c9171aeb0..1089c0dd49 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx @@ -4,7 +4,6 @@ import type { } from 'react' import { useEffect, useMemo, useState } from 'react' import useSWR from 'swr' -import cn from 'classnames' import { useTranslation } from 'react-i18next' import type { DefaultModel, @@ -21,6 +20,7 @@ import ParameterItem from './parameter-item' import type { ParameterValue } from './parameter-item' import Trigger from './trigger' import type { TriggerProps } from './trigger' +import PresetsParameter from './presets-parameter' import { PortalToFollowElem, PortalToFollowElemContent, @@ -30,13 +30,7 @@ import { CubeOutline } from '@/app/components/base/icons/src/vender/line/shapes' import { fetchModelParameterRules } from '@/service/common' import Loading from '@/app/components/base/loading' import { useProviderContext } from '@/context/provider-context' -import Radio from '@/app/components/base/radio' import { TONE_LIST } from '@/config' -import { Brush01 } from '@/app/components/base/icons/src/vender/solid/editor' -import { Scales02 } from '@/app/components/base/icons/src/vender/solid/FinanceAndECommerce' -import { Target04 } from '@/app/components/base/icons/src/vender/solid/general' -import { Sliders02 } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices' -import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' import { ArrowNarrowLeft } from '@/app/components/base/icons/src/vender/line/arrows' export type ModelParameterModalProps = { @@ -84,8 +78,6 @@ const ModelParameterModal: FC = ({ }) => { const { t } = useTranslation() const { hasSettedApiKey } = useProviderContext() - const media = useBreakpoints() - const isMobile = media === MediaType.mobile const [open, setOpen] = useState(false) const { data: parameterRulesData, isLoading } = useSWR((provider && modelId) ? `/workspaces/current/model-providers/${provider}/models/parameter-rules?model=${modelId}` : null, fetchModelParameterRules) const { @@ -100,46 +92,10 @@ const ModelParameterModal: FC = ({ const modelDisabled = currentModel?.status !== ModelStatusEnum.active const disabled = !hasSettedApiKey || hasDeprecated || modelDisabled - const parameterRules = useMemo(() => { + const parameterRules: ModelParameterRule[] = useMemo(() => { return parameterRulesData?.data || [] }, [parameterRulesData]) - // only openai support this - function matchToneId(completionParams: FormValue): number { - const remvoedCustomeTone = TONE_LIST.slice(0, -1) - const CUSTOM_TONE_ID = 4 - const tone = remvoedCustomeTone.find((tone) => { - const config: Record = tone.config || {} - - return Object.keys(config).every((key) => { - return config[key] === completionParams[key] - }) - }) - return tone ? tone.id : CUSTOM_TONE_ID - } - - // tone is a preset of completionParams. - const [toneId, setToneId] = useState(matchToneId(completionParams)) // default is Balanced - const toneTabBgClassName = ({ - 1: 'bg-[#F5F8FF]', - 2: 'bg-[#F4F3FF]', - 3: 'bg-[#F6FEFC]', - })[toneId] || '' - // set completionParams by toneId - const handleToneChange = (id: number) => { - const tone = TONE_LIST.find(tone => tone.id === id) - if (tone) { - setToneId(id) - onCompletionParamsChange({ - ...tone.config, - }) - } - } - - useEffect(() => { - setToneId(matchToneId(completionParams)) - }, [completionParams]) - const handleParamChange = (key: string, value: ParameterValue) => { onCompletionParamsChange({ ...completionParams, @@ -175,7 +131,6 @@ const ModelParameterModal: FC = ({ const handleInitialParams = () => { const newCompletionParams = { ...completionParams } - const defaultParams: Record = {} if (parameterRules.length) { parameterRules.forEach((parameterRule) => { if (!newCompletionParams[parameterRule.name]) { @@ -184,13 +139,8 @@ const ModelParameterModal: FC = ({ else delete newCompletionParams[parameterRule.name] } - if (!isNullOrUndefined(parameterRule.default)) - defaultParams[parameterRule.name] = parameterRule.default }) - if (PROVIDER_WITH_PRESET_TONE.includes(provider)) - TONE_LIST[3].config = defaultParams as any - onCompletionParamsChange(newCompletionParams) } } @@ -199,15 +149,14 @@ const ModelParameterModal: FC = ({ handleInitialParams() }, [parameterRules]) - const getToneIcon = (toneId: number) => { - const className = 'w-[14px] h-[14px]' - const res = ({ - 1: , - 2: , - 3: , - 4: , - })[toneId] - return res + const handleSelectPresetParameter = (toneId: number) => { + const tone = TONE_LIST.find(tone => tone.id === toneId) + if (tone) { + onCompletionParamsChange({ + ...completionParams, + ...tone.config, + }) + } } return ( @@ -274,47 +223,18 @@ const ModelParameterModal: FC = ({
) } - {PROVIDER_WITH_PRESET_TONE.includes(provider) && !isLoading && !!parameterRules.length && ( -
-
{t('appDebug.modelConfig.setTone')}
- - <> - {TONE_LIST.slice(0, 3).map(tone => ( -
- - <> - {getToneIcon(tone.id)} - {!isMobile &&
{t(`common.model.tone.${tone.name}`) as string}
} -
- -
- {tone.id !== toneId && tone.id + 1 !== toneId && (
)} -
- ))} - - - <> - {getToneIcon(TONE_LIST[3].id)} - {!isMobile &&
{t(`common.model.tone.${TONE_LIST[3].name}`) as string}
} - -
-
-
- )} + { + !isLoading && !!parameterRules.length && ( +
+
{t('common.modelProvider.parameters')}
+ { + PROVIDER_WITH_PRESET_TONE.includes(provider) && ( + + ) + } +
+ ) + } { !isLoading && !!parameterRules.length && ( [ diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/parameter-item.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/parameter-item.tsx index 41aed8e4d7..6b383108c2 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/parameter-item.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/parameter-item.tsx @@ -1,5 +1,5 @@ import type { FC } from 'react' -import { useState } from 'react' +import { useEffect, useRef, useState } from 'react' import type { ModelParameterRule } from '../declarations' import { useLanguage } from '../hooks' import { isNullOrUndefined } from '../utils' @@ -29,6 +29,7 @@ const ParameterItem: FC = ({ }) => { const language = useLanguage() const [localValue, setLocalValue] = useState(value) + const numberInputRef = useRef(null) const getDefaultValue = () => { let defaultValue: ParameterValue @@ -57,8 +58,10 @@ const ParameterItem: FC = ({ const handleNumberInputChange = (e: React.ChangeEvent) => { let num = +e.target.value - if (!isNullOrUndefined(parameterRule.max) && num > parameterRule.max!) + if (!isNullOrUndefined(parameterRule.max) && num > parameterRule.max!) { num = parameterRule.max as number + numberInputRef.current!.value = `${num}` + } if (!isNullOrUndefined(parameterRule.min) && num < parameterRule.min!) num = parameterRule.min as number @@ -66,14 +69,26 @@ const ParameterItem: FC = ({ handleInputChange(num) } - const handleSlideChange = (num: number) => { - if (!isNullOrUndefined(parameterRule.max) && num > parameterRule.max!) - return handleInputChange(parameterRule.max) + const handleNumberInputBlur = () => { + if (numberInputRef.current) + numberInputRef.current.value = renderValue as string + } - if (!isNullOrUndefined(parameterRule.min) && num < parameterRule.min!) - return handleInputChange(parameterRule.min) + const handleSlideChange = (num: number) => { + if (!isNullOrUndefined(parameterRule.max) && num > parameterRule.max!) { + handleInputChange(parameterRule.max) + numberInputRef.current!.value = `${parameterRule.max}` + return + } + + if (!isNullOrUndefined(parameterRule.min) && num < parameterRule.min!) { + handleInputChange(parameterRule.min) + numberInputRef.current!.value = `${parameterRule.min}` + return + } handleInputChange(num) + numberInputRef.current!.value = `${num}` } const handleRadioChange = (v: number) => { @@ -129,13 +144,14 @@ const ParameterItem: FC = ({ onChange={handleSlideChange} />} ) @@ -191,6 +207,11 @@ const ParameterItem: FC = ({ return null } + useEffect(() => { + if (numberInputRef.current) + numberInputRef.current.value = `${renderValue}` + }, []) + return (
diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/presets-parameter.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/presets-parameter.tsx new file mode 100644 index 0000000000..91e659c2f8 --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/presets-parameter.tsx @@ -0,0 +1,65 @@ +import type { FC } from 'react' +import { useCallback } from 'react' +import { useTranslation } from 'react-i18next' +import Dropdown from '@/app/components/base/dropdown' +import { SlidersH } from '@/app/components/base/icons/src/vender/line/mediaAndDevices' +import { ChevronDown } from '@/app/components/base/icons/src/vender/line/arrows' +import { Brush01 } from '@/app/components/base/icons/src/vender/solid/editor' +import { Scales02 } from '@/app/components/base/icons/src/vender/solid/FinanceAndECommerce' +import { Target04 } from '@/app/components/base/icons/src/vender/solid/general' +import { TONE_LIST } from '@/config' + +type PresetsParameterProps = { + onSelect: (toneId: number) => void +} +const PresetsParameter: FC = ({ + onSelect, +}) => { + const { t } = useTranslation() + const renderTrigger = useCallback((open: boolean) => { + return ( +
+ + {t('common.modelProvider.loadPresets')} + +
+ ) + }, []) + const getToneIcon = (toneId: number) => { + const className = 'mr-2 w-[14px] h-[14px]' + const res = ({ + 1: , + 2: , + 3: , + })[toneId] + return res + } + const options = TONE_LIST.slice(0, 3).map((tone) => { + return { + value: tone.id, + text: ( +
+ {getToneIcon(tone.id)} + {t(`common.model.tone.${tone.name}`) as string} +
+ ), + } + }) + + return ( + onSelect(item.value as number)} + popupClassName='z-[70]' + /> + ) +} + +export default PresetsParameter diff --git a/web/i18n/lang/common.en.ts b/web/i18n/lang/common.en.ts index 1745af0b40..1b1f7ed78b 100644 --- a/web/i18n/lang/common.en.ts +++ b/web/i18n/lang/common.en.ts @@ -309,6 +309,8 @@ const translation = { deprecated: 'Deprecated', confirmDelete: 'confirm deletion?', quotaTip: 'Remaining available free tokens', + loadPresets: 'Load Presents', + parameters: 'PARAMETERS', }, dataSource: { add: 'Add a data source', diff --git a/web/i18n/lang/common.pt.ts b/web/i18n/lang/common.pt.ts index 80ff11b259..2709692a21 100644 --- a/web/i18n/lang/common.pt.ts +++ b/web/i18n/lang/common.pt.ts @@ -138,6 +138,8 @@ const translation = { deprecated: 'Descontinuado', confirmDelete: 'confirmar exclusão?', quotaTip: 'Tokens gratuitos disponíveis restantes', + loadPresets: 'Carregar presentes', + parameters: 'PARÂMETROS', }, dataSource: { add: 'Adicionar uma fonte de dados', diff --git a/web/i18n/lang/common.zh.ts b/web/i18n/lang/common.zh.ts index 82a57e4278..772eafe530 100644 --- a/web/i18n/lang/common.zh.ts +++ b/web/i18n/lang/common.zh.ts @@ -309,6 +309,8 @@ const translation = { deprecated: '已弃用', confirmDelete: '确认删除?', quotaTip: '剩余免费额度', + loadPresets: '加载预设', + parameters: '参数', }, dataSource: { add: '添加数据源',