Skip to content

Commit

Permalink
fix(chat): improve Change Agent handling and add mobile tooltips in c…
Browse files Browse the repository at this point in the history
…hats (Issues #2842, #2849)
  • Loading branch information
Derikyan committed Dec 30, 2024
1 parent 54d0f31 commit c10abbc
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 104 deletions.
19 changes: 14 additions & 5 deletions apps/chat/src/components/Chat/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ export const ChatView = memo(() => {
const isPlayback = useAppSelector(
ConversationsSelectors.selectIsPlaybackSelectedConversations,
);
const talkToConversationId = useAppSelector(
ConversationsSelectors.selectТalkToConversationId,
);
const isAnyMenuOpen = useAppSelector(UISelectors.selectIsAnyMenuOpen);
const isIsolatedView = useAppSelector(SettingsSelectors.selectIsIsolatedView);
const installedModelIds = useAppSelector(
Expand All @@ -128,7 +131,13 @@ export const ChatView = memo(() => {
const [prevSelectedIds, setPrevSelectedIds] = useState<string[]>([]);
const [inputHeight, setInputHeight] = useState<number>(142);
const [notAllowedType, setNotAllowedType] = useState<EntityType | null>(null);
const [talkToConversationId, setTalkToConversationId] = useState<string>();

const handleTalkToConversationId = useCallback(
(conversationId: string | null) => {
dispatch(ConversationsActions.setTalkToConversationId(conversationId));
},
[dispatch],
);

const selectedConversationsTemporarySettings = useRef<
Record<string, ConversationsTemporarySettings>
Expand Down Expand Up @@ -471,8 +480,8 @@ export const ChatView = memo(() => {
}, []);

const handleTalkToClose = useCallback(() => {
setTalkToConversationId(undefined);
}, []);
handleTalkToConversationId(null);
}, [handleTalkToConversationId]);

const showLastMessageRegenerate =
!isReplay &&
Expand Down Expand Up @@ -582,7 +591,7 @@ export const ChatView = memo(() => {
}),
);
}}
onModelClick={setTalkToConversationId}
onModelClick={handleTalkToConversationId}
/>
</div>
)}
Expand Down Expand Up @@ -629,7 +638,7 @@ export const ChatView = memo(() => {
>
<EmptyChatDescription
conversation={conv}
onShowChangeModel={setTalkToConversationId}
onShowChangeModel={handleTalkToConversationId}
onShowSettings={setIsShowChatSettings}
/>
</div>
Expand Down
8 changes: 7 additions & 1 deletion apps/chat/src/components/Chat/ChatHeader/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,9 @@ export const ChatHeader = ({
<>
<span className="flex items-center" data-qa="chat-model">
<Tooltip
isTriggerClickable={
!(isMessageStreaming || disallowChangeAgent)
}
tooltip={
<HeaderModelTooltip
model={model}
Expand Down Expand Up @@ -329,6 +332,9 @@ export const ChatHeader = ({
<div className="flex items-center gap-2">
{isTopChatModelSettingsEnabled && !isConversationInvalid && (
<Tooltip
isTriggerClickable={
!(isMessageStreaming || disallowChangeSettings)
}
tooltip={
<HeaderSettingsTooltip
disallowChangeSettings={disallowChangeSettings}
Expand Down Expand Up @@ -374,7 +380,7 @@ export const ChatHeader = ({
!isConversationInvalid &&
!isCompareMode && (
<Tooltip
isTriggerClickable
isTriggerClickable={!isMessageStreaming}
tooltip={t('Clear conversation messages')}
>
<button
Expand Down
1 change: 1 addition & 0 deletions apps/chat/src/components/Header/CreateNewConversation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export const CreateNewConversation = ({ iconSize }: Props) => {
dispatch(
ConversationsActions.createNewConversations({
names: [DEFAULT_CONVERSATION_NAME],
headerCreateNew: true,
}),
);
dispatch(ConversationsActions.resetSearch());
Expand Down
217 changes: 119 additions & 98 deletions apps/chat/src/store/conversations/conversations.epics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,114 +353,135 @@ const initFoldersAndConversationsEpic: AppEpic = (action$) =>
const createNewConversationsEpic: AppEpic = (action$, state$) =>
action$.pipe(
filter(ConversationsActions.createNewConversations.match),
switchMap(({ payload: { names, modelReference, folderId } }) => {
return state$.pipe(
startWith(state$.value),
filter(ModelsSelectors.selectIsRecentModelsLoaded),
map((state) => {
const isIsolatedView = SettingsSelectors.selectIsIsolatedView(state);
const isolatedModelId =
SettingsSelectors.selectIsolatedModelId(state);

if (isIsolatedView && isolatedModelId) {
const models = ModelsSelectors.selectModels(state);
return models.filter(
(model) => model.reference === isolatedModelId,
)[0]?.reference;
}
switchMap(
({ payload: { names, modelReference, folderId, headerCreateNew } }) => {
return state$.pipe(
startWith(state$.value),
filter(ModelsSelectors.selectIsRecentModelsLoaded),
map((state) => {
const isIsolatedView =
SettingsSelectors.selectIsIsolatedView(state);
const isolatedModelId =
SettingsSelectors.selectIsolatedModelId(state);

if (isIsolatedView && isolatedModelId) {
const models = ModelsSelectors.selectModels(state);
return models.filter(
(model) => model.reference === isolatedModelId,
)[0]?.reference;
}

if (modelReference) {
return modelReference;
}
if (modelReference) {
return modelReference;
}

const modelReferences = ModelsSelectors.selectModels(state).map(
(m) => m.reference,
);
const recentModelReferences =
ModelsSelectors.selectRecentWithInstalledModelsIds(state).filter(
(reference) => modelReferences.includes(reference),
const modelReferences = ModelsSelectors.selectModels(state).map(
(m) => m.reference,
);
const recentModelReferences =
ModelsSelectors.selectRecentWithInstalledModelsIds(state).filter(
(reference) => modelReferences.includes(reference),
);

const overlayDefaultModel =
SettingsSelectors.selectOverlayDefaultModelId(state);
const isOverlay = SettingsSelectors.selectIsOverlay(state);
const overlayDefaultModel =
SettingsSelectors.selectOverlayDefaultModelId(state);
const isOverlay = SettingsSelectors.selectIsOverlay(state);

if (isOverlay && overlayDefaultModel) {
return getDefaultModelReference({
recentModelReferences,
modelReferences,
defaultModelId: overlayDefaultModel,
});
}
if (isOverlay && overlayDefaultModel) {
return getDefaultModelReference({
recentModelReferences,
modelReferences,
defaultModelId: overlayDefaultModel,
});
}

return [...recentModelReferences, ...modelReferences][0];
}),
take(1),
switchMap((modelReference) =>
forkJoin({
modelReference: of(modelReference),
lastConversationSettings: DataService.getLastConversationSettings(),
return [...recentModelReferences, ...modelReferences][0];
}),
),
switchMap(({ modelReference, lastConversationSettings }) => {
if (!modelReference) {
console.error(
'Creation failed: no models were found for conversation',
);
return EMPTY;
}

const nonLocalConversations =
ConversationsSelectors.selectConversations(state$.value).filter(
(conversation) => !isEntityIdLocal(conversation),
);
const conversationFolderId = folderId ?? getConversationRootId();
const newConversations: Conversation[] = names.map((name, index) =>
regenerateConversationId({
name:
name !== DEFAULT_CONVERSATION_NAME
? name
: getNextDefaultName(
DEFAULT_CONVERSATION_NAME,
nonLocalConversations.filter(
(conv) => conv.folderId === conversationFolderId,
),
index,
),
messages: [],
model: {
id: modelReference,
},
prompt: DefaultsService.get('defaultSystemPrompt', ''),
temperature:
lastConversationSettings?.temperature ?? DEFAULT_TEMPERATURE,
selectedAddons: [],
lastActivityDate: Date.now(),
status: UploadStatus.LOADED,
folderId: folderId ?? getConversationRootId(LOCAL_BUCKET),
take(1),
switchMap((modelReference) =>
forkJoin({
modelReference: of(modelReference),
lastConversationSettings:
DataService.getLastConversationSettings(),
}),
);
),
switchMap(({ modelReference, lastConversationSettings }) => {
if (!modelReference) {
console.error(
'Creation failed: no models were found for conversation',
);
return EMPTY;
}

return concat(
of(
ConversationsActions.createNotLocalConversations({
conversations: newConversations,
}),
),
of(
ConversationsActions.addConversations({
conversations: newConversations,
}),
),
of(
ConversationsActions.selectConversations({
conversationIds: newConversations.map((c) => c.id),
const nonLocalConversations =
ConversationsSelectors.selectConversations(state$.value).filter(
(conversation) => !isEntityIdLocal(conversation),
);
const conversationFolderId = folderId ?? getConversationRootId();
const newConversations: Conversation[] = names.map((name, index) =>
regenerateConversationId({
name:
name !== DEFAULT_CONVERSATION_NAME
? name
: getNextDefaultName(
DEFAULT_CONVERSATION_NAME,
nonLocalConversations.filter(
(conv) => conv.folderId === conversationFolderId,
),
index,
),
messages: [],
model: {
id: modelReference,
},
prompt: DefaultsService.get('defaultSystemPrompt', ''),
temperature:
lastConversationSettings?.temperature ?? DEFAULT_TEMPERATURE,
selectedAddons: [],
lastActivityDate: Date.now(),
status: UploadStatus.LOADED,
folderId: folderId ?? getConversationRootId(LOCAL_BUCKET),
}),
),
);
}),
);
}),
);
const selectedConversationsIds =
ConversationsSelectors.selectSelectedConversationsIds(
state$.value,
);

if (
headerCreateNew &&
selectedConversationsIds.length === 1 &&
isEntityIdLocal({ id: selectedConversationsIds[0] })
//TODO: Check if selectedConversation uses MindMap application
) {
return of(
ConversationsActions.setTalkToConversationId(
newConversations[0].id,
),
);
}

return concat(
of(
ConversationsActions.createNotLocalConversations({
conversations: newConversations,
}),
),
of(
ConversationsActions.addConversations({
conversations: newConversations,
}),
),
of(
ConversationsActions.selectConversations({
conversationIds: newConversations.map((c) => c.id),
}),
),
);
}),
);
},
),
);

const createNotLocalConversationsEpic: AppEpic = (action$) =>
Expand Down
8 changes: 8 additions & 0 deletions apps/chat/src/store/conversations/conversations.reducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ const initialState: ConversationsState = {
chosenConversationIds: [],
chosenEmptyFoldersIds: [],
renamingConversationId: null,
talkToConversationId: null,
};

export const conversationsSlice = createSlice({
Expand Down Expand Up @@ -209,6 +210,7 @@ export const conversationsSlice = createSlice({
folderId?: string | null;
modelReference?: string;
suspendHideSidebar?: boolean;
headerCreateNew?: boolean;
}>,
) => state,
createNotLocalConversations: (
Expand Down Expand Up @@ -860,6 +862,12 @@ export const conversationsSlice = createSlice({
) => {
state.renamingConversationId = payload;
},
setTalkToConversationId: (
state,
{ payload }: PayloadAction<string | null>,
) => {
state.talkToConversationId = payload;
},
},
});

Expand Down
5 changes: 5 additions & 0 deletions apps/chat/src/store/conversations/conversations.selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -835,3 +835,8 @@ export const selectRenamingConversation = createSelector(
(conversations, renamingConversationId) =>
conversations.find((conv) => conv.id === renamingConversationId),
);

export const selectТalkToConversationId = createSelector(
[rootSelector],
(state) => state.talkToConversationId,
);
1 change: 1 addition & 0 deletions apps/chat/src/store/conversations/conversations.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,5 @@ export interface ConversationsState {
chosenEmptyFoldersIds: string[];
lastConversationSettings?: LastConversationSettings;
renamingConversationId?: string | null;
talkToConversationId?: string | null;
}

0 comments on commit c10abbc

Please sign in to comment.