@lobehub/chat 0.31.0 → 0.33.0

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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,56 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 0.33.0](https://github.com/lobehub/lobe-chat/compare/v0.32.0...v0.33.0)
6
+
7
+ <sup>Released on **2023-07-30**</sup>
8
+
9
+ #### ✨ Features
10
+
11
+ - **misc**: 支持输入模板预处理.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's improved
19
+
20
+ - **misc**: 支持输入模板预处理 ([84082c1](https://github.com/lobehub/lobe-chat/commit/84082c1))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
30
+ ## [Version 0.32.0](https://github.com/lobehub/lobe-chat/compare/v0.31.0...v0.32.0)
31
+
32
+ <sup>Released on **2023-07-30**</sup>
33
+
34
+ #### ✨ Features
35
+
36
+ - **misc**: 支持会话置顶.
37
+
38
+ <br/>
39
+
40
+ <details>
41
+ <summary><kbd>Improvements and Fixes</kbd></summary>
42
+
43
+ #### What's improved
44
+
45
+ - **misc**: 支持会话置顶, closes [#32](https://github.com/lobehub/lobe-chat/issues/32) ([fc44b5d](https://github.com/lobehub/lobe-chat/commit/fc44b5d))
46
+
47
+ </details>
48
+
49
+ <div align="right">
50
+
51
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
52
+
53
+ </div>
54
+
5
55
  ## [Version 0.31.0](https://github.com/lobehub/lobe-chat/compare/v0.30.1...v0.31.0)
6
56
 
7
57
  <sup>Released on **2023-07-30**</sup>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.31.0",
3
+ "version": "0.33.0",
4
4
  "description": "Lobe Chat is an open-source chatbot client using LangChain, Typescript and Next.js",
5
5
  "keywords": [
6
6
  "chatbot",
@@ -19,10 +19,9 @@ interface SessionItemProps {
19
19
  active?: boolean;
20
20
  id: string;
21
21
  loading?: boolean;
22
- pin?: boolean;
23
22
  }
24
23
 
25
- const SessionItem: FC<SessionItemProps> = memo(({ id, active = true, loading, pin }) => {
24
+ const SessionItem: FC<SessionItemProps> = memo(({ id, active = true, loading }) => {
26
25
  const ref = useRef(null);
27
26
  const isHovering = useHover(ref);
28
27
 
@@ -33,6 +32,7 @@ const SessionItem: FC<SessionItemProps> = memo(({ id, active = true, loading, pi
33
32
  const [defaultModel] = useSettings((s) => [s.settings.model], shallow);
34
33
 
35
34
  const [
35
+ pin,
36
36
  title,
37
37
  description,
38
38
  systemRole,
@@ -42,11 +42,13 @@ const SessionItem: FC<SessionItemProps> = memo(({ id, active = true, loading, pi
42
42
  model,
43
43
  chatLength,
44
44
  removeSession,
45
+ pinSession,
45
46
  ] = useSessionStore((s) => {
46
47
  const session = sessionSelectors.getSessionById(id)(s);
47
48
  const meta = session.meta;
48
49
  const systemRole = session.config.systemRole;
49
50
  return [
51
+ session.pinned,
50
52
  agentSelectors.getTitle(meta),
51
53
  meta.description,
52
54
  systemRole,
@@ -56,6 +58,7 @@ const SessionItem: FC<SessionItemProps> = memo(({ id, active = true, loading, pi
56
58
  session.config.model,
57
59
  chatSelectors.currentChats(s).length,
58
60
  s.removeSession,
61
+ s.pinSession,
59
62
  ];
60
63
  }, shallow);
61
64
 
@@ -66,8 +69,9 @@ const SessionItem: FC<SessionItemProps> = memo(({ id, active = true, loading, pi
66
69
  icon: <Icon icon={pin ? PinOff : Pin} />,
67
70
  key: 'pin',
68
71
  label: t(pin ? 'pinOff' : 'pin'),
69
- // TODO: 动作绑定
70
- onClick: () => {},
72
+ onClick: () => {
73
+ pinSession(id, !pin);
74
+ },
71
75
  },
72
76
  {
73
77
  children: [
@@ -154,7 +158,16 @@ const SessionItem: FC<SessionItemProps> = memo(({ id, active = true, loading, pi
154
158
  style={{ color: theme.colorText }}
155
159
  title={title}
156
160
  />
157
- <Dropdown arrow={false} menu={{ items }} trigger={['click']}>
161
+ <Dropdown
162
+ arrow={false}
163
+ menu={{
164
+ items,
165
+ onClick: ({ domEvent }) => {
166
+ domEvent.stopPropagation();
167
+ },
168
+ }}
169
+ trigger={['click']}
170
+ >
158
171
  <ActionIcon
159
172
  className="session-remove"
160
173
  icon={MoreVertical}
@@ -1,3 +1,4 @@
1
+ import { template } from 'lodash-es';
1
2
  import { StateCreator } from 'zustand/vanilla';
2
3
 
3
4
  import { LOADING_FLAT } from '@/const/message';
@@ -113,8 +114,41 @@ export const chatMessage: StateCreator<
113
114
  set({ chatLoadingId: assistantId });
114
115
  const config = agentSelectors.currentAgentConfigSafe(get());
115
116
 
117
+ const compiler = template(config.inputTemplate, { interpolate: /{{([\S\s]+?)}}/g });
118
+
119
+ // 对 message 做统一预处理
120
+
121
+ // 1. 替换 inputMessage 模板
122
+ const postMessages = !config.inputTemplate
123
+ ? messages
124
+ : messages.map((m) => {
125
+ if (m.role === 'user') {
126
+ try {
127
+ return { ...m, content: compiler({ text: m.content }) };
128
+ } catch (error) {
129
+ console.error(error);
130
+
131
+ return m;
132
+ }
133
+ }
134
+ return m;
135
+ });
136
+
137
+ // 2. TODO 按参数设定截断长度
138
+
139
+ // 3. 添加 systemRole
140
+ const { systemRole } = agentSelectors.currentAgentConfigSafe(get());
141
+ if (systemRole) {
142
+ postMessages.unshift({ content: systemRole, role: 'system' } as ChatMessage);
143
+ }
144
+
116
145
  const fetcher = () =>
117
- fetchChatModel({ messages, model: config.model, ...config.params, plugins: config.plugins });
146
+ fetchChatModel({
147
+ messages: postMessages,
148
+ model: config.model,
149
+ ...config.params,
150
+ plugins: config.plugins,
151
+ });
118
152
 
119
153
  let output = '';
120
154
  let isFunctionCall = false;
@@ -154,13 +188,9 @@ export const chatMessage: StateCreator<
154
188
  realFetchAIResponse: async (messages, userMessageId) => {
155
189
  const { dispatchMessage, generateMessage, triggerFunctionCall, activeTopicId } = get();
156
190
 
157
- // 添加 systemRole
158
- const { systemRole, model } = agentSelectors.currentAgentConfigSafe(get());
159
- if (systemRole) {
160
- messages.unshift({ content: systemRole, role: 'system' } as ChatMessage);
161
- }
191
+ const { model } = agentSelectors.currentAgentConfigSafe(get());
162
192
 
163
- // 再添加一个空的信息用于放置 ai 响应,注意顺序不能反
193
+ // 添加一个空的信息用于放置 ai 响应,注意顺序不能反
164
194
  // 因为如果顺序反了,messages 中将包含新增的 ai message
165
195
  const mid = nanoid();
166
196
 
@@ -283,10 +313,10 @@ export const chatMessage: StateCreator<
283
313
 
284
314
  const mid = nanoid();
285
315
 
286
- dispatchMessage({ id: mid, message: LOADING_FLAT, role: 'assistant', type: 'addMessage' });
287
-
288
316
  const chats = chatSelectors.currentChats(get());
289
317
 
318
+ dispatchMessage({ id: mid, message: LOADING_FLAT, role: 'assistant', type: 'addMessage' });
319
+
290
320
  await generateMessage(chats, mid);
291
321
  },
292
322
  });
@@ -31,6 +31,11 @@ export interface SessionAction {
31
31
  */
32
32
  importSessions: (sessions: LobeSessions) => void;
33
33
 
34
+ /**
35
+ * 置顶会话
36
+ * @param sessionId
37
+ */
38
+ pinSession: (sessionId: string, pinned?: boolean) => void;
34
39
  /**
35
40
  * 生成压缩后的消息
36
41
  * @returns 压缩后的消息
@@ -42,12 +47,13 @@ export interface SessionAction {
42
47
  * @returns void
43
48
  */
44
49
  removeSession: (sessionId: string) => void;
50
+
45
51
  /**
46
52
  * @title 切换会话
47
53
  * @param sessionId - 会话索引
48
54
  * @returns void
49
55
  */
50
- switchSession: (sessionId?: string | 'new') => Promise<void>;
56
+ switchSession: (sessionId?: string) => Promise<void>;
51
57
  }
52
58
 
53
59
  export const createSessionSlice: StateCreator<
@@ -104,6 +110,19 @@ export const createSessionSlice: StateCreator<
104
110
  }),
105
111
  });
106
112
  },
113
+ // genShareUrl: () => {
114
+ // const session = sessionSelectors.currentSession(get());
115
+ // if (!session) return '';
116
+ //
117
+ // const agent = session.config;
118
+ // return genShareMessagesUrl(session.chats, agent.systemRole);
119
+ // },
120
+ pinSession: (sessionId, pinned) => {
121
+ const nextValue = typeof pinned === 'boolean' ? pinned : !get().sessions[sessionId].pinned;
122
+
123
+ get().dispatchSession({ id: sessionId, pinned: nextValue, type: 'toggleSessionPinned' });
124
+ },
125
+
107
126
  removeSession: (sessionId) => {
108
127
  get().dispatchSession({ id: sessionId, type: 'removeSession' });
109
128
 
@@ -122,11 +141,4 @@ export const createSessionSlice: StateCreator<
122
141
  // 新会话
123
142
  await Router.push(`/chat/${sessionId}`);
124
143
  },
125
- // genShareUrl: () => {
126
- // const session = sessionSelectors.currentSession(get());
127
- // if (!session) return '';
128
- //
129
- // const agent = session.config;
130
- // return genShareMessagesUrl(session.chats, agent.systemRole);
131
- // },
132
144
  });
@@ -63,6 +63,11 @@ interface UpdateSessionAgentConfig {
63
63
  id: string;
64
64
  type: 'updateSessionConfig';
65
65
  }
66
+ interface ToggleSessionPinned {
67
+ id: string;
68
+ pinned: boolean;
69
+ type: 'toggleSessionPinned';
70
+ }
66
71
 
67
72
  export type SessionDispatch =
68
73
  | AddSession
@@ -70,7 +75,8 @@ export type SessionDispatch =
70
75
  | RemoveSession
71
76
  | UpdateSessionMeta
72
77
  | UpdateSessionAgentConfig
73
- | UpdateSessionTopic;
78
+ | UpdateSessionTopic
79
+ | ToggleSessionPinned;
74
80
 
75
81
  export const sessionsReducer = (state: LobeSessions, payload: SessionDispatch): LobeSessions => {
76
82
  switch (payload.type) {
@@ -89,6 +95,16 @@ export const sessionsReducer = (state: LobeSessions, payload: SessionDispatch):
89
95
  });
90
96
  }
91
97
 
98
+ case 'toggleSessionPinned': {
99
+ return produce(state, (draft) => {
100
+ const { pinned, id } = payload;
101
+ const session = draft[id];
102
+ if (!session) return;
103
+
104
+ session.pinned = pinned;
105
+ });
106
+ }
107
+
92
108
  case 'updateSessionMeta': {
93
109
  return produce(state, (draft) => {
94
110
  const chat = draft[payload.id];
@@ -21,8 +21,19 @@ export const sessionList = (s: SessionStore) => {
21
21
  .map((c) => c.content)
22
22
  .join(''),
23
23
  ]);
24
-
25
- return Object.values(filterChats).sort((a, b) => (b.updateAt || 0) - (a.updateAt || 0));
24
+ // 先按照 `pinned` 字段进行排序,置顶的会排在前面。然后,根据 `updateAt` 字段进行倒序排序。
25
+ return Object.values(filterChats).sort((a, b) => {
26
+ if (a.pinned && !b.pinned) {
27
+ return -1;
28
+ }
29
+ if (!a.pinned && b.pinned) {
30
+ return 1;
31
+ }
32
+ if (a.updateAt && b.updateAt) {
33
+ return b.updateAt - a.updateAt;
34
+ }
35
+ return 0;
36
+ });
26
37
  };
27
38
 
28
39
  export const getSessionById =
@@ -19,6 +19,13 @@ interface LobeSessionBase extends BaseDataModel {
19
19
  * 聊天记录
20
20
  */
21
21
  chats: ChatMessageMap;
22
+ /**
23
+ * 置顶
24
+ */
25
+ pinned?: boolean;
26
+ /**
27
+ * 主题
28
+ */
22
29
  topics?: ChatTopicMap;
23
30
  /**
24
31
  * 每个会话的类别