Skip to content

Commit

Permalink
πŸ› fix: fix editing long message issue (#2431)
Browse files Browse the repository at this point in the history
* ♻️ refactor: refactor chatLoadingId to chatLoadingIds

* πŸ’„ style: improve yi icon

* πŸ’„ style: fix openrouter model meta

* βœ… test: add test

* πŸ’„ style: fix translate item
  • Loading branch information
arvinxx authored May 8, 2024
1 parent 2630c86 commit 380d8da
Show file tree
Hide file tree
Showing 22 changed files with 194 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { useSendMessage } from '@/features/ChatInput/useSend';
import { useAgentStore } from '@/store/agent';
import { agentSelectors } from '@/store/agent/slices/chat';
import { useChatStore } from '@/store/chat';
import { chatSelectors } from '@/store/chat/selectors';
import { useUserStore } from '@/store/user';
import { modelProviderSelectors, preferenceSelectors } from '@/store/user/selectors';
import { isMacOS } from '@/utils/platform';
Expand Down Expand Up @@ -60,7 +61,7 @@ const Footer = memo<FooterProps>(({ setExpand }) => {
const { theme, styles } = useStyles();

const [loading, stopGenerateMessage] = useChatStore((s) => [
!!s.chatLoadingId,
chatSelectors.isAIGenerating(s),
s.stopGenerateMessage,
]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ describe('<InputArea />', () => {
it('does not send message when loading or shift key is pressed', () => {
const sendMessageMock = vi.fn();
act(() => {
useChatStore.setState({ chatLoadingId: '123', sendMessage: sendMessageMock });
useChatStore.setState({ chatLoadingIds: ['123'], sendMessage: sendMessageMock });
});

render(<InputArea setExpand={setExpandMock} />);
Expand All @@ -209,7 +209,7 @@ describe('<InputArea />', () => {
const sendMessageMock = vi.fn();
act(() => {
useChatStore.setState({
chatLoadingId: '',
chatLoadingIds: [],
inputMessage: 'abc',
sendMessage: sendMessageMock,
});
Expand All @@ -228,7 +228,7 @@ describe('<InputArea />', () => {
const sendMessageMock = vi.fn();
act(() => {
useChatStore.setState({
chatLoadingId: '',
chatLoadingIds: [],
inputMessage: '123',
sendMessage: sendMessageMock,
});
Expand All @@ -247,7 +247,7 @@ describe('<InputArea />', () => {
const updateInputMessageMock = vi.fn();
act(() => {
useChatStore.setState({
chatLoadingId: '',
chatLoadingIds: [],
inputMessage: 'Test',
sendMessage: sendMessageMock,
updateInputMessage: updateInputMessageMock,
Expand All @@ -271,7 +271,7 @@ describe('<InputArea />', () => {
const sendMessageMock = vi.fn();
act(() => {
useChatStore.setState({
chatLoadingId: '',
chatLoadingIds: [],
inputMessage: '123',
sendMessage: sendMessageMock,
});
Expand All @@ -295,7 +295,7 @@ describe('<InputArea />', () => {
const updateInputMessageMock = vi.fn();
act(() => {
useChatStore.setState({
chatLoadingId: '',
chatLoadingIds: [],
inputMessage: 'Test',
sendMessage: sendMessageMock,
updateInputMessage: updateInputMessageMock,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next';

import { useSendMessage } from '@/features/ChatInput/useSend';
import { useChatStore } from '@/store/chat';
import { chatSelectors } from '@/store/chat/selectors';
import { useUserStore } from '@/store/user';
import { preferenceSelectors } from '@/store/user/selectors';
import { isCommandPressed } from '@/utils/keyboard';
Expand Down Expand Up @@ -42,7 +43,7 @@ const InputArea = memo<InputAreaProps>(({ setExpand }) => {
const isChineseInput = useRef(false);

const [loading, value, updateInputMessage] = useChatStore((s) => [
!!s.chatLoadingId,
chatSelectors.isAIGenerating(s),
s.inputMessage,
s.updateInputMessage,
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { shallow } from 'zustand/shallow';
import ModelTag from '@/components/ModelTag';
import { useAgentStore } from '@/store/agent';
import { useChatStore } from '@/store/chat';
import { chatSelectors } from '@/store/chat/selectors';
import { useSessionStore } from '@/store/session';
import { sessionHelpers } from '@/store/session/helpers';
import { sessionMetaSelectors, sessionSelectors } from '@/store/session/selectors';
Expand All @@ -23,7 +24,7 @@ const SessionItem = memo<SessionItemProps>(({ id }) => {
const [defaultModel] = useAgentStore((s) => [s.defaultAgentConfig.model]);

const [active] = useSessionStore((s) => [s.activeId === id]);
const [loading] = useChatStore((s) => [!!s.chatLoadingId && id === s.activeId]);
const [loading] = useChatStore((s) => [chatSelectors.isAIGenerating(s) && id === s.activeId]);

const [pin, title, description, avatar, avatarBackground, updateAt, model, group] =
useSessionStore((s) => {
Expand Down
4 changes: 2 additions & 2 deletions src/components/ModelTag/ModelIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
Stability,
Tongyi,
Wenxin,
ZeroOne,
Yi,
} from '@lobehub/icons';
import { memo } from 'react';

Expand All @@ -54,7 +54,7 @@ const ModelIcon = memo<ModelIconProps>(({ model, size = 12 }) => {
if (model.includes('abab')) return <Minimax size={size} />;
if (model.includes('mistral') || model.includes('mixtral')) return <Mistral size={size} />;
if (model.includes('pplx') || model.includes('sonar')) return <Perplexity size={size} />;
if (model.includes('yi-')) return <ZeroOne size={size} />;
if (model.includes('yi-')) return <Yi size={size} />;
if (model.startsWith('openrouter')) return <OpenRouter size={size} />; // only for Cinematika and Auto
if (model.startsWith('openchat')) return <OpenChat size={size} />;
if (model.includes('command')) return <Cohere size={size} />;
Expand Down
3 changes: 2 additions & 1 deletion src/features/ChatInput/STT/browser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { SWRConfiguration } from 'swr';
import { useAgentStore } from '@/store/agent';
import { agentSelectors } from '@/store/agent/slices/chat';
import { useChatStore } from '@/store/chat';
import { chatSelectors } from '@/store/chat/selectors';
import { useUserStore } from '@/store/user';
import { settingsSelectors } from '@/store/user/selectors';
import { ChatMessageError } from '@/types/message';
Expand Down Expand Up @@ -40,7 +41,7 @@ const BrowserSTT = memo<{ mobile?: boolean }>(({ mobile }) => {
const { t } = useTranslation('chat');

const [loading, updateInputMessage] = useChatStore((s) => [
!!s.chatLoadingId,
chatSelectors.isAIGenerating(s),
s.updateInputMessage,
]);

Expand Down
3 changes: 2 additions & 1 deletion src/features/ChatInput/STT/openai.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { API_ENDPOINTS } from '@/services/_url';
import { useAgentStore } from '@/store/agent';
import { agentSelectors } from '@/store/agent/selectors';
import { useChatStore } from '@/store/chat';
import { chatSelectors } from '@/store/chat/slices/message/selectors';
import { useUserStore } from '@/store/user';
import { settingsSelectors } from '@/store/user/selectors';
import { ChatMessageError } from '@/types/message';
Expand Down Expand Up @@ -51,7 +52,7 @@ const OpenaiSTT = memo<{ mobile?: boolean }>(({ mobile }) => {
const { t } = useTranslation('chat');

const [loading, updateInputMessage] = useChatStore((s) => [
!!s.chatLoadingId,
chatSelectors.isAIGenerating(s),
s.updateInputMessage,
]);

Expand Down
3 changes: 2 additions & 1 deletion src/features/ChatInput/useChatInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useCallback, useRef, useState } from 'react';
import { useAgentStore } from '@/store/agent';
import { agentSelectors } from '@/store/agent/slices/chat';
import { useChatStore } from '@/store/chat';
import { chatSelectors } from '@/store/chat/selectors';
import { useUserStore } from '@/store/user';
import { modelProviderSelectors } from '@/store/user/selectors';

Expand All @@ -18,7 +19,7 @@ export const useChatInput = () => {
const canUpload = useUserStore(modelProviderSelectors.isModelEnabledUpload(model));

const [loading, value, onInput, onStop] = useChatStore((s) => [
!!s.chatLoadingId,
chatSelectors.isAIGenerating(s),
s.inputMessage,
s.updateInputMessage,
s.stopGenerateMessage,
Expand Down
3 changes: 2 additions & 1 deletion src/features/ChatInput/useSend.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useCallback } from 'react';

import { useChatStore } from '@/store/chat';
import { chatSelectors } from '@/store/chat/selectors';
import { SendMessageParams } from '@/store/chat/slices/message/action';
import { filesSelectors, useFileStore } from '@/store/file';

Expand All @@ -17,7 +18,7 @@ export const useSendMessage = () => {

return useCallback((params: UseSendMessageParams = {}) => {
const store = useChatStore.getState();
if (!!store.chatLoadingId) return;
if (chatSelectors.isAIGenerating(store)) return;
if (!store.inputMessage) return;

const imageList = filesSelectors.imageUrlOrBase64List(useFileStore.getState());
Expand Down
9 changes: 9 additions & 0 deletions src/features/Conversation/Actions/customAction.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import { ActionIconGroupItems } from '@lobehub/ui/es/ActionIconGroup';
import { css, cx } from 'antd-style';
import { LanguagesIcon, Play } from 'lucide-react';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';

import { localeOptions } from '@/locales/resources';

const translateStyle = css`
.ant-dropdown-menu-sub {
overflow-y: scroll;
max-height: 400px;
}
`;

export const useCustomActions = () => {
const { t } = useTranslation('chat');

Expand All @@ -16,6 +24,7 @@ export const useCustomActions = () => {
icon: LanguagesIcon,
key: 'translate',
label: t('translate.action'),
popupClassName: cx(translateStyle),
} as ActionIconGroupItems;

const tts = {
Expand Down
3 changes: 2 additions & 1 deletion src/features/Conversation/Extras/Assistant.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import ModelTag from '@/components/ModelTag';
import { useAgentStore } from '@/store/agent';
import { agentSelectors } from '@/store/agent/slices/chat';
import { useChatStore } from '@/store/chat';
import { chatSelectors } from '@/store/chat/selectors';
import { ChatMessage } from '@/types/message';

import { RenderMessageExtra } from '../types';
Expand All @@ -15,7 +16,7 @@ import Translate from './Translate';
export const AssistantMessageExtra: RenderMessageExtra = memo<ChatMessage>(
({ extra, id, content }) => {
const model = useAgentStore(agentSelectors.currentAgentModel);
const loading = useChatStore((s) => s.chatLoadingId === id);
const loading = useChatStore(chatSelectors.isMessageGenerating(id));

const showModelTag = extra?.fromModel && model !== extra?.fromModel;
const showTranslate = !!extra?.translate;
Expand Down
3 changes: 2 additions & 1 deletion src/features/Conversation/Extras/User.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { memo } from 'react';

import { useChatStore } from '@/store/chat';
import { chatSelectors } from '@/store/chat/selectors';
import { ChatMessage } from '@/types/message';

import { RenderMessageExtra } from '../types';
Expand All @@ -9,7 +10,7 @@ import TTS from './TTS';
import Translate from './Translate';

export const UserMessageExtra: RenderMessageExtra = memo<ChatMessage>(({ extra, id, content }) => {
const loading = useChatStore((s) => s.chatLoadingId === id);
const loading = useChatStore(chatSelectors.isMessageGenerating(id));

const showTranslate = !!extra?.translate;
const showTTS = !!extra?.tts;
Expand Down
2 changes: 1 addition & 1 deletion src/features/Conversation/components/AutoScroll.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ interface AutoScrollProps {
onScrollToBottom: (type: 'auto' | 'click') => void;
}
const AutoScroll = memo<AutoScrollProps>(({ atBottom, isScrolling, onScrollToBottom }) => {
const trackVisibility = useChatStore((s) => !!s.chatLoadingId);
const trackVisibility = useChatStore(chatSelectors.isAIGenerating);
const str = useChatStore(chatSelectors.chatsMessageString);

useEffect(() => {
Expand Down
34 changes: 22 additions & 12 deletions src/features/Conversation/components/ChatItem/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AlertProps, ChatItem } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import isEqual from 'fast-deep-equal';
import { ReactNode, memo, useCallback, useMemo, useState } from 'react';
import { ReactNode, memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';

import { useAgentStore } from '@/store/agent';
Expand Down Expand Up @@ -41,7 +41,6 @@ const Item = memo<ChatListItemProps>(({ index, id }) => {
const fontSize = useUserStore((s) => settingsSelectors.currentSettings(s).fontSize);
const { t } = useTranslation('common');
const { styles, cx } = useStyles();
const [editing, setEditing] = useState(false);
const [type = 'chat'] = useAgentStore((s) => {
const config = agentSelectors.currentAgentConfig(s);
return [config.displayMode];
Expand All @@ -58,12 +57,14 @@ const Item = memo<ChatListItemProps>(({ index, id }) => {

const historyLength = useChatStore((s) => chatSelectors.currentChats(s).length);

const [loading, updateMessageContent] = useChatStore((s) => [
s.chatLoadingId === id || s.messageLoadingIds.includes(id),
s.modifyMessageContent,
]);

const [isMessageLoading] = useChatStore((s) => [s.messageLoadingIds.includes(id)]);
const [isMessageLoading, generating, editing, toggleMessageEditing, updateMessageContent] =
useChatStore((s) => [
chatSelectors.isMessageLoading(id)(s),
chatSelectors.isMessageGenerating(id)(s),
chatSelectors.isMessageEditing(id)(s),
s.toggleMessageEditing,
s.modifyMessageContent,
]);

const onAvatarsClick = useAvatarsClick();

Expand Down Expand Up @@ -115,25 +116,34 @@ const Item = memo<ChatListItemProps>(({ index, id }) => {
<>
<HistoryDivider enable={enableHistoryDivider} />
<ChatItem
actions={<ActionsBar index={index} setEditing={setEditing} />}
actions={
<ActionsBar
index={index}
setEditing={(edit) => {
toggleMessageEditing(id, edit);
}}
/>
}
avatar={item.meta}
className={cx(styles.message, isMessageLoading && styles.loading)}
editing={editing}
error={error}
errorMessage={<ErrorMessageExtra data={item} />}
fontSize={fontSize}
loading={loading}
loading={generating}
message={item.content}
messageExtra={<MessageExtra data={item} />}
onAvatarClick={onAvatarsClick?.(item.role)}
onChange={(value) => updateMessageContent(item.id, value)}
onDoubleClick={(e) => {
if (item.id === 'default' || item.error) return;
if (item.role && ['assistant', 'user'].includes(item.role) && e.altKey) {
setEditing(true);
toggleMessageEditing(id, true);
}
}}
onEditingChange={setEditing}
onEditingChange={(edit) => {
toggleMessageEditing(id, edit);
}}
placement={type === 'chat' ? (item.role === 'user' ? 'right' : 'left') : 'left'}
primary={item.role === 'user'}
renderMessage={(editableContent) => (
Expand Down
5 changes: 4 additions & 1 deletion src/libs/agent-runtime/openrouter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ export const LobeOpenRouterAI = LobeOpenAICompatibleFactory({
? model.top_provider.max_completion_tokens
: undefined,
tokens: model.context_length,
vision: model.description.includes('vision') || model.id.includes('vision'),
vision:
model.description.includes('vision') ||
model.description.includes('multimodal') ||
model.id.includes('vision'),
};
},
},
Expand Down
Loading

0 comments on commit 380d8da

Please sign in to comment.