@lobehub/chat 0.162.19 → 0.162.20

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,23 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 0.162.20](https://github.com/lobehub/lobe-chat/compare/v0.162.19...v0.162.20)
6
+
7
+ <sup>Released on **2024-06-08**</sup>
8
+
9
+ <br/>
10
+
11
+ <details>
12
+ <summary><kbd>Improvements and Fixes</kbd></summary>
13
+
14
+ </details>
15
+
16
+ <div align="right">
17
+
18
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
19
+
20
+ </div>
21
+
5
22
  ### [Version 0.162.19](https://github.com/lobehub/lobe-chat/compare/v0.162.18...v0.162.19)
6
23
 
7
24
  <sup>Released on **2024-06-07**</sup>
@@ -35,3 +35,51 @@ You can use the subdomain provided by Zeabur, or choose to bind a custom domain.
35
35
 
36
36
  [deploy-button-image]: https://zeabur.com/button.svg
37
37
  [deploy-link]: https://zeabur.com/templates/VZGGTI
38
+
39
+ # Deploy LobeChat with Zeabur as serverless function
40
+
41
+ >Note: There are still issues with [middlewares and rewrites of next.js on Zeabur](https://github.com/lobehub/lobe-chat/pull/2775?notification_referrer_id=NT_kwDOAdi2DrQxMDkyODQ4MDc2NTozMDk3OTU5OA#issuecomment-2146713899), use at your own risk!
42
+
43
+ Since Zeabur does NOT officially support FREE users deploy containerized service, you may wish to deploy LobeChat as a serverless function service.
44
+ To deploy LobeChat as a serverless function service on Zeabur, you can follow the steps below:
45
+
46
+ ## Zeabur Deployment Process
47
+
48
+ <Steps>
49
+
50
+ ### Fork LobeChat
51
+
52
+ ### Add Zeabur pack config file
53
+
54
+ Add a `zbpack.json` configuration file with the following content to the root dir of your fork:
55
+ ```json
56
+ {
57
+ "serverless": true,
58
+ "ignore_dockerfile": true
59
+ }
60
+ ```
61
+
62
+ ### Prepare your OpenAI API Key
63
+
64
+ Go to [OpenAI API Key](https://platform.openai.com/account/api-keys) to get your OpenAI API Key.
65
+
66
+ ### Login to your [Zeabur dashboard](https://dash.zeabur.com)
67
+
68
+ If you do not already have an account, you will need to register one.
69
+
70
+ ### Create a project and service
71
+
72
+ Create a project, then create a service under this project.
73
+
74
+ ### Link your fork of LobeChat to the just created Zeabur service.
75
+
76
+ When adding service, choose github.
77
+ This may triger a oAuth depend on varies factors like how you login to Zeabur and if you have already authorized Zeabur to access all your repos
78
+
79
+ ### Bind a custom domain (optional)
80
+
81
+ You can create a subdomain provided by Zeabur, or choose to bind a custom domain. Currently, the domains provided by Zeabur have not been contaminated, and most regions can connect directly.
82
+
83
+ ### Zeabur shall start auto build and you should be able to access it by the domain of your choice after a while.
84
+
85
+ </Steps>
@@ -34,3 +34,51 @@ tags:
34
34
 
35
35
  [deploy-button-image]: https://zeabur.com/button.svg
36
36
  [deploy-link]: https://zeabur.com/templates/VZGGTI
37
+
38
+ # 使用 Zeabur 将 LobeChat 部署为无服务器函数
39
+
40
+ >**注意:** 仍然存在关于 [Zeabur 上 next.js 的中间件和重写问题](https://github.com/lobehub/lobe-chat/pull/2775?notification_referrer_id=NT_kwDOAdi2DrQxMDkyODQ4MDc2NTozMDk3OTU5OA#issuecomment-2146713899),请自担风险!
41
+
42
+ 由于 Zeabur 并未官方支持免费用户部署容器化服务,您可能希望将 LobeChat 部署为无服务器函数服务。
43
+ 要在 Zeabur 上将 LobeChat 部署为无服务器函数服务,您可以按照以下步骤操作:
44
+
45
+ ## Zeabur 部署流程
46
+
47
+ <Steps>
48
+
49
+ ### Fork LobeChat
50
+
51
+ ### 添加 Zeabur 打包配置文件
52
+
53
+ 在您的分支的根目录下添加一个 `zbpack.json` 配置文件,内容如下:
54
+ ```json
55
+ {
56
+ "serverless": true,
57
+ "ignore_dockerfile": true
58
+ }
59
+ ```
60
+
61
+ ### 准备您的 OpenAI API 密钥
62
+
63
+ 前往 [OpenAI API 密钥](https://platform.openai.com/account/api-keys) 获取您的 OpenAI API 密钥。
64
+
65
+ ### 登录到您的 [Zeabur 仪表板](https://dash.zeabur.com)
66
+
67
+ 如果您尚未拥有一个账号,您需要注册一个。
68
+
69
+ ### 创建项目与服务。
70
+
71
+ 创建一个项目,并再这个项目下新建一个服务。
72
+
73
+ ### 将您的 LobeChat 分支链接到刚创建的 Zeabur 服务。
74
+
75
+ 在添加服务时,选择 github。
76
+ 这可能会触发一个 oAuth,取决于诸如您如何登录到 Zeabur以及您是否已经授权 Zeabur 访问所有您的存储库等各种因素。
77
+
78
+ ### 绑定自定义域名(可选)
79
+
80
+ 您可以创建 Zeabur 提供的子域名,或选择绑定自定义域名。目前,Zeabur 提供的域名尚未受到污染,大多数地区可以直接连接。
81
+
82
+ ### Zeabur 将开始自动构建,您应该可以在一段时间后通过您选择的域名访问它。
83
+
84
+ </Steps>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.162.19",
3
+ "version": "0.162.20",
4
4
  "description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
5
5
  "keywords": [
6
6
  "framework",
@@ -9,14 +9,23 @@ import { useServerConfigStore } from '@/store/serverConfig';
9
9
  import { useSessionStore } from '@/store/session';
10
10
 
11
11
  import ListItem from '../ListItem';
12
+ import { useSwitchSession } from '../useSwitchSession';
12
13
 
13
14
  const Inbox = memo(() => {
14
15
  const { t } = useTranslation('chat');
15
16
  const mobile = useServerConfigStore((s) => s.isMobile);
16
17
  const activeId = useSessionStore((s) => s.activeId);
18
+ const switchSession = useSwitchSession();
17
19
 
18
20
  return (
19
- <Link aria-label={t('inbox.title')} href={SESSION_CHAT_URL(INBOX_SESSION_ID, mobile)}>
21
+ <Link
22
+ aria-label={t('inbox.title')}
23
+ href={SESSION_CHAT_URL(INBOX_SESSION_ID, mobile)}
24
+ onClick={(e) => {
25
+ e.preventDefault();
26
+ switchSession(INBOX_SESSION_ID);
27
+ }}
28
+ >
20
29
  <ListItem
21
30
  active={activeId === INBOX_SESSION_ID}
22
31
  avatar={DEFAULT_INBOX_AVATAR}
@@ -13,6 +13,7 @@ import { sessionSelectors } from '@/store/session/selectors';
13
13
  import { LobeAgentSession } from '@/types/session';
14
14
 
15
15
  import SkeletonList from '../../SkeletonList';
16
+ import { useSwitchSession } from '../useSwitchSession';
16
17
  import AddButton from './AddButton';
17
18
  import SessionItem from './Item';
18
19
 
@@ -28,19 +29,28 @@ interface SessionListProps {
28
29
  }
29
30
  const SessionList = memo<SessionListProps>(({ dataSource, groupId, showAddButton = true }) => {
30
31
  const { t } = useTranslation('chat');
31
- const isInit = useSessionStore((s) => sessionSelectors.isSessionListInit(s));
32
- const { showCreateSession } = useServerConfigStore(featureFlagsSelectors);
33
-
34
32
  const { styles } = useStyles();
35
33
 
34
+ const isInit = useSessionStore(sessionSelectors.isSessionListInit);
35
+ const { showCreateSession } = useServerConfigStore(featureFlagsSelectors);
36
36
  const mobile = useServerConfigStore((s) => s.isMobile);
37
+
38
+ const switchSession = useSwitchSession();
39
+
37
40
  const isEmpty = !dataSource || dataSource.length === 0;
38
41
  return !isInit ? (
39
42
  <SkeletonList />
40
43
  ) : !isEmpty ? (
41
44
  dataSource.map(({ id }) => (
42
45
  <LazyLoad className={styles} key={id}>
43
- <Link aria-label={id} href={SESSION_CHAT_URL(id, mobile)}>
46
+ <Link
47
+ aria-label={id}
48
+ href={SESSION_CHAT_URL(id, mobile)}
49
+ onClick={(e) => {
50
+ e.preventDefault();
51
+ switchSession(id);
52
+ }}
53
+ >
44
54
  <SessionItem id={id} />
45
55
  </Link>
46
56
  </LazyLoad>
@@ -0,0 +1,26 @@
1
+ import { useCallback } from 'react';
2
+
3
+ import { useQueryRoute } from '@/hooks/useQueryRoute';
4
+ import { useServerConfigStore } from '@/store/serverConfig';
5
+ import { useSessionStore } from '@/store/session';
6
+
7
+ export const useSwitchSession = () => {
8
+ const switchSession = useSessionStore((s) => s.switchSession);
9
+ const mobile = useServerConfigStore((s) => s.isMobile);
10
+ const router = useQueryRoute();
11
+
12
+ return useCallback(
13
+ (id: string) => {
14
+ switchSession(id);
15
+
16
+ if (mobile) {
17
+ setTimeout(() => {
18
+ router.push('/chat', {
19
+ query: { session: id, showMobileWorkspace: 'true' },
20
+ });
21
+ }, 50);
22
+ }
23
+ },
24
+ [mobile],
25
+ );
26
+ };
@@ -141,7 +141,7 @@ describe('SessionAction', () => {
141
141
  const sessionId = 'active-session-id';
142
142
 
143
143
  act(() => {
144
- result.current.activeSession(sessionId);
144
+ result.current.switchSession(sessionId);
145
145
  });
146
146
 
147
147
  expect(result.current.activeId).toBe(sessionId);
@@ -35,10 +35,9 @@ const SEARCH_SESSIONS_KEY = 'searchSessions';
35
35
  /* eslint-disable typescript-sort-keys/interface */
36
36
  export interface SessionAction {
37
37
  /**
38
- * active the session
39
- * @param sessionId
38
+ * switch the session
40
39
  */
41
- activeSession: (sessionId: string) => void;
40
+ switchSession: (sessionId: string) => void;
42
41
  /**
43
42
  * reset sessions to default
44
43
  */
@@ -94,19 +93,13 @@ export const createSessionSlice: StateCreator<
94
93
  [],
95
94
  SessionAction
96
95
  > = (set, get) => ({
97
- activeSession: (sessionId) => {
98
- if (get().activeId === sessionId) return;
99
-
100
- set({ activeId: sessionId }, false, n(`activeSession/${sessionId}`));
101
- },
102
-
103
96
  clearSessions: async () => {
104
97
  await sessionService.removeAllSessions();
105
98
  await get().refreshSessions();
106
99
  },
107
100
 
108
101
  createSession: async (agent, isSwitchSession = true) => {
109
- const { activeSession, refreshSessions } = get();
102
+ const { switchSession, refreshSessions } = get();
110
103
 
111
104
  // merge the defaultAgent in settings
112
105
  const defaultAgent = merge(
@@ -120,12 +113,13 @@ export const createSessionSlice: StateCreator<
120
113
  await refreshSessions();
121
114
 
122
115
  // Whether to goto to the new session after creation, the default is to switch to
123
- if (isSwitchSession) activeSession(id);
116
+ if (isSwitchSession) switchSession(id);
124
117
 
125
118
  return id;
126
119
  },
120
+
127
121
  duplicateSession: async (id) => {
128
- const { activeSession, refreshSessions } = get();
122
+ const { switchSession, refreshSessions } = get();
129
123
  const session = sessionSelectors.getSessionById(id)(get());
130
124
 
131
125
  if (!session) return;
@@ -154,9 +148,8 @@ export const createSessionSlice: StateCreator<
154
148
  message.destroy(messageLoadingKey);
155
149
  message.success(t('duplicateSession.success', { ns: 'chat' }));
156
150
 
157
- activeSession(newId);
151
+ switchSession(newId);
158
152
  },
159
-
160
153
  pinSession: async (id, pinned) => {
161
154
  await get().internal_updateSession(id, { pinned });
162
155
  },
@@ -167,10 +160,16 @@ export const createSessionSlice: StateCreator<
167
160
 
168
161
  // If the active session deleted, switch to the inbox session
169
162
  if (sessionId === get().activeId) {
170
- get().activeSession(INBOX_SESSION_ID);
163
+ get().switchSession(INBOX_SESSION_ID);
171
164
  }
172
165
  },
173
166
 
167
+ switchSession: (sessionId) => {
168
+ if (get().activeId === sessionId) return;
169
+
170
+ set({ activeId: sessionId }, false, n(`activeSession/${sessionId}`));
171
+ },
172
+
174
173
  updateSearchKeywords: (keywords) => {
175
174
  set(
176
175
  { isSearching: !!keywords, sessionSearchKeywords: keywords },