diff --git a/apps/chat/src/store/models/models.reducers.ts b/apps/chat/src/store/models/models.reducers.ts index f79a08c97..d13855c62 100644 --- a/apps/chat/src/store/models/models.reducers.ts +++ b/apps/chat/src/store/models/models.reducers.ts @@ -1,16 +1,13 @@ -import { PayloadAction, createSelector, createSlice } from '@reduxjs/toolkit'; +import { PayloadAction, createSlice } from '@reduxjs/toolkit'; import { combineEntities } from '@/src/utils/app/common'; -import { canWriteSharedWithMe } from '@/src/utils/app/share'; import { translate } from '@/src/utils/app/translation'; import { ApplicationInfo, ApplicationStatus } from '@/src/types/applications'; -import { EntityType } from '@/src/types/common'; import { ErrorMessage } from '@/src/types/error'; import { DialAIEntityModel, InstalledModel, - ModelsMap, PublishRequestDialAIEntityModel, } from '@/src/types/models'; @@ -18,29 +15,15 @@ import { RECENT_MODELS_COUNT } from '@/src/constants/chat'; import { errorsMessages } from '@/src/constants/errors'; import { DeleteType } from '@/src/constants/marketplace'; -import { RootState } from '../index'; +import * as ModelsSelectors from './models.selectors'; +import { ModelsState } from './models.types'; import { UploadStatus } from '@epam/ai-dial-shared'; -import { sortBy } from 'lodash-es'; import cloneDeep from 'lodash-es/cloneDeep'; -import groupBy from 'lodash-es/groupBy'; import omit from 'lodash-es/omit'; -import orderBy from 'lodash-es/orderBy'; import uniq from 'lodash-es/uniq'; -export interface ModelsState { - initialized: boolean; - status: UploadStatus; - error: ErrorMessage | undefined; - models: DialAIEntityModel[]; - modelsMap: ModelsMap; - recentModelsIds: string[]; - recentModelsStatus: UploadStatus; - isInstalledModelsInitialized: boolean; - installedModels: InstalledModel[]; - publishRequestModels: PublishRequestDialAIEntityModel[]; - publishedApplicationIds: string[]; -} +export { ModelsSelectors }; const initialState: ModelsState = { initialized: false, @@ -132,6 +115,7 @@ export const modelsSlice = createSlice({ : [translate(errorsMessages.generalServer, { ns: 'common' })], } as ErrorMessage; }, + initRecentModels: ( state, { @@ -319,146 +303,4 @@ export const modelsSlice = createSlice({ }, }); -const rootSelector = (state: RootState): ModelsState => state.models; - -const selectModelsIsLoading = createSelector([rootSelector], (state) => { - return ( - state.status === UploadStatus.LOADING || - state.status === UploadStatus.UNINITIALIZED - ); -}); - -const selectIsModelsLoaded = createSelector([rootSelector], (state) => { - return state.status === UploadStatus.LOADED; -}); - -const selectIsInstalledModelsInitialized = createSelector( - [rootSelector], - (state) => { - return state.isInstalledModelsInitialized; - }, -); - -const selectModelsError = createSelector([rootSelector], (state) => { - return state.error; -}); - -const selectIsRecentModelsLoaded = createSelector([rootSelector], (state) => { - return state.recentModelsStatus === UploadStatus.LOADED; -}); - -const selectModels = createSelector([rootSelector], (state) => { - const groups = groupBy(state.models, (model) => - model.reference === model.id ? 'rest' : 'custom', - ); - - return sortBy( - [ - ...(groups.rest ?? []), - ...orderBy(groups.custom ?? [], 'version', 'desc'), - ], - (model) => model.name.toLowerCase(), - ); -}); - -const selectModelTopics = createSelector([rootSelector], (state) => { - return uniq( - state.models?.flatMap((model) => model.topics ?? []) ?? [], - ).sort(); -}); - -const selectModelsMap = createSelector([rootSelector], (state) => { - return state.modelsMap; -}); -const selectRecentModelsIds = createSelector([rootSelector], (state) => { - return state.recentModelsIds; -}); - -const selectRecentModels = createSelector( - [selectRecentModelsIds, selectModelsMap], - (recentModelsIds, modelsMap) => { - return recentModelsIds.map((id) => modelsMap[id]).filter(Boolean); - }, -); - -const selectModelsOnly = createSelector([selectModels], (models) => { - return models.filter((model) => model.type === EntityType.Model); -}); - -const selectPublishRequestModels = createSelector([rootSelector], (state) => { - return state.publishRequestModels; -}); - -const selectPublishedApplicationIds = createSelector( - [rootSelector], - (state) => { - return state.publishedApplicationIds; - }, -); - -const selectInstalledModels = createSelector([rootSelector], (state) => { - return state.installedModels; -}); - -const selectInstalledModelIds = createSelector([rootSelector], (state) => { - return new Set(state.installedModels.map(({ id }) => id)); -}); - -const selectRecentWithInstalledModelsIds = createSelector( - [selectRecentModelsIds, selectInstalledModelIds], - (recentModelIds, installedModelIds) => { - // TODO: implement Pin-behavior in future - const installedWithoutRecents = Array.from(installedModelIds).filter( - (id) => !recentModelIds.includes(id), - ); - return [...recentModelIds, ...installedWithoutRecents]; - }, -); - -const selectInitialized = createSelector( - [rootSelector], - (state) => state.initialized, -); - -const selectCustomModels = createSelector([rootSelector], (state) => { - return state.models.filter((model) => model.reference !== model.id); -}); - -const selectSharedWithMeModels = createSelector( - [selectCustomModels], - (customModels) => { - return customModels.filter((model) => model.sharedWithMe); - }, -); - -const selectSharedWriteModels = createSelector( - [selectCustomModels], - (customModels) => { - return customModels.filter((model) => canWriteSharedWithMe(model)); - }, -); - -export const ModelsSelectors = { - selectIsInstalledModelsInitialized, - selectIsModelsLoaded, - selectModelsIsLoading, - selectModelsError, - selectModels, - selectModelsMap, - selectCustomModels, - selectInstalledModels, - selectInstalledModelIds, - selectRecentModelsIds, - selectRecentModels, - selectIsRecentModelsLoaded, - selectModelsOnly, - selectPublishRequestModels, - selectPublishedApplicationIds, - selectModelTopics, - selectRecentWithInstalledModelsIds, - selectInitialized, - selectSharedWithMeModels, - selectSharedWriteModels, -}; - export const ModelsActions = modelsSlice.actions; diff --git a/apps/chat/src/store/models/models.selectors.ts b/apps/chat/src/store/models/models.selectors.ts new file mode 100644 index 000000000..f5d552e7a --- /dev/null +++ b/apps/chat/src/store/models/models.selectors.ts @@ -0,0 +1,143 @@ +import { createSelector } from '@reduxjs/toolkit'; + +import { canWriteSharedWithMe } from '@/src/utils/app/share'; + +import { EntityType } from '@/src/types/common'; + +import { RootState } from '../index'; +import { ModelsState } from './models.types'; + +import { UploadStatus } from '@epam/ai-dial-shared'; +import { sortBy } from 'lodash-es'; +import groupBy from 'lodash-es/groupBy'; +import orderBy from 'lodash-es/orderBy'; +import uniq from 'lodash-es/uniq'; + +const rootSelector = (state: RootState): ModelsState => state.models; + +export const selectModelsIsLoading = createSelector([rootSelector], (state) => { + return ( + state.status === UploadStatus.LOADING || + state.status === UploadStatus.UNINITIALIZED + ); +}); + +export const selectIsModelsLoaded = createSelector([rootSelector], (state) => { + return state.status === UploadStatus.LOADED; +}); + +export const selectIsInstalledModelsInitialized = createSelector( + [rootSelector], + (state) => { + return state.isInstalledModelsInitialized; + }, +); + +export const selectModelsError = createSelector([rootSelector], (state) => { + return state.error; +}); + +export const selectIsRecentModelsLoaded = createSelector( + [rootSelector], + (state) => { + return state.recentModelsStatus === UploadStatus.LOADED; + }, +); + +export const selectModels = createSelector([rootSelector], (state) => { + const groups = groupBy(state.models, (model) => + model.reference === model.id ? 'rest' : 'custom', + ); + + return sortBy( + [ + ...(groups.rest ?? []), + ...orderBy(groups.custom ?? [], 'version', 'desc'), + ], + (model) => model.name.toLowerCase(), + ); +}); + +export const selectModelTopics = createSelector([rootSelector], (state) => { + return uniq( + state.models?.flatMap((model) => model.topics ?? []) ?? [], + ).sort(); +}); + +export const selectModelsMap = createSelector([rootSelector], (state) => { + return state.modelsMap; +}); + +export const selectRecentModelsIds = createSelector([rootSelector], (state) => { + return state.recentModelsIds; +}); + +export const selectRecentModels = createSelector( + [selectRecentModelsIds, selectModelsMap], + (recentModelsIds, modelsMap) => { + return recentModelsIds.map((id) => modelsMap[id]).filter(Boolean); + }, +); + +export const selectModelsOnly = createSelector([selectModels], (models) => { + return models.filter((model) => model.type === EntityType.Model); +}); + +export const selectPublishRequestModels = createSelector( + [rootSelector], + (state) => { + return state.publishRequestModels; + }, +); + +export const selectPublishedApplicationIds = createSelector( + [rootSelector], + (state) => { + return state.publishedApplicationIds; + }, +); + +export const selectInstalledModels = createSelector([rootSelector], (state) => { + return state.installedModels; +}); + +export const selectInstalledModelIds = createSelector( + [rootSelector], + (state) => { + return new Set(state.installedModels.map(({ id }) => id)); + }, +); + +export const selectRecentWithInstalledModelsIds = createSelector( + [selectRecentModelsIds, selectInstalledModelIds], + (recentModelIds, installedModelIds) => { + // TODO: implement Pin-behavior in future + const installedWithoutRecents = Array.from(installedModelIds).filter( + (id) => !recentModelIds.includes(id), + ); + return [...recentModelIds, ...installedWithoutRecents]; + }, +); + +export const selectInitialized = createSelector( + [rootSelector], + (state) => state.initialized, +); + +export const selectCustomModels = createSelector([rootSelector], (state) => { + return state.models.filter((model) => model.reference !== model.id); +}); + +export const selectSharedWithMeModels = createSelector( + [selectCustomModels], + (customModels) => { + return customModels.filter((model) => model.sharedWithMe); + }, +); + +export const selectSharedWriteModels = createSelector( + [selectCustomModels], + (customModels) => { + return customModels.filter((model) => canWriteSharedWithMe(model)); + }, +); diff --git a/apps/chat/src/store/models/models.types.ts b/apps/chat/src/store/models/models.types.ts new file mode 100644 index 000000000..4ee7761ed --- /dev/null +++ b/apps/chat/src/store/models/models.types.ts @@ -0,0 +1,23 @@ +import { ErrorMessage } from '@/src/types/error'; +import { + DialAIEntityModel, + InstalledModel, + ModelsMap, + PublishRequestDialAIEntityModel, +} from '@/src/types/models'; + +import { UploadStatus } from '@epam/ai-dial-shared'; + +export interface ModelsState { + initialized: boolean; + status: UploadStatus; + error: ErrorMessage | undefined; + models: DialAIEntityModel[]; + modelsMap: ModelsMap; + recentModelsIds: string[]; + recentModelsStatus: UploadStatus; + isInstalledModelsInitialized: boolean; + installedModels: InstalledModel[]; + publishRequestModels: PublishRequestDialAIEntityModel[]; + publishedApplicationIds: string[]; +}