Skip to content

Commit

Permalink
✨ feat: 为所有新角色添加欢迎消息 (#77)
Browse files Browse the repository at this point in the history
  • Loading branch information
arvinxx authored Aug 14, 2023
2 parents 986f975 + 06cb654 commit cf5bd60
Show file tree
Hide file tree
Showing 12 changed files with 116 additions and 39 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ config.rules['unicorn/no-null'] = 0;
config.rules['unicorn/no-typeof-undefined'] = 0;
config.rules['unicorn/explicit-length-check'] = 0;
config.rules['unicorn/prefer-code-point'] = 0;
config.rules['no-extra-boolean-cast'] = 0;

module.exports = config;
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
"react-dom": "^18",
"react-hotkeys-hook": "^4",
"react-i18next": "^13",
"react-layout-kit": "^1",
"react-layout-kit": "^1.7.1",
"serpapi": "^2",
"swr": "^2",
"ts-md5": "^1",
Expand Down
5 changes: 5 additions & 0 deletions src/locales/default/common.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
export default {
about: '关于',
advanceSettings: '高级设置',
agentDefaultMessage:
'你好,我是你的自定义助手,你可以立即与我开始对话,也可以前往 [助手设置](/chat/{{id}}/setting) 完善我的信息。',
agentDefaultMessageWithSystemRole:
'你好,我是你的自定义助手,我的角色设定如下: \n\n --- \n\n {{systemRole}} \n\n --- \n\n 立即与我开始对话吧~',
agentMaxToken: '会话最大长度',
agentModel: '模型',
agentProfile: '助手信息',
Expand Down Expand Up @@ -31,6 +35,7 @@ export default {
},
feedback: '反馈与建议',
import: '导入配置',

inbox: {
defaultMessage:
'你好,我是你的智能助手,你可以问我任何问题,我会尽力回答你。如果需要获得更加专业或定制的助手,可以点击「+」创建自定义助手',
Expand Down
32 changes: 6 additions & 26 deletions src/pages/chat/features/Conversation/ChatList/index.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { ChatList, RenderErrorMessage, RenderMessage } from '@lobehub/ui';
import isEqual from 'fast-deep-equal';
import { memo, useCallback, useMemo } from 'react';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { Flexbox } from 'react-layout-kit';

import { DEFAULT_INBOX_AVATAR } from '@/const/meta';
import { INBOX_SESSION_ID } from '@/const/session';
import {
agentSelectors,
chatSelectors,
useSessionHydrated,
useSessionChatInit,
useSessionStore,
} from '@/store/session';
import { ChatMessage } from '@/types/chatMessage';
Expand All @@ -35,14 +33,13 @@ const renderErrorMessage: RenderErrorMessage = (error, message) => {
};

const List = () => {
const init = useSessionHydrated();
const init = useSessionChatInit();
const { t } = useTranslation('common');

const data = useSessionStore(chatSelectors.currentChats, isEqual);
const data = useSessionStore(chatSelectors.currentChatsWithGuideMessage, isEqual);

const [isInbox, displayMode, chatLoadingId, deleteMessage, resendMessage, dispatchMessage] =
const [displayMode, chatLoadingId, deleteMessage, resendMessage, dispatchMessage] =
useSessionStore((s) => [
s.activeId === INBOX_SESSION_ID,
agentSelectors.currentAgentConfig(s).displayMode,
s.chatLoadingId,
s.deleteMessage,
Expand Down Expand Up @@ -81,28 +78,11 @@ const List = () => {
[chatLoadingId],
);

// 针对 inbox 添加初始化时的自定义消息
const displayDataSource = useMemo(() => {
const emptyGuideMessage = {
content: t('inbox.defaultMessage'),
createAt: Date.now(),
extra: {},
id: 'default',
meta: {
avatar: DEFAULT_INBOX_AVATAR,
},
role: 'assistant',
updateAt: Date.now(),
} as ChatMessage;

return isInbox && data.length === 0 ? [emptyGuideMessage] : data;
}, [data]);

return !init ? (
<SkeletonList />
) : (
<ChatList
data={displayDataSource}
data={data}
loadingId={chatLoadingId}
onActionClick={(key, id) => {
switch (key) {
Expand Down
4 changes: 2 additions & 2 deletions src/pages/chat/features/Header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import HeaderTitle from '@/components/HeaderTitle';
import Tag from '@/components/Tag';
import { INBOX_SESSION_ID } from '@/const/session';
import { useGlobalStore } from '@/store/global';
import { agentSelectors, useSessionHydrated, useSessionStore } from '@/store/session';
import { agentSelectors, useSessionChatInit, useSessionStore } from '@/store/session';

import PluginTag from './PluginTag';

const Header = memo<{ settings?: boolean }>(({ settings = true }) => {
const init = useSessionHydrated();
const init = useSessionChatInit();

const { t } = useTranslation('common');

Expand Down
10 changes: 5 additions & 5 deletions src/pages/chat/features/Sidebar/SystemRole.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next';
import { Flexbox } from 'react-layout-kit';

import Header from '@/pages/chat/features/Sidebar/Header';
import { agentSelectors, useSessionHydrated, useSessionStore } from '@/store/session';
import { agentSelectors, useSessionChatInit, useSessionStore } from '@/store/session';

const useStyles = createStyles(({ css, token }) => ({
prompt: css`
Expand Down Expand Up @@ -49,7 +49,7 @@ const SystemRole = memo(() => {
s.updateAgentConfig,
]);

const hydrated = useSessionHydrated();
const init = useSessionChatInit();
const { t } = useTranslation('common');
return (
<Flexbox height={'fit-content'}>
Expand All @@ -65,7 +65,9 @@ const SystemRole = memo(() => {
title={t('settingAgent.prompt.title', { ns: 'setting' })}
/>
<Flexbox className={styles.promptBox} height={200} padding={'0 16px 16px'}>
{hydrated ? (
{!init ? (
<Skeleton active avatar={false} round style={{ marginTop: 12 }} title={false} />
) : (
<>
<EditableMessage
classNames={{ markdown: styles.prompt }}
Expand All @@ -86,8 +88,6 @@ const SystemRole = memo(() => {
/>
<div className={styles.promptMask} />
</>
) : (
<Skeleton active avatar={false} style={{ marginTop: 12 }} title={false} />
)}
</Flexbox>
</Flexbox>
Expand Down
6 changes: 3 additions & 3 deletions src/pages/chat/features/Sidebar/Topic/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import { Flexbox } from 'react-layout-kit';

import Empty from '@/components/Empty';
import { useGlobalStore } from '@/store/global';
import { topicSelectors, useSessionHydrated, useSessionStore } from '@/store/session';
import { topicSelectors, useSessionChatInit, useSessionStore } from '@/store/session';

import SkeletonList from './SkeletonList';
import TopicItem from './TopicItem';

export const Topic = () => {
const hydrated = useSessionHydrated();
const init = useSessionChatInit();
const topics = useSessionStore(topicSelectors.currentTopics, isEqual);
const { isDarkMode } = useThemeMode();
const [activeTopicId] = useSessionStore((s) => [s.activeTopicId]);
Expand All @@ -22,7 +22,7 @@ export const Topic = () => {
s.updateGuideState,
]);

return !hydrated ? (
return !init ? (
<SkeletonList />
) : (
<Flexbox gap={2} style={{ marginBottom: 12 }}>
Expand Down
42 changes: 42 additions & 0 deletions src/plugins/type.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,59 @@
import { ChatCompletionFunctions } from 'openai-edge/types/api';
import { ReactNode } from 'react';

/**
* 插件项
* @template Result - 结果类型,默认为 any
* @template RunnerParams - 运行参数类型,默认为 any
*/
export interface PluginItem<Result = any, RunnerParams = any> {
/**
* 头像
*/
avatar: string;
/**
* 名称
*/
name: string;
/**
* 运行器
* @param params - 运行参数
* @returns 运行结果的 Promise
*/
runner: PluginRunner<RunnerParams, Result>;
/**
* 聊天完成函数的模式
*/
schema: ChatCompletionFunctions;
}

/**
* 插件渲染函数
* @param props - 插件渲染属性
* @returns React 节点
*/
export type PluginRender = (props: PluginRenderProps) => ReactNode;

/**
* 插件运行器
* @template Params - 参数类型,默认为 object
* @template Result - 结果类型,默认为 any
* @param params - 运行参数
* @returns 运行结果的 Promise
*/
export type PluginRunner<Params = object, Result = any> = (params: Params) => Promise<Result>;

/**
* 插件渲染属性
* @template Result - 结果类型,默认为 any
*/
export interface PluginRenderProps<Result = any> {
/**
* 内容
*/
content: Result;
/**
* 名称
*/
name: string;
}
1 change: 1 addition & 0 deletions src/store/session/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './useOnFinishHydrationSession';
export * from './useSessionChatInit';
export * from './useSessionHydrated';
12 changes: 12 additions & 0 deletions src/store/session/hooks/useSessionChatInit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { useSessionStore } from '../store';
import { useSessionHydrated } from './useSessionHydrated';

/**
* 用于判断某个会话是否完全被激活
*/
export const useSessionChatInit = () => {
const sessionHydrated = useSessionHydrated();
const [hasActive] = useSessionStore((s) => [!!s.activeId]);

return sessionHydrated && hasActive;
};
37 changes: 36 additions & 1 deletion src/store/session/slices/chat/selectors/chat.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { DEFAULT_USER_AVATAR } from '@/const/meta';
import { t } from 'i18next';

import { DEFAULT_INBOX_AVATAR, DEFAULT_USER_AVATAR } from '@/const/meta';
import { INBOX_SESSION_ID } from '@/const/session';
import { useGlobalStore } from '@/store/global';
import { ChatMessage } from '@/types/chatMessage';

import type { SessionStore } from '../../../store';
import { agentSelectors } from '../../agentConfig';
import { sessionSelectors } from '../../session/selectors';
import { getSlicedMessagesWithConfig } from '../utils';
import { currentTopics } from './topic';
import { organizeChats } from './utils';

export const getChatsById =
Expand Down Expand Up @@ -36,6 +40,37 @@ export const currentChats = (s: SessionStore): ChatMessage[] => {
return getChatsById(s.activeId)(s);
};

// 针对新助手添加初始化时的自定义消息
export const currentChatsWithGuideMessage = (s: SessionStore): ChatMessage[] => {
const data = currentChats(s);
const noTopic = currentTopics(s);

const isBrandNewChat = data.length === 0 && noTopic;

if (!isBrandNewChat) return data;

const [activeId, isInbox] = [s.activeId, s.activeId === INBOX_SESSION_ID];
const systemRole = agentSelectors.currentAgentSystemRole(s);

const emptyInboxGuideMessage = {
content: isInbox
? t('inbox.defaultMessage')
: !!systemRole
? t('agentDefaultMessageWithSystemRole', { systemRole })
: t('agentDefaultMessage', { id: activeId }),
createAt: Date.now(),
extra: {},
id: 'default',
meta: {
avatar: DEFAULT_INBOX_AVATAR,
},
role: 'assistant',
updateAt: Date.now(),
} as ChatMessage;

return [emptyInboxGuideMessage];
};

export const currentChatsWithHistoryConfig = (s: SessionStore): ChatMessage[] => {
const chats = currentChats(s);
const config = agentSelectors.currentAgentConfig(s);
Expand Down
3 changes: 2 additions & 1 deletion src/store/session/slices/chat/selectors/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { currentChats, getChatsById } from './chat';
import { currentChats, currentChatsWithGuideMessage, getChatsById } from './chat';
import { chatsTokenCount, systemRoleTokenCount, totalTokenCount } from './token';
import { currentTopics, getTopicMessages } from './topic';

export const chatSelectors = {
chatsTokenCount,
currentChats,
currentChatsWithGuideMessage,
getChatsById,
systemRoleTokenCount,
totalTokenCount,
Expand Down

0 comments on commit cf5bd60

Please sign in to comment.