Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(chat): split models.reducers.ts to types, selectors and reducers #2958

Merged
merged 1 commit into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 5 additions & 163 deletions apps/chat/src/store/models/models.reducers.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,29 @@
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';

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,
Expand Down Expand Up @@ -132,6 +115,7 @@ export const modelsSlice = createSlice({
: [translate(errorsMessages.generalServer, { ns: 'common' })],
} as ErrorMessage;
},

initRecentModels: (
state,
{
Expand Down Expand Up @@ -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;
143 changes: 143 additions & 0 deletions apps/chat/src/store/models/models.selectors.ts
Original file line number Diff line number Diff line change
@@ -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));
},
);
23 changes: 23 additions & 0 deletions apps/chat/src/store/models/models.types.ts
Original file line number Diff line number Diff line change
@@ -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[];
}
Loading