@lobehub/chat 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/.changelogrc.js +1 -0
  2. package/.commitlintrc.js +1 -0
  3. package/.editorconfig +16 -0
  4. package/.eslintignore +32 -0
  5. package/.eslintrc.js +6 -0
  6. package/.github/ISSUE_TEMPLATE/1_bug_report.yml +45 -0
  7. package/.github/ISSUE_TEMPLATE/2_feature_request.yml +21 -0
  8. package/.github/ISSUE_TEMPLATE/3_question.yml +15 -0
  9. package/.github/ISSUE_TEMPLATE/4_other.md +7 -0
  10. package/.github/PULL_REQUEST_TEMPLATE.md +17 -0
  11. package/.github/dependabot.yml +17 -0
  12. package/.github/workflows/auto-merge.yml +32 -0
  13. package/.github/workflows/contributor-help.yml +29 -0
  14. package/.github/workflows/issue-check-inactive.yml +22 -0
  15. package/.github/workflows/issue-close-require.yml +46 -0
  16. package/.github/workflows/issue-remove-inactive.yml +25 -0
  17. package/.github/workflows/release.yml +34 -0
  18. package/.github/workflows/test.yml +30 -0
  19. package/.gitpod.yml +3 -0
  20. package/.husky/commit-msg +4 -0
  21. package/.husky/pre-commit +5 -0
  22. package/.i18nrc.js +13 -0
  23. package/.prettierignore +63 -0
  24. package/.prettierrc.js +1 -0
  25. package/.releaserc.js +1 -0
  26. package/.remarkrc.js +1 -0
  27. package/.stylelintrc.js +8 -0
  28. package/CHANGELOG.md +20 -0
  29. package/README.md +147 -0
  30. package/locales/en_US/common.json +41 -0
  31. package/locales/en_US/setting.json +117 -0
  32. package/locales/zh_CN/common.json +41 -0
  33. package/locales/zh_CN/setting.json +117 -0
  34. package/next.config.mjs +32 -0
  35. package/package.json +140 -0
  36. package/public/next.svg +1 -0
  37. package/public/vercel.svg +1 -0
  38. package/scripts/genDefaultLocale.ts +18 -0
  39. package/scripts/genResources.ts +51 -0
  40. package/scripts/toc.ts +7 -0
  41. package/src/const/fetch.ts +1 -0
  42. package/src/const/modelTokens.ts +8 -0
  43. package/src/features/AvatarWithUpload/index.tsx +27 -0
  44. package/src/features/FolderPanel/index.tsx +55 -0
  45. package/src/features/SideBar/index.tsx +35 -0
  46. package/src/helpers/prompt.test.ts +36 -0
  47. package/src/helpers/prompt.ts +36 -0
  48. package/src/helpers/url.ts +17 -0
  49. package/src/layout/index.tsx +57 -0
  50. package/src/layout/style.ts +18 -0
  51. package/src/locales/create.ts +49 -0
  52. package/src/locales/default/common.ts +41 -0
  53. package/src/locales/default/setting.ts +117 -0
  54. package/src/locales/index.ts +5 -0
  55. package/src/locales/options.ts +19 -0
  56. package/src/locales/resources/en_US.ts +9 -0
  57. package/src/locales/resources/index.ts +13 -0
  58. package/src/locales/resources/zh_CN.ts +9 -0
  59. package/src/migrations/FromV0ToV1.ts +12 -0
  60. package/src/migrations/index.ts +13 -0
  61. package/src/pages/_app.page.tsx +13 -0
  62. package/src/pages/_document.page.tsx +70 -0
  63. package/src/pages/api/LangChainStream.ts +95 -0
  64. package/src/pages/api/chain.api.ts +17 -0
  65. package/src/pages/api/openai.api.ts +31 -0
  66. package/src/pages/chat/SessionList/Header.tsx +56 -0
  67. package/src/pages/chat/SessionList/List/SessionItem.tsx +90 -0
  68. package/src/pages/chat/SessionList/List/index.tsx +31 -0
  69. package/src/pages/chat/SessionList/List/style.ts +77 -0
  70. package/src/pages/chat/SessionList/index.tsx +18 -0
  71. package/src/pages/chat/[id]/Config/ConfigCell.tsx +68 -0
  72. package/src/pages/chat/[id]/Config/ReadMode.tsx +63 -0
  73. package/src/pages/chat/[id]/Config/index.tsx +79 -0
  74. package/src/pages/chat/[id]/Conversation/ChatList.tsx +36 -0
  75. package/src/pages/chat/[id]/Conversation/Input.tsx +61 -0
  76. package/src/pages/chat/[id]/Conversation/index.tsx +32 -0
  77. package/src/pages/chat/[id]/Header.tsx +86 -0
  78. package/src/pages/chat/[id]/edit/AgentConfig.tsx +95 -0
  79. package/src/pages/chat/[id]/edit/AgentMeta.tsx +117 -0
  80. package/src/pages/chat/[id]/edit/FormItem.tsx +26 -0
  81. package/src/pages/chat/[id]/edit/Prompt.tsx +68 -0
  82. package/src/pages/chat/[id]/edit/index.page.tsx +62 -0
  83. package/src/pages/chat/[id]/edit/style.ts +42 -0
  84. package/src/pages/chat/[id]/index.page.tsx +40 -0
  85. package/src/pages/chat/index.page.tsx +1 -0
  86. package/src/pages/chat/layout.tsx +51 -0
  87. package/src/pages/index.page.tsx +1 -0
  88. package/src/pages/setting/Header.tsx +27 -0
  89. package/src/pages/setting/SettingForm.tsx +299 -0
  90. package/src/pages/setting/SliderWithInput/index.tsx +40 -0
  91. package/src/pages/setting/ThemeSwatches/ThemeSwatchesNeutral.tsx +33 -0
  92. package/src/pages/setting/ThemeSwatches/ThemeSwatchesPrimary.tsx +33 -0
  93. package/src/pages/setting/ThemeSwatches/index.ts +2 -0
  94. package/src/pages/setting/index.page.tsx +41 -0
  95. package/src/prompts/agent.ts +65 -0
  96. package/src/services/chatModel.ts +34 -0
  97. package/src/services/langChain.ts +18 -0
  98. package/src/services/url.ts +8 -0
  99. package/src/store/middleware/createHashStorage.ts +49 -0
  100. package/src/store/session/index.ts +33 -0
  101. package/src/store/session/initialState.ts +11 -0
  102. package/src/store/session/selectors.ts +3 -0
  103. package/src/store/session/slices/agentConfig/action.ts +226 -0
  104. package/src/store/session/slices/agentConfig/index.ts +3 -0
  105. package/src/store/session/slices/agentConfig/initialState.ts +34 -0
  106. package/src/store/session/slices/agentConfig/selectors.ts +54 -0
  107. package/src/store/session/slices/chat/action.ts +210 -0
  108. package/src/store/session/slices/chat/index.ts +3 -0
  109. package/src/store/session/slices/chat/initialState.ts +12 -0
  110. package/src/store/session/slices/chat/messageReducer.test.ts +70 -0
  111. package/src/store/session/slices/chat/messageReducer.ts +84 -0
  112. package/src/store/session/slices/chat/selectors.ts +83 -0
  113. package/src/store/session/slices/session/action.ts +118 -0
  114. package/src/store/session/slices/session/index.ts +3 -0
  115. package/src/store/session/slices/session/initialState.ts +31 -0
  116. package/src/store/session/slices/session/reducers/session.test.ts +456 -0
  117. package/src/store/session/slices/session/reducers/session.ts +113 -0
  118. package/src/store/session/slices/session/selectors/chat.ts +4 -0
  119. package/src/store/session/slices/session/selectors/index.ts +20 -0
  120. package/src/store/session/slices/session/selectors/list.ts +65 -0
  121. package/src/store/session/store.ts +17 -0
  122. package/src/store/settings/action.ts +40 -0
  123. package/src/store/settings/index.ts +23 -0
  124. package/src/store/settings/initialState.ts +41 -0
  125. package/src/store/settings/selectors.ts +17 -0
  126. package/src/store/settings/store.ts +13 -0
  127. package/src/styles/antdOverride.ts +33 -0
  128. package/src/styles/global.ts +23 -0
  129. package/src/styles/index.ts +6 -0
  130. package/src/types/chatMessage.ts +46 -0
  131. package/src/types/exportConfig.ts +44 -0
  132. package/src/types/global.d.ts +14 -0
  133. package/src/types/i18next.d.ts +8 -0
  134. package/src/types/langchain.ts +34 -0
  135. package/src/types/llm.ts +49 -0
  136. package/src/types/locale.ts +1 -0
  137. package/src/types/meta.ts +26 -0
  138. package/src/types/openai.ts +62 -0
  139. package/src/types/session.ts +59 -0
  140. package/src/utils/VersionController.test.ts +90 -0
  141. package/src/utils/VersionController.ts +64 -0
  142. package/src/utils/compass.ts +94 -0
  143. package/src/utils/fetch.ts +132 -0
  144. package/src/utils/filter.test.ts +120 -0
  145. package/src/utils/filter.ts +29 -0
  146. package/src/utils/uploadFIle.ts +8 -0
  147. package/src/utils/uuid.ts +9 -0
  148. package/tsconfig.json +31 -0
  149. package/vitest.config.ts +11 -0
@@ -0,0 +1,12 @@
1
+ export interface ChatState {
2
+ chatLoading: boolean;
3
+ editingMessageId?: string;
4
+ }
5
+
6
+ export const initialChatState: ChatState = {
7
+ chatLoading: false,
8
+
9
+ // activeId: null,
10
+ // searchKeywords: '',
11
+ //
12
+ };
@@ -0,0 +1,70 @@
1
+ test('placeholder', () => {});
2
+ // describe('messagesReducer', () => {
3
+ // let initialState: ChatMessage[];
4
+ //
5
+ // beforeEach(() => {
6
+ // initialState = [
7
+ // { role: 'user', content: 'Hello!' },
8
+ // { role: 'assistant', content: 'Hi there!' },
9
+ // ];
10
+ // });
11
+ //
12
+ // it('should add a message', () => {
13
+ // const newMessage: ChatMessage = { role: 'user', content: 'How are you?' };
14
+ // const action: MessageDispatch = { type: 'addMessage', message: newMessage };
15
+ // const newState = messagesReducer(initialState, action);
16
+ // expect(newState).toEqual([...initialState, newMessage]);
17
+ // });
18
+ //
19
+ // it('should delete a message', () => {
20
+ // const action: MessageDispatch = { type: 'deleteMessage', index: 1 };
21
+ // const newState = messagesReducer(initialState, action);
22
+ // expect(newState).toEqual([{ role: 'user', content: 'Hello!' }]);
23
+ // });
24
+ //
25
+ // it('should update a message', () => {
26
+ // const action: MessageDispatch = { type: 'updateMessage', index: 1, message: 'I am fine!' };
27
+ // const newState = messagesReducer(initialState, action);
28
+ // expect(newState).toEqual([
29
+ // { role: 'user', content: 'Hello!' },
30
+ // { role: 'assistant', content: 'I am fine!' },
31
+ // ]);
32
+ // });
33
+ //
34
+ // it('should add a user message', () => {
35
+ // const action: MessageDispatch = { type: 'addUserMessage', message: 'Goodbye!' };
36
+ // const newState = messagesReducer(initialState, action);
37
+ // expect(newState).toEqual([
38
+ // { role: 'user', content: 'Hello!' },
39
+ // { role: 'assistant', content: 'Hi there!' },
40
+ // { role: 'user', content: 'Goodbye!' },
41
+ // ]);
42
+ // });
43
+ //
44
+ // it('should set error message correctly', () => {
45
+ // const action: MessageDispatch = {
46
+ // type: 'setErrorMessage',
47
+ // error: { message: 'Not Found', status: 404, type: 'chatbot' },
48
+ // index: 0,
49
+ // };
50
+ // const newState = messagesReducer(initialState, action);
51
+ // expect(newState).toEqual([
52
+ // {
53
+ // role: 'user',
54
+ // content: 'Hello!',
55
+ // error: { message: 'Not Found', status: 404, type: 'chatbot' },
56
+ // },
57
+ // { role: 'assistant', content: 'Hi there!' },
58
+ // ]);
59
+ // });
60
+ //
61
+ // it('should update the latest bot message', () => {
62
+ // const responseStream = ['I', ' am', ' a', ' bot.'];
63
+ // const action: MessageDispatch = { type: 'updateLatestBotMessage', responseStream };
64
+ // const newState = messagesReducer(initialState, action);
65
+ // expect(newState).toEqual([
66
+ // { role: 'user', content: 'Hello!' },
67
+ // { role: 'assistant', content: 'I am a bot.' },
68
+ // ]);
69
+ // });
70
+ // });
@@ -0,0 +1,84 @@
1
+ import { produce } from 'immer';
2
+
3
+ import { ChatMessage, ChatMessageMap } from '@/types/chatMessage';
4
+ import { LLMRoleType } from '@/types/llm';
5
+ import { MetaData } from '@/types/meta';
6
+ import { nanoid } from '@/utils/uuid';
7
+
8
+ interface AddMessage {
9
+ id?: string;
10
+ message: string;
11
+ meta?: MetaData;
12
+ parentId?: string;
13
+ quotaId?: string;
14
+ role: LLMRoleType;
15
+ type: 'addMessage';
16
+ }
17
+
18
+ interface DeleteMessage {
19
+ id: string;
20
+ type: 'deleteMessage';
21
+ }
22
+
23
+ interface ResetMessages {
24
+ type: 'resetMessages';
25
+ }
26
+
27
+ interface UpdateMessage {
28
+ id: string;
29
+ key: keyof ChatMessage;
30
+ type: 'updateMessage';
31
+ value: ChatMessage[keyof ChatMessage];
32
+ }
33
+
34
+ export type MessageDispatch = AddMessage | DeleteMessage | ResetMessages | UpdateMessage;
35
+
36
+ export const messagesReducer = (
37
+ state: ChatMessageMap,
38
+ payload: MessageDispatch,
39
+ ): ChatMessageMap => {
40
+ switch (payload.type) {
41
+ case 'addMessage': {
42
+ return produce(state, (draftState) => {
43
+ const mid = payload.id || nanoid();
44
+
45
+ draftState[mid] = {
46
+ content: payload.message,
47
+ createAt: Date.now(),
48
+ id: mid,
49
+ meta: payload.meta || {},
50
+ parentId: payload.parentId,
51
+ quotaId: payload.quotaId,
52
+ role: payload.role,
53
+ updateAt: Date.now(),
54
+ };
55
+ });
56
+ }
57
+
58
+ case 'deleteMessage': {
59
+ return produce(state, (draftState) => {
60
+ delete draftState[payload.id];
61
+ });
62
+ }
63
+
64
+ case 'updateMessage': {
65
+ return produce(state, (draftState) => {
66
+ const { id, key, value } = payload;
67
+ const message = draftState[id];
68
+ if (!message) return;
69
+
70
+ // @ts-ignore
71
+ message[key] = value;
72
+ message.updateAt = Date.now();
73
+ });
74
+ }
75
+
76
+ case 'resetMessages': {
77
+ return {};
78
+ }
79
+
80
+ default: {
81
+ throw new Error('暂未实现的 type,请检查 reducer');
82
+ }
83
+ }
84
+ };
@@ -0,0 +1,83 @@
1
+ import { encode } from 'gpt-tokenizer';
2
+
3
+ import { agentSelectors } from '@/store/session';
4
+ import { ChatMessage } from '@/types/chatMessage';
5
+
6
+ import type { SessionStore } from '../../store';
7
+ import { sessionSelectors } from '../session';
8
+
9
+ // 展示在聊天框中的消息
10
+ const currentChats = (s: SessionStore): ChatMessage[] => {
11
+ const session = sessionSelectors.currentSession(s);
12
+ if (!session) return [];
13
+
14
+ const basic = Object.values<ChatMessage>(session.chats)
15
+ // 首先按照时间顺序排序,越早的在越前面
16
+ .sort((pre, next) => pre.createAt - next.createAt)
17
+ // 过滤掉已归档的消息,归档消息不应该出现在聊天框中
18
+ .filter((m) => !m.archive)
19
+ // 映射头像关系
20
+ .map((m) => {
21
+ return {
22
+ ...m,
23
+ meta:
24
+ m.role === 'assistant'
25
+ ? {
26
+ avatar: agentSelectors.currentAgentAvatar(s),
27
+ title: session.meta.title,
28
+ }
29
+ : m.meta,
30
+ };
31
+ });
32
+
33
+ const finalList: ChatMessage[] = [];
34
+
35
+ const addItem = (item: ChatMessage) => {
36
+ const isExist = finalList.findIndex((i) => item.id === i.id) > -1;
37
+ if (!isExist) {
38
+ finalList.push(item);
39
+ }
40
+ };
41
+
42
+ for (const item of basic) {
43
+ // 先判存在与否,不存在就加入
44
+ addItem(item);
45
+
46
+ for (const another of basic) {
47
+ if (another.parentId === item.id) {
48
+ addItem(another);
49
+ }
50
+ }
51
+ }
52
+
53
+ return finalList;
54
+ };
55
+
56
+ const systemRoleSel = (s: SessionStore): string => {
57
+ const config = agentSelectors.currentAgentConfigSafe(s);
58
+
59
+ return config.systemRole;
60
+ };
61
+
62
+ const totalTokens = (s: SessionStore): number[] => {
63
+ const chats = currentChats(s);
64
+ return encode(chats.map((m) => m.content).join(''));
65
+ };
66
+
67
+ const systemRoleTokens = (s: SessionStore): number[] => {
68
+ const systemRole = systemRoleSel(s);
69
+
70
+ return encode(systemRole || '');
71
+ };
72
+
73
+ const totalTokenCount = (s: SessionStore) => totalTokens(s).length;
74
+
75
+ const systemRoleTokenCount = (s: SessionStore) => systemRoleTokens(s).length;
76
+
77
+ export const chatSelectors = {
78
+ currentChats,
79
+ systemRoleTokenCount,
80
+ systemRoleTokens,
81
+ totalTokenCount,
82
+ totalTokens,
83
+ };
@@ -0,0 +1,118 @@
1
+ import Router from 'next/router';
2
+ import { StateCreator } from 'zustand/vanilla';
3
+
4
+ import { SessionStore } from '@/store/session';
5
+ import { LanguageModel } from '@/types/llm';
6
+ import { LobeAgentSession, LobeSessionType } from '@/types/session';
7
+ import { uuid } from '@/utils/uuid';
8
+
9
+ import { SessionDispatch, sessionsReducer } from './reducers/session';
10
+
11
+ export interface SessionAction {
12
+ activeSession: (sessionId: string) => void;
13
+ /**
14
+ * @title 添加会话
15
+ * @param session - 会话信息
16
+ * @returns void
17
+ */
18
+ createSession: () => Promise<string>;
19
+
20
+ /**
21
+ * 分发聊天记录
22
+ * @param payload - 聊天记录
23
+ */
24
+ dispatchSession: (payload: SessionDispatch) => void;
25
+ /**
26
+ * @title 删除会话
27
+ * @param index - 会话索引
28
+ * @returns void
29
+ */
30
+ removeSession: (sessionId: string) => void;
31
+
32
+ /**
33
+ * @title 切换会话
34
+ * @param sessionId - 会话索引
35
+ * @returns void
36
+ */
37
+ switchSession: (sessionId?: string | 'new') => Promise<void>;
38
+
39
+ /**
40
+ * 生成压缩后的消息
41
+ * @returns 压缩后的消息
42
+ */
43
+ // genShareUrl: () => string;
44
+ }
45
+
46
+ export const createSessionSlice: StateCreator<
47
+ SessionStore,
48
+ [['zustand/devtools', never]],
49
+ [],
50
+ SessionAction
51
+ > = (set, get) => ({
52
+ activeSession: (sessionId) => {
53
+ set({ activeId: sessionId });
54
+ },
55
+
56
+ createSession: async () => {
57
+ const { dispatchSession, switchSession } = get();
58
+
59
+ const timestamp = Date.now();
60
+
61
+ const newSession: LobeAgentSession = {
62
+ chats: {},
63
+ config: {
64
+ model: LanguageModel.GPT3_5,
65
+ params: {
66
+ temperature: 0.6,
67
+ },
68
+ systemRole: '',
69
+ },
70
+ createAt: timestamp,
71
+ id: uuid(),
72
+ meta: {},
73
+ type: LobeSessionType.Agent,
74
+ updateAt: timestamp,
75
+ };
76
+
77
+ dispatchSession({ session: newSession, type: 'addSession' });
78
+
79
+ await switchSession(newSession.id);
80
+
81
+ return newSession.id;
82
+ },
83
+
84
+ dispatchSession: (payload) => {
85
+ const { type, ...res } = payload;
86
+ set({ sessions: sessionsReducer(get().sessions, payload) }, false, {
87
+ payload: res,
88
+ type: `dispatchChat/${type}`,
89
+ });
90
+ },
91
+
92
+ removeSession: (sessionId) => {
93
+ get().dispatchSession({ id: sessionId, type: 'removeSession' });
94
+
95
+ if (sessionId === get().activeId) {
96
+ Router.push('/');
97
+ }
98
+ },
99
+
100
+ switchSession: async (sessionId) => {
101
+ if (get().activeId === sessionId) return;
102
+
103
+ if (sessionId) {
104
+ get().activeSession(sessionId);
105
+ }
106
+
107
+ // 新会话
108
+ await Router.push(`/chat/${sessionId}`);
109
+ },
110
+
111
+ // genShareUrl: () => {
112
+ // const session = sessionSelectors.currentSession(get());
113
+ // if (!session) return '';
114
+ //
115
+ // const agent = session.config;
116
+ // return genShareMessagesUrl(session.chats, agent.systemRole);
117
+ // },
118
+ });
@@ -0,0 +1,3 @@
1
+ export * from './action';
2
+ export * from './initialState';
3
+ export * from './selectors';
@@ -0,0 +1,31 @@
1
+ import { LobeAgentSession, LobeSessionType } from '@/types/session';
2
+
3
+ import { initialLobeAgentConfig } from '../agentConfig';
4
+
5
+ export interface SessionState {
6
+ /**
7
+ * @title 当前活动的会话
8
+ * @description 当前正在编辑或查看的会话
9
+ * @default null
10
+ */
11
+ activeId: string | null;
12
+ searchKeywords: string;
13
+ sessions: Record<string, LobeAgentSession>;
14
+ }
15
+
16
+ export const initLobeSession: LobeAgentSession = {
17
+ chats: {},
18
+ config: initialLobeAgentConfig,
19
+ createAt: Date.now(),
20
+ id: '',
21
+ meta: {},
22
+ type: LobeSessionType.Agent,
23
+ updateAt: Date.now(),
24
+ };
25
+
26
+ export const initialSessionState: SessionState = {
27
+ activeId: null,
28
+
29
+ searchKeywords: '',
30
+ sessions: {},
31
+ };