@lobehub/chat 0.82.7 → 0.82.8
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 +25 -0
- package/next.config.mjs +6 -1
- package/package.json +5 -1
- package/src/app/chat/features/Conversation/Input/ActionBar/Token.tsx +10 -7
- package/src/app/chat/features/Conversation/Input/index.tsx +7 -3
- package/src/app/home/Redirect.tsx +29 -0
- package/src/app/home/index.tsx +18 -29
- package/src/app/market/features/SideBar/AgentInfo/TokenTag.tsx +4 -3
- package/src/app/page.tsx +7 -3
- package/src/app/welcome/{layout.desktop.tsx → DesktopLayout/Desktop.tsx} +5 -4
- package/src/app/welcome/DesktopLayout/index.tsx +15 -0
- package/src/app/welcome/{layout.mobile.tsx → MobileLayout.tsx} +6 -3
- package/src/app/welcome/features/Banner/index.tsx +2 -0
- package/src/app/welcome/features/Footer/index.tsx +2 -0
- package/src/app/welcome/layout.tsx +22 -0
- package/src/app/welcome/page.tsx +13 -4
- package/src/features/AgentSetting/AgentPrompt/index.tsx +3 -6
- package/src/hooks/useTokenCount.ts +20 -0
- package/src/prompts/chat.ts +4 -2
- package/src/store/session/hooks/useEffectAfterHydrated.ts +11 -6
- package/src/store/session/slices/chat/actions/message.ts +1 -1
- package/src/store/session/slices/chat/actions/topic.ts +3 -3
- package/src/store/session/slices/chat/helpers.ts +3 -4
- package/src/store/session/slices/chat/selectors/chat.ts +5 -0
- package/src/store/session/slices/chat/selectors/index.ts +9 -5
- package/src/store/session/slices/session/action.ts +1 -1
- package/src/utils/responsive.ts +18 -0
- package/src/utils/tokenizer.ts +5 -0
- package/src/app/home/Loading.tsx +0 -23
- package/src/app/welcome/index.tsx +0 -39
- package/src/store/session/slices/chat/selectors/token.ts +0 -28
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
### [Version 0.82.8](https://github.com/lobehub/lobe-chat/compare/v0.82.7...v0.82.8)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2023-09-30**</sup>
|
|
8
|
+
|
|
9
|
+
#### ♻ Code Refactoring
|
|
10
|
+
|
|
11
|
+
- **misc**: Refactor / route to reduce page js size.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### Code refactoring
|
|
19
|
+
|
|
20
|
+
- **misc**: Refactor / route to reduce page js size ([79f0347](https://github.com/lobehub/lobe-chat/commit/79f0347))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
5
30
|
### [Version 0.82.7](https://github.com/lobehub/lobe-chat/compare/v0.82.6...v0.82.7)
|
|
6
31
|
|
|
7
32
|
<sup>Released on **2023-09-30**</sup>
|
package/next.config.mjs
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
import analyzer from '@next/bundle-analyzer';
|
|
1
2
|
import nextPWA from 'next-pwa';
|
|
2
3
|
|
|
3
4
|
const isProd = process.env.NODE_ENV === 'production';
|
|
4
5
|
|
|
6
|
+
const withBundleAnalyzer = analyzer({
|
|
7
|
+
enabled: process.env.ANALYZE === 'true',
|
|
8
|
+
});
|
|
9
|
+
|
|
5
10
|
const withPWA = nextPWA({
|
|
6
11
|
dest: 'public',
|
|
7
12
|
register: true,
|
|
@@ -29,4 +34,4 @@ const nextConfig = {
|
|
|
29
34
|
},
|
|
30
35
|
};
|
|
31
36
|
|
|
32
|
-
export default isProd ? withPWA(nextConfig) : nextConfig;
|
|
37
|
+
export default isProd ? withBundleAnalyzer(withPWA(nextConfig)) : nextConfig;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "0.82.
|
|
3
|
+
"version": "0.82.8",
|
|
4
4
|
"description": "An open-source, extensible (Function Calling), high-performance chatbot framework. It supports one-click free deployment of your private ChatGPT/LLM web application",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"chatbot",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"sideEffects": false,
|
|
25
25
|
"scripts": {
|
|
26
26
|
"build": "next build",
|
|
27
|
+
"build:analyze": "ANALYZE=true next build",
|
|
27
28
|
"dev": "next dev -p 3010",
|
|
28
29
|
"i18n": "npm run i18n:workflow && lobe-i18n",
|
|
29
30
|
"i18n:workflow": "ts-node --project ./tsconfig.json scripts/i18nWorkflow/index.ts",
|
|
@@ -106,6 +107,7 @@
|
|
|
106
107
|
"swr": "^2",
|
|
107
108
|
"systemjs": "^6",
|
|
108
109
|
"ts-md5": "^1",
|
|
110
|
+
"ua-parser-js": "^1",
|
|
109
111
|
"url-join": "^5",
|
|
110
112
|
"use-merge-value": "^1",
|
|
111
113
|
"utility-types": "^3",
|
|
@@ -117,6 +119,7 @@
|
|
|
117
119
|
"@commitlint/cli": "^17",
|
|
118
120
|
"@lobehub/i18n-cli": "latest",
|
|
119
121
|
"@lobehub/lint": "latest",
|
|
122
|
+
"@next/bundle-analyzer": "^13",
|
|
120
123
|
"@next/eslint-plugin-next": "^13",
|
|
121
124
|
"@testing-library/jest-dom": "^6",
|
|
122
125
|
"@testing-library/react": "^14",
|
|
@@ -127,6 +130,7 @@
|
|
|
127
130
|
"@types/react": "^18",
|
|
128
131
|
"@types/react-dom": "^18",
|
|
129
132
|
"@types/systemjs": "^6",
|
|
133
|
+
"@types/ua-parser-js": "^0.7",
|
|
130
134
|
"@types/uuid": "^9",
|
|
131
135
|
"@umijs/lint": "^4",
|
|
132
136
|
"@vitest/coverage-v8": "latest",
|
|
@@ -1,24 +1,27 @@
|
|
|
1
1
|
import { TokenTag, Tooltip } from '@lobehub/ui';
|
|
2
|
-
import {
|
|
3
|
-
import { memo, useMemo } from 'react';
|
|
2
|
+
import { memo } from 'react';
|
|
4
3
|
import { useTranslation } from 'react-i18next';
|
|
5
4
|
|
|
6
5
|
import { ModelTokens } from '@/const/modelTokens';
|
|
6
|
+
import { useTokenCount } from '@/hooks/useTokenCount';
|
|
7
7
|
import { useSessionStore } from '@/store/session';
|
|
8
8
|
import { agentSelectors, chatSelectors } from '@/store/session/selectors';
|
|
9
9
|
|
|
10
10
|
const Token = memo<{ input: string }>(({ input }) => {
|
|
11
11
|
const { t } = useTranslation('common');
|
|
12
12
|
|
|
13
|
-
const inputTokenCount =
|
|
13
|
+
const inputTokenCount = useTokenCount(input);
|
|
14
14
|
|
|
15
|
-
const [
|
|
16
|
-
chatSelectors.
|
|
17
|
-
|
|
18
|
-
chatSelectors.chatsTokenCount(s),
|
|
15
|
+
const [messageString, systemRole, model] = useSessionStore((s) => [
|
|
16
|
+
chatSelectors.chatsMessageString(s),
|
|
17
|
+
agentSelectors.currentAgentSystemRole(s),
|
|
19
18
|
agentSelectors.currentAgentModel(s),
|
|
20
19
|
]);
|
|
21
20
|
|
|
21
|
+
const systemRoleToken = useTokenCount(systemRole);
|
|
22
|
+
const chatsToken = useTokenCount(messageString);
|
|
23
|
+
|
|
24
|
+
const totalToken = systemRoleToken + chatsToken;
|
|
22
25
|
return (
|
|
23
26
|
<Tooltip placement={'bottom'} title={t('tokenDetail', { chatsToken, systemRoleToken })}>
|
|
24
27
|
<TokenTag
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ChatInputArea } from '@lobehub/ui';
|
|
2
2
|
import { useResponsive } from 'antd-style';
|
|
3
|
-
import
|
|
3
|
+
import dynamic from 'next/dynamic';
|
|
4
|
+
import { Suspense, memo, useState } from 'react';
|
|
4
5
|
import { useTranslation } from 'react-i18next';
|
|
5
6
|
|
|
6
7
|
import { CHAT_TEXTAREA_HEIGHT } from '@/const/layoutTokens';
|
|
@@ -8,11 +9,12 @@ import { useSessionStore } from '@/store/session';
|
|
|
8
9
|
|
|
9
10
|
import ActionLeft from './ActionBar/ActionLeft';
|
|
10
11
|
import ActionsRight from './ActionBar/ActionRight';
|
|
11
|
-
import Token from './ActionBar/Token';
|
|
12
12
|
import Desktop from './Desktop';
|
|
13
13
|
import Footer from './Footer';
|
|
14
14
|
import Mobile from './Mobile';
|
|
15
15
|
|
|
16
|
+
const Token = dynamic(() => import('./ActionBar/Token'), { ssr: false });
|
|
17
|
+
|
|
16
18
|
const ChatInput = () => {
|
|
17
19
|
const { t } = useTranslation('common');
|
|
18
20
|
const [expand, setExpand] = useState<boolean>(false);
|
|
@@ -33,7 +35,9 @@ const ChatInput = () => {
|
|
|
33
35
|
actions={
|
|
34
36
|
<>
|
|
35
37
|
<ActionLeft />
|
|
36
|
-
<
|
|
38
|
+
<Suspense>
|
|
39
|
+
<Token input={message} />
|
|
40
|
+
</Suspense>
|
|
37
41
|
</>
|
|
38
42
|
}
|
|
39
43
|
actionsRight={<ActionsRight />}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useRouter } from 'next/navigation';
|
|
4
|
+
import { memo, useEffect } from 'react';
|
|
5
|
+
|
|
6
|
+
import { useSessionStore } from '@/store/session';
|
|
7
|
+
import { sessionSelectors } from '@/store/session/selectors';
|
|
8
|
+
|
|
9
|
+
const Redirect = memo(() => {
|
|
10
|
+
const router = useRouter();
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
useSessionStore.persist.onFinishHydration(() => {
|
|
14
|
+
const store = useSessionStore.getState();
|
|
15
|
+
const hasSession = sessionSelectors.hasSessionList(store);
|
|
16
|
+
|
|
17
|
+
if (hasSession) {
|
|
18
|
+
router.push('/chat');
|
|
19
|
+
store.switchSession();
|
|
20
|
+
} else {
|
|
21
|
+
router.push('/welcome');
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}, []);
|
|
25
|
+
|
|
26
|
+
return null;
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export default Redirect;
|
package/src/app/home/index.tsx
CHANGED
|
@@ -1,31 +1,20 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
useEffectAfterSessionHydrated(() => {
|
|
23
|
-
if (hasSession) switchSession();
|
|
24
|
-
}, [hasSession]);
|
|
25
|
-
|
|
26
|
-
if (!hydrated) return <Loading />;
|
|
27
|
-
|
|
28
|
-
return !hasSession ? <Welcome /> : <Loading />;
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
export default Home;
|
|
3
|
+
import { Icon, Logo } from '@lobehub/ui';
|
|
4
|
+
import { t } from 'i18next';
|
|
5
|
+
import { Loader2 } from 'lucide-react';
|
|
6
|
+
import { Center, Flexbox } from 'react-layout-kit';
|
|
7
|
+
|
|
8
|
+
const Loading = () => (
|
|
9
|
+
<Flexbox height={'100vh'} width={'100%'}>
|
|
10
|
+
<Center flex={1} gap={12} width={'100%'}>
|
|
11
|
+
<Logo extra={'Chat'} size={48} type={'combine'} />
|
|
12
|
+
<Center gap={16} horizontal>
|
|
13
|
+
<Icon icon={Loader2} spin />
|
|
14
|
+
{t('appInitializing', { ns: 'common' })}
|
|
15
|
+
</Center>
|
|
16
|
+
</Center>
|
|
17
|
+
</Flexbox>
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
export default Loading;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createStyles } from 'antd-style';
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import { memo } from 'react';
|
|
3
|
+
|
|
4
|
+
import { useTokenCount } from '@/hooks/useTokenCount';
|
|
4
5
|
|
|
5
6
|
const useStyles = createStyles(
|
|
6
7
|
({ css, token }) => css`
|
|
@@ -17,7 +18,7 @@ const useStyles = createStyles(
|
|
|
17
18
|
|
|
18
19
|
const TokenTag = memo<{ systemRole: string }>(({ systemRole }) => {
|
|
19
20
|
const { styles } = useStyles();
|
|
20
|
-
const value =
|
|
21
|
+
const value = useTokenCount(systemRole);
|
|
21
22
|
|
|
22
23
|
return <div className={styles}>{value}</div>;
|
|
23
24
|
});
|
package/src/app/page.tsx
CHANGED
|
@@ -3,10 +3,11 @@ import Link from 'next/link';
|
|
|
3
3
|
import { PropsWithChildren, memo } from 'react';
|
|
4
4
|
import { Center, Flexbox } from 'react-layout-kit';
|
|
5
5
|
|
|
6
|
-
import AppLayout from '
|
|
7
|
-
import { useStyles } from './features/Banner/style';
|
|
6
|
+
import AppLayout from '@/layout/AppLayout';
|
|
8
7
|
|
|
9
|
-
|
|
8
|
+
import { useStyles } from '../features/Banner/style';
|
|
9
|
+
|
|
10
|
+
const Desktop = memo<PropsWithChildren>(({ children }) => {
|
|
10
11
|
const { styles } = useStyles();
|
|
11
12
|
return (
|
|
12
13
|
<AppLayout>
|
|
@@ -28,4 +29,4 @@ const WelcomeLayout = memo<PropsWithChildren>(({ children }) => {
|
|
|
28
29
|
);
|
|
29
30
|
});
|
|
30
31
|
|
|
31
|
-
export default
|
|
32
|
+
export default Desktop;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useResponsive } from 'antd-style';
|
|
4
|
+
import { PropsWithChildren, memo } from 'react';
|
|
5
|
+
|
|
6
|
+
import Mobile from '../MobileLayout';
|
|
7
|
+
import Desktop from './Desktop';
|
|
8
|
+
|
|
9
|
+
const ResponsiveLayout = memo<PropsWithChildren>(({ children }) => {
|
|
10
|
+
const { mobile } = useResponsive();
|
|
11
|
+
|
|
12
|
+
return mobile ? <Mobile>{children}</Mobile> : <Desktop>{children}</Desktop>;
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export default ResponsiveLayout;
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
1
3
|
import { Logo, MobileNavBar } from '@lobehub/ui';
|
|
2
4
|
import { useTheme } from 'antd-style';
|
|
3
5
|
import { PropsWithChildren, memo } from 'react';
|
|
4
6
|
|
|
5
|
-
import AppMobileLayout from '
|
|
7
|
+
import AppMobileLayout from '@/layout/AppMobileLayout';
|
|
6
8
|
|
|
7
|
-
const
|
|
9
|
+
const MobileLayout = memo<PropsWithChildren>(({ children }) => {
|
|
8
10
|
const theme = useTheme();
|
|
11
|
+
|
|
9
12
|
return (
|
|
10
13
|
<AppMobileLayout
|
|
11
14
|
navBar={<MobileNavBar center={<Logo type={'text'} />} />}
|
|
@@ -17,4 +20,4 @@ const WelcomeLayout = memo<PropsWithChildren>(({ children }) => {
|
|
|
17
20
|
);
|
|
18
21
|
});
|
|
19
22
|
|
|
20
|
-
export default
|
|
23
|
+
export default MobileLayout;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { PropsWithChildren } from 'react';
|
|
2
|
+
|
|
3
|
+
import { isMobileDevice } from '@/utils/responsive';
|
|
4
|
+
|
|
5
|
+
import Desktop from './DesktopLayout';
|
|
6
|
+
import Mobile from './MobileLayout';
|
|
7
|
+
import Footer from './features/Footer';
|
|
8
|
+
|
|
9
|
+
const Layout = ({ children }: PropsWithChildren) => {
|
|
10
|
+
const mobile = isMobileDevice();
|
|
11
|
+
|
|
12
|
+
return mobile ? (
|
|
13
|
+
<Mobile>{children}</Mobile>
|
|
14
|
+
) : (
|
|
15
|
+
<Desktop>
|
|
16
|
+
{children}
|
|
17
|
+
<Footer />
|
|
18
|
+
</Desktop>
|
|
19
|
+
);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default Layout;
|
package/src/app/welcome/page.tsx
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { t } from 'i18next';
|
|
2
|
+
import { Metadata } from 'next';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
import { genSiteHeadTitle } from '@/utils/genSiteHeadTitle';
|
|
5
|
+
|
|
6
|
+
import Banner from './features/Banner';
|
|
7
|
+
|
|
8
|
+
export const generateMetadata = (): Metadata => ({
|
|
9
|
+
title: genSiteHeadTitle(t('header', { ns: 'welcome' })),
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const Page = () => {
|
|
13
|
+
return <Banner />;
|
|
5
14
|
};
|
|
6
15
|
|
|
7
|
-
export default
|
|
16
|
+
export default Page;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { EditableMessage, FormGroup, TokenTag } from '@lobehub/ui';
|
|
2
2
|
import { Button } from 'antd';
|
|
3
3
|
import { createStyles } from 'antd-style';
|
|
4
|
-
import { encode } from 'gpt-tokenizer';
|
|
5
4
|
import { Bot } from 'lucide-react';
|
|
6
5
|
import { memo, useState } from 'react';
|
|
7
6
|
import { useTranslation } from 'react-i18next';
|
|
@@ -9,6 +8,7 @@ import { Flexbox } from 'react-layout-kit';
|
|
|
9
8
|
|
|
10
9
|
import { FORM_STYLE } from '@/const/layoutTokens';
|
|
11
10
|
import { ModelTokens } from '@/const/modelTokens';
|
|
11
|
+
import { useTokenCount } from '@/hooks/useTokenCount';
|
|
12
12
|
|
|
13
13
|
import { useStore } from '../store';
|
|
14
14
|
|
|
@@ -26,11 +26,8 @@ const AgentPrompt = memo(() => {
|
|
|
26
26
|
const { styles } = useStyles();
|
|
27
27
|
const [editing, setEditing] = useState(false);
|
|
28
28
|
const updateConfig = useStore((s) => s.setAgentConfig);
|
|
29
|
-
const [systemRole, model
|
|
30
|
-
|
|
31
|
-
s.config.model,
|
|
32
|
-
encode(s.config.systemRole || '').length,
|
|
33
|
-
]);
|
|
29
|
+
const [systemRole, model] = useStore((s) => [s.config.systemRole, s.config.model]);
|
|
30
|
+
const systemTokenCount = useTokenCount(systemRole);
|
|
34
31
|
|
|
35
32
|
return (
|
|
36
33
|
<FormGroup
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { startTransition, useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { encodeAsync } from '@/utils/tokenizer';
|
|
4
|
+
|
|
5
|
+
export const useTokenCount = (input: string = '') => {
|
|
6
|
+
const [value, setNum] = useState(0);
|
|
7
|
+
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
startTransition(() => {
|
|
10
|
+
encodeAsync(input)
|
|
11
|
+
.then(setNum)
|
|
12
|
+
.catch(() => {
|
|
13
|
+
// 兜底采用字符数
|
|
14
|
+
setNum(input.length);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
}, [input]);
|
|
18
|
+
|
|
19
|
+
return value;
|
|
20
|
+
};
|
package/src/prompts/chat.ts
CHANGED
|
@@ -2,7 +2,9 @@ import { chatHelpers } from '@/store/session/helpers';
|
|
|
2
2
|
import { LanguageModel } from '@/types/llm';
|
|
3
3
|
import { OpenAIChatMessage, OpenAIStreamPayload } from '@/types/openai';
|
|
4
4
|
|
|
5
|
-
export const promptSummaryTitle = (
|
|
5
|
+
export const promptSummaryTitle = async (
|
|
6
|
+
messages: OpenAIChatMessage[],
|
|
7
|
+
): Promise<Partial<OpenAIStreamPayload>> => {
|
|
6
8
|
const finalMessages: OpenAIChatMessage[] = [
|
|
7
9
|
{
|
|
8
10
|
content:
|
|
@@ -17,7 +19,7 @@ export const promptSummaryTitle = (messages: OpenAIChatMessage[]): Partial<OpenA
|
|
|
17
19
|
},
|
|
18
20
|
];
|
|
19
21
|
// 如果超过 4k,则使用 GPT3.5 16K 模型
|
|
20
|
-
const tokens = chatHelpers.getMessagesTokenCount(finalMessages);
|
|
22
|
+
const tokens = await chatHelpers.getMessagesTokenCount(finalMessages);
|
|
21
23
|
let model: LanguageModel | undefined = undefined;
|
|
22
24
|
if (tokens > 4000) {
|
|
23
25
|
model = LanguageModel.GPT3_5_16K;
|
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
import { useEffect } from 'react';
|
|
2
2
|
|
|
3
|
-
import { useSessionStore } from '../store';
|
|
3
|
+
import { SessionStore, useSessionStore } from '../store';
|
|
4
4
|
|
|
5
5
|
export const useEffectAfterSessionHydrated = (
|
|
6
|
-
fn: (session: typeof useSessionStore) => void,
|
|
6
|
+
fn: (session: typeof useSessionStore, store: SessionStore) => void,
|
|
7
7
|
deps: any[] = [],
|
|
8
8
|
) => {
|
|
9
|
+
// const hasTrigger = useRef(false);
|
|
9
10
|
useEffect(() => {
|
|
10
11
|
const hasRehydrated = useSessionStore.persist.hasHydrated();
|
|
11
12
|
|
|
12
13
|
if (hasRehydrated) {
|
|
13
|
-
//
|
|
14
|
-
fn(useSessionStore);
|
|
14
|
+
// equal useEffect triggered multi-time
|
|
15
|
+
fn(useSessionStore, useSessionStore.getState());
|
|
15
16
|
} else {
|
|
16
|
-
//
|
|
17
|
+
// keep onFinishHydration just are triggered only once
|
|
18
|
+
// if (hasTrigger.current) return;
|
|
19
|
+
//
|
|
20
|
+
// hasTrigger.current = true;
|
|
21
|
+
// equal useEffect first trigger
|
|
17
22
|
useSessionStore.persist.onFinishHydration(() => {
|
|
18
|
-
fn(useSessionStore);
|
|
23
|
+
fn(useSessionStore, useSessionStore.getState());
|
|
19
24
|
});
|
|
20
25
|
}
|
|
21
26
|
}, deps);
|
|
@@ -26,7 +26,7 @@ export interface ChatTopicAction {
|
|
|
26
26
|
/**
|
|
27
27
|
* 将当前消息保存为主题
|
|
28
28
|
*/
|
|
29
|
-
saveToTopic: () => string | undefined
|
|
29
|
+
saveToTopic: () => Promise<string | undefined>;
|
|
30
30
|
/**
|
|
31
31
|
* 切换主题
|
|
32
32
|
* @param id - 要切换的主题的 ID
|
|
@@ -72,7 +72,7 @@ export const chatTopic: StateCreator<
|
|
|
72
72
|
// 切换到默认 topic
|
|
73
73
|
toggleTopic();
|
|
74
74
|
},
|
|
75
|
-
saveToTopic: () => {
|
|
75
|
+
saveToTopic: async () => {
|
|
76
76
|
const session = sessionSelectors.currentSession(get());
|
|
77
77
|
if (!session) return;
|
|
78
78
|
|
|
@@ -116,7 +116,7 @@ export const chatTopic: StateCreator<
|
|
|
116
116
|
output += x;
|
|
117
117
|
dispatchTopic({ id: topicId, key: 'title', type: 'updateChatTopic', value: output });
|
|
118
118
|
},
|
|
119
|
-
params: promptSummaryTitle(messages),
|
|
119
|
+
params: await promptSummaryTitle(messages),
|
|
120
120
|
});
|
|
121
121
|
|
|
122
122
|
return topicId;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { encode } from 'gpt-tokenizer';
|
|
2
|
-
|
|
3
1
|
import { OpenAIChatMessage } from '@/types/openai';
|
|
2
|
+
import { encodeAsync } from '@/utils/tokenizer';
|
|
4
3
|
|
|
5
|
-
export const getMessagesTokenCount = (messages: OpenAIChatMessage[]) =>
|
|
6
|
-
|
|
4
|
+
export const getMessagesTokenCount = async (messages: OpenAIChatMessage[]) =>
|
|
5
|
+
encodeAsync(messages.map((m) => m.content).join(''));
|
|
7
6
|
|
|
8
7
|
export const chatHelpers = {
|
|
9
8
|
getMessagesTokenCount,
|
|
@@ -79,3 +79,8 @@ export const currentChatsWithHistoryConfig = (s: SessionStore): ChatMessage[] =>
|
|
|
79
79
|
|
|
80
80
|
return getSlicedMessagesWithConfig(chats, config);
|
|
81
81
|
};
|
|
82
|
+
|
|
83
|
+
export const chatsMessageString = (s: SessionStore): string => {
|
|
84
|
+
const chats = currentChatsWithHistoryConfig(s);
|
|
85
|
+
return chats.map((m) => m.content).join('');
|
|
86
|
+
};
|
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
chatsMessageString,
|
|
3
|
+
currentChats,
|
|
4
|
+
currentChatsWithGuideMessage,
|
|
5
|
+
currentChatsWithHistoryConfig,
|
|
6
|
+
getChatsById,
|
|
7
|
+
} from './chat';
|
|
3
8
|
import { currentTopics, getTopicMessages } from './topic';
|
|
4
9
|
|
|
5
10
|
export const chatSelectors = {
|
|
6
|
-
|
|
11
|
+
chatsMessageString,
|
|
7
12
|
currentChats,
|
|
8
13
|
currentChatsWithGuideMessage,
|
|
14
|
+
currentChatsWithHistoryConfig,
|
|
9
15
|
getChatsById,
|
|
10
|
-
systemRoleTokenCount,
|
|
11
|
-
totalTokenCount,
|
|
12
16
|
};
|
|
13
17
|
|
|
14
18
|
export const topicSelectors = {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { headers } from 'next/headers';
|
|
2
|
+
import { UAParser } from 'ua-parser-js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* check mobile device in server
|
|
6
|
+
*/
|
|
7
|
+
export const isMobileDevice = () => {
|
|
8
|
+
if (typeof process === 'undefined') {
|
|
9
|
+
throw new Error('[Server method] you are importing a server-only module outside of server');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { get } = headers();
|
|
13
|
+
const ua = get('user-agent');
|
|
14
|
+
|
|
15
|
+
const device = new UAParser(ua || '').getDevice();
|
|
16
|
+
|
|
17
|
+
return device.type === 'mobile';
|
|
18
|
+
};
|
package/src/app/home/Loading.tsx
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { Icon, Logo } from '@lobehub/ui';
|
|
2
|
-
import { Loader2 } from 'lucide-react';
|
|
3
|
-
import { memo } from 'react';
|
|
4
|
-
import { useTranslation } from 'react-i18next';
|
|
5
|
-
import { Center, Flexbox } from 'react-layout-kit';
|
|
6
|
-
|
|
7
|
-
const Loading = memo(() => {
|
|
8
|
-
const { t } = useTranslation('common');
|
|
9
|
-
|
|
10
|
-
return (
|
|
11
|
-
<Flexbox height={'100vh'} width={'100%'}>
|
|
12
|
-
<Center flex={1} gap={12} width={'100%'}>
|
|
13
|
-
<Logo extra={'Chat'} size={48} type={'combine'} />
|
|
14
|
-
<Center gap={16} horizontal>
|
|
15
|
-
<Icon icon={Loader2} spin />
|
|
16
|
-
{t('appInitializing')}
|
|
17
|
-
</Center>
|
|
18
|
-
</Center>
|
|
19
|
-
</Flexbox>
|
|
20
|
-
);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
export default Loading;
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useResponsive } from 'antd-style';
|
|
4
|
-
import Head from 'next/head';
|
|
5
|
-
import { memo } from 'react';
|
|
6
|
-
import { useTranslation } from 'react-i18next';
|
|
7
|
-
|
|
8
|
-
import { genSiteHeadTitle } from '@/utils/genSiteHeadTitle';
|
|
9
|
-
|
|
10
|
-
import Banner from './features/Banner';
|
|
11
|
-
import Footer from './features/Footer';
|
|
12
|
-
import Layout from './layout.desktop';
|
|
13
|
-
import Mobile from './layout.mobile';
|
|
14
|
-
|
|
15
|
-
const Welcome = memo(() => {
|
|
16
|
-
const { mobile } = useResponsive();
|
|
17
|
-
const { t } = useTranslation('welcome');
|
|
18
|
-
const pageTitle = genSiteHeadTitle(t('header'));
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<>
|
|
22
|
-
<Head>
|
|
23
|
-
<title>{pageTitle}</title>
|
|
24
|
-
</Head>
|
|
25
|
-
{mobile ? (
|
|
26
|
-
<Mobile>
|
|
27
|
-
<Banner />
|
|
28
|
-
</Mobile>
|
|
29
|
-
) : (
|
|
30
|
-
<Layout>
|
|
31
|
-
<Banner />
|
|
32
|
-
<Footer />
|
|
33
|
-
</Layout>
|
|
34
|
-
)}
|
|
35
|
-
</>
|
|
36
|
-
);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
export default Welcome;
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { encode } from 'gpt-tokenizer';
|
|
2
|
-
|
|
3
|
-
import { agentSelectors } from '@/store/session/slices/agentConfig';
|
|
4
|
-
|
|
5
|
-
import type { SessionStore } from '../../../store';
|
|
6
|
-
import { currentChatsWithHistoryConfig } from './chat';
|
|
7
|
-
|
|
8
|
-
// TODO: We should find a smaller package to do this. gpt-tokenizer has 2M Size
|
|
9
|
-
const count = (text: string) => encode(text).length;
|
|
10
|
-
|
|
11
|
-
export const systemRoleSel = (s: SessionStore): string => {
|
|
12
|
-
const config = agentSelectors.currentAgentConfig(s);
|
|
13
|
-
|
|
14
|
-
return config.systemRole;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export const systemRoleTokenCount = (s: SessionStore): number => {
|
|
18
|
-
const systemRole = systemRoleSel(s);
|
|
19
|
-
|
|
20
|
-
return count(systemRole || '');
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export const chatsTokenCount = (s: SessionStore): number => {
|
|
24
|
-
const chats = currentChatsWithHistoryConfig(s);
|
|
25
|
-
return count(chats.map((m) => m.content).join(''));
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export const totalTokenCount = (s: SessionStore) => chatsTokenCount(s) + systemRoleTokenCount(s);
|