@@ -0,0 +1,55 @@ +import { IModelParams } from '#/shared/api/models/models' + +export type ModelParamValues = Record + +export function getAllowedParamKeys(params: IModelParams[], currentVersion: string): Set { + return new Set( + params + .filter((el) => { + if (el.versions.length === 0) { + return true + } + + if (!currentVersion) { + return true + } + + return el.versions.includes(currentVersion) + }) + .map((el) => el.key) + ) +} + +export function pruneModelParams( + includeParams: ModelParamValues, + params: IModelParams[], + currentVersion: string +): ModelParamValues | null { + const allowedKeys = getAllowedParamKeys(params, currentVersion) + + if (allowedKeys.size === 0) { + return Object.keys(includeParams).length !== 0 ? {} : null + } + + const filteredEntries = Object.entries(includeParams).filter(([key]) => allowedKeys.has(key)) + + if (filteredEntries.length === Object.keys(includeParams).length) { + return null + } + + return filteredEntries.length ? Object.fromEntries(filteredEntries) : {} +} + +export function buildDefaultParamsForVersion(parameters: IModelParams[], version: string): ModelParamValues { + if (!version) { + return parameters.reduce((acc, param) => ({ ...acc, [param.key]: param.values.default }), {}) + } + + return parameters.reduce((acc, param) => { + if (param.versions.length === 0 || param.versions.includes(version)) { + return { ...acc, [param.key]: param.values.default } + } + + return acc + }, {} as ModelParamValues) +} @@ -0,0 +1,27 @@ +import React from 'react' + +import { replaceParams } from '#/app/store/model-parametres-store' +import { useAppDispatch, useAppSelector } from '#/app/store/store' +import { IModelParams } from '#/shared/api/models/models' + +import { pruneModelParams } from '../lib/prune-model-params' + +export function usePruneModelParams(params: IModelParams[] | undefined, currentVersion: string) { + const includeParams = useAppSelector((state) => state.params.params) as Record< + string, + string | number | number[] | boolean + > + const dispatch = useAppDispatch() + + React.useEffect(() => { + if (!params?.length) { + return + } + + const nextPayload = pruneModelParams(includeParams, params, currentVersion) + + if (nextPayload !== null) { + dispatch(replaceParams(nextPayload)) + } + }, [currentVersion, dispatch, includeParams, params]) +} @@ -1,126 +1,94 @@ -import React from 'react' -import { Stack } from '@mui/material' - -import { CheckboxFilter } from '#/app/components/filters/checkbox_filter' -import { InputFilter } from '#/app/components/filters/input_filter' -import { SelectFilter } from '#/app/components/filters/select_filter' -import { Slide } from '#/app/components/filters/slide_filter' -import { replaceParams, setParams } from '#/app/store/model-parametres-store' -import { useAppDispatch, useAppSelector } from '#/app/store/store' -import { IModelParams } from '#/shared/api/models/models' - -interface IProps { - params: IModelParams[] - currentVersion: string -} -export default function BotParamsMap({ params, currentVersion }: IProps) { - const includeParams = useAppSelector((state) => state.params.params) - const dispatch = useAppDispatch() - - const setNewParam = React.useCallback( - (payload: { [key: string]: string | number | number[] | boolean }) => { - dispatch(setParams(payload)) - }, - [dispatch] - ) - - React.useEffect(() => { - if (!params) { - return - } - - const allowedKeys = new Set( - params - .filter((el) => { - if (el.versions.length === 0) { - return true - } - - // Согласовано с chat-bot filteredParams: без выбранной версии считаем все параметры допустимыми - if (!currentVersion) { - return true - } - - return el.versions.includes(currentVersion) - }) - .map((el) => el.key) - ) - - if (allowedKeys.size === 0) { - if (Object.keys(includeParams).length !== 0) { - dispatch(replaceParams({})) - } - return - } - - const filteredEntries = Object.entries(includeParams).filter(([key]) => allowedKeys.has(key)) - - if (filteredEntries.length !== Object.keys(includeParams).length) { - const nextPayload = filteredEntries.length ? Object.fromEntries(filteredEntries) : {} - dispatch(replaceParams(nextPayload)) - } - }, [currentVersion, dispatch, includeParams, params, setNewParam]) - - return ( - - {params.map((item, idx) => { - if (item.versions.length === 0 || item.versions.includes(currentVersion)) { - if (item.type == 'floatrange' || item.type == 'intrange') { - return ( - - ) - } - if (item.type == 'bool') { - return ( - - ) - } - - if (item.type == 'list') { - return ( - - ) - } - - if (item.type == 'int' || item.type == 'str') { - return ( - - ) - } - } - })} - - ) -} +import React from 'react' +import { Stack } from '@mui/material' + +import { CheckboxFilter } from '#/app/components/filters/checkbox_filter' +import { InputFilter } from '#/app/components/filters/input_filter' +import { SelectFilter } from '#/app/components/filters/select_filter' +import { Slide } from '#/app/components/filters/slide_filter' +import { setParams } from '#/app/store/model-parametres-store' +import { useAppDispatch, useAppSelector } from '#/app/store/store' +import { IModelParams } from '#/shared/api/models/models' + +import { usePruneModelParams } from './model/use-prune-model-params' + +interface IProps { + params: IModelParams[] + currentVersion: string +} +export default function BotParamsMap({ params, currentVersion }: IProps) { + const includeParams = useAppSelector((state) => state.params.params) + const dispatch = useAppDispatch() + + usePruneModelParams(params, currentVersion) + + const setNewParam = React.useCallback( + (payload: { [key: string]: string | number | number[] | boolean }) => { + dispatch(setParams(payload)) + }, + [dispatch] + ) + + return ( + + {params.map((item, idx) => { + if (item.versions.length === 0 || item.versions.includes(currentVersion)) { + if (item.type == 'floatrange' || item.type == 'intrange') { + return ( + + ) + } + if (item.type == 'bool') { + return ( + + ) + } + + if (item.type == 'list') { + return ( + + ) + } + + if (item.type == 'int' || item.type == 'str') { + return ( + + ) + } + } + })} + + ) +} + \ No newline at end of file @@ -10,6 +10,7 @@ type DrawerProps = { export function DrawerCustom(props: DrawerProps) { return ( = () => { const { data } = useSession() const router = useRouter() const { push } = router + const slug = router.query.slug as string | undefined const currentChat = useAppSelector(selectCurrentChat) const { messages, sendMessage, loading, getMessagesPagination, deleteMessage } = useModel(currentChat, showMessage, modelType) const includeParams = useAppSelector((state) => state.params.params) - const dispatch = useDispatch() + const dispatch = useAppDispatch() + + usePruneModelParams(botParams?.parameters, version) const deleteMessageMemo = useCallback(deleteMessage, [currentChat, messages]) React.useEffect(() => { - if (data?.access) { - model_api.getBotParams(router.asPath.split('/')[2], data.access).then((res) => { - if (!res.title) return push('/404') - setBotParams(res) - setModelType(res.slug) - if (res.versions.length !== 0) { - setVersion(res.versions[0].slug) - dispatch( - setParametres( - res.parameters.reduce( - (a, v) => (v.versions.includes(res.versions[0].slug) ? { ...a, [v.key]: v.values.default } : { ...a }), - {} - ) - ) - ) - } else { - setVersion('') - dispatch(setParametres(res.parameters.reduce((a, v) => ({ ...a, [v.key]: v.values.default }), {}))) - } - }) + if (!slug) { + return } - }, [data?.access, dispatch, push, router.asPath, router.query]) - const resetParams = () => { - if (botParams) { - dispatch(setParametres({})) - if (botParams.versions?.length !== 0) { - setVersion(botParams.versions[0].slug) - dispatch( - setParametres( - botParams.parameters.reduce( - (a, v) => (v.versions.includes(botParams.versions[0].slug) ? { ...a, [v.key]: v.values.default } : { ...a }), - {} - ) - ) - ) + dispatch(replaceParams({})) + }, [dispatch, slug]) + + React.useEffect(() => { + if (!slug || !data?.access) { + return + } + + model_api.getBotParams(slug, data.access).then((res) => { + if (!res.title) return push('/404') + setBotParams(res) + setModelType(res.slug) + + if (res.versions.length !== 0) { + const initialVersion = res.versions[0].slug + setVersion(initialVersion) + dispatch(replaceParams(buildDefaultParamsForVersion(res.parameters, initialVersion))) } else { - setVersion(botParams.slug) - dispatch(setParametres(botParams.parameters.reduce((a, v) => ({ ...a, [v.key]: v.values.default }), {}))) + setVersion('') + dispatch(replaceParams(buildDefaultParamsForVersion(res.parameters, ''))) } + }) + }, [data?.access, dispatch, push, slug]) + + const resetParams = () => { + if (!botParams) { + return + } + + if (botParams.versions?.length !== 0) { + const initialVersion = botParams.versions[0].slug + setVersion(initialVersion) + dispatch(replaceParams(buildDefaultParamsForVersion(botParams.parameters, initialVersion))) + } else { + setVersion(botParams.slug) + dispatch(replaceParams(buildDefaultParamsForVersion(botParams.parameters, ''))) } } const setDefaultParams = (newVersion?: string) => { - if (botParams) { - const targetVersion = newVersion !== undefined ? newVersion : version - - dispatch(setParametres({})) - if (targetVersion !== '') { - dispatch( - setParametres( - botParams.parameters.reduce( - (a, v) => (v.versions.includes(targetVersion) ? { ...a, [v.key]: v.values.default } : { ...a }), - {} - ) - ) - ) - } else { - dispatch(setParametres(botParams.parameters.reduce((a, v) => ({ ...a, [v.key]: v.values.default }), {}))) - } + if (!botParams) { + return } + + const targetVersion = newVersion !== undefined ? newVersion : version + dispatch(replaceParams(buildDefaultParamsForVersion(botParams.parameters, targetVersion))) } const viewMobileSettings = () => { @@ -132,29 +125,6 @@ const Page: NextPageWithLayout = () => { setOpenFiltersMobile(false) } - const onSendMessage = (input: string, required: (string | null)[]) => { - if (required.includes('text') && (input === '' || input === null)) { - showMessage('Введите сообщение!') - return false - } - if (required.includes('image') && file === null) { - showMessage('Прикрепите изображение!') - return false - } - - let data = { ...(includeParams || {}) } - if (version !== '') data = { ...data, ...{ version: version } } - - sendMessage({ - content: input, - file, - info: { - ...data, - }, - }) - return true - } - const filteredParams = useMemo(() => { if (!botParams?.parameters) { return {} @@ -183,6 +153,29 @@ const Page: NextPageWithLayout = () => { ) }, [botParams?.parameters, version, includeParams]) + const onSendMessage = useCallback( + (input: string, required: (string | null)[]) => { + if (required.includes('text') && (input === '' || input === null)) { + showMessage('Введите сообщение!') + return false + } + if (required.includes('image') && file === null) { + showMessage('Прикрепите изображение!') + return false + } + + const info = version !== '' ? { ...filteredParams, version } : { ...filteredParams } + + sendMessage({ + content: input, + file, + info, + }) + return true + }, + [file, filteredParams, sendMessage, showMessage, version] + ) + const showFileExample = useMemo(() => { if (!botParams?.inputs) return false @@ -202,7 +195,10 @@ const Page: NextPageWithLayout = () => { }) }, [botParams?.inputs, version]) - const predictPriceInfo = useMemo(() => ({ ...(includeParams || {}), ...(version ? { version } : {}) }), [includeParams, version]) + const predictPriceInfo = useMemo( + () => (version ? { ...filteredParams, version } : { ...filteredParams }), + [filteredParams, version] + ) const predictedPrice = usePredictPrice({ modelSlug: modelType,