@lobehub/chat 0.151.7 → 0.151.9

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 (40) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/package.json +1 -1
  3. package/src/{layout/DefaultLayout/Desktop/SideBar → app/(main)/@nav/_layout/Desktop}/index.tsx +9 -6
  4. package/src/app/(main)/@nav/_layout/Mobile.tsx +71 -0
  5. package/src/app/(main)/@nav/default.tsx +10 -0
  6. package/src/app/(main)/_layout/Desktop.tsx +33 -0
  7. package/src/app/(main)/_layout/Mobile.tsx +24 -0
  8. package/src/app/(main)/_layout/type.ts +6 -0
  9. package/src/app/(main)/chat/(mobile)/features/ChatInput/index.tsx +1 -1
  10. package/src/app/(main)/chat/(mobile)/index.tsx +4 -3
  11. package/src/app/(main)/chat/(mobile)/mobile/ChatHeader/index.tsx +1 -0
  12. package/src/app/(main)/chat/(mobile)/mobile/page.tsx +6 -9
  13. package/src/app/(main)/chat/settings/(mobile)/index.tsx +4 -3
  14. package/src/app/(main)/error.tsx +5 -0
  15. package/src/app/(main)/layout.tsx +11 -0
  16. package/src/app/(main)/market/_layout/Mobile/index.tsx +6 -8
  17. package/src/app/(main)/market/features/AgentSearchBar/index.tsx +15 -12
  18. package/src/app/(main)/not-found.tsx +3 -0
  19. package/src/app/(main)/settings/_layout/Mobile/index.tsx +3 -3
  20. package/src/app/layout.tsx +1 -4
  21. package/src/app/page.tsx +2 -0
  22. package/src/components/404/index.tsx +4 -2
  23. package/src/components/Error/index.tsx +5 -3
  24. package/src/components/server/MobileNavLayout.tsx +60 -0
  25. package/src/components/server/ServerLayout.tsx +6 -7
  26. package/src/database/client/models/__tests__/sessionGroup.test.ts +18 -0
  27. package/src/features/Conversation/index.tsx +3 -3
  28. package/src/libs/agent-runtime/minimax/index.test.ts +18 -4
  29. package/src/libs/agent-runtime/minimax/index.ts +22 -8
  30. package/src/server/globalConfig/index.ts +2 -0
  31. package/src/styles/mobileHeader.ts +1 -0
  32. package/src/layout/DefaultLayout/Desktop/index.tsx +0 -32
  33. package/src/layout/DefaultLayout/Mobile/index.tsx +0 -59
  34. package/src/layout/DefaultLayout/index.ts +0 -2
  35. package/src/layout/routes/Desktop.tsx +0 -18
  36. package/src/layout/routes/Mobile.tsx +0 -24
  37. package/src/layout/routes/index.tsx +0 -8
  38. /package/src/{layout/DefaultLayout/Desktop/SideBar → app/(main)/@nav/_layout/Desktop}/Avatar.tsx +0 -0
  39. /package/src/{layout/DefaultLayout/Desktop/SideBar → app/(main)/@nav/_layout/Desktop}/BottomActions.tsx +0 -0
  40. /package/src/{layout/DefaultLayout/Desktop/SideBar → app/(main)/@nav/_layout/Desktop}/TopActions.tsx +0 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,56 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 0.151.9](https://github.com/lobehub/lobe-chat/compare/v0.151.8...v0.151.9)
6
+
7
+ <sup>Released on **2024-04-30**</sup>
8
+
9
+ #### 🐛 Bug Fixes
10
+
11
+ - **misc**: Minimax truncationed output.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's fixed
19
+
20
+ - **misc**: Minimax truncationed output, closes [#2308](https://github.com/lobehub/lobe-chat/issues/2308) ([488f319](https://github.com/lobehub/lobe-chat/commit/488f319))
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.151.8](https://github.com/lobehub/lobe-chat/compare/v0.151.7...v0.151.8)
31
+
32
+ <sup>Released on **2024-04-30**</sup>
33
+
34
+ #### ♻ Code Refactoring
35
+
36
+ - **misc**: Move NavBar to `[@nav](https://github.com/nav)` slot route.
37
+
38
+ <br/>
39
+
40
+ <details>
41
+ <summary><kbd>Improvements and Fixes</kbd></summary>
42
+
43
+ #### Code refactoring
44
+
45
+ - **misc**: Move NavBar to `[@nav](https://github.com/nav)` slot route, closes [#2306](https://github.com/lobehub/lobe-chat/issues/2306) ([aee7231](https://github.com/lobehub/lobe-chat/commit/aee7231))
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.151.7](https://github.com/lobehub/lobe-chat/compare/v0.151.6...v0.151.7)
6
56
 
7
57
  <sup>Released on **2024-04-30**</sup>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.151.7",
3
+ "version": "0.151.9",
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",
@@ -1,17 +1,16 @@
1
+ 'use client';
2
+
1
3
  import { SideNav } from '@lobehub/ui';
2
4
  import { memo } from 'react';
3
5
 
4
- import { SidebarTabKey } from '@/store/global/initialState';
6
+ import { useActiveTabKey } from '@/hooks/useActiveTabKey';
5
7
 
6
8
  import Avatar from './Avatar';
7
9
  import BottomActions from './BottomActions';
8
10
  import TopActions from './TopActions';
9
11
 
10
- interface Props {
11
- sidebarKey?: SidebarTabKey;
12
- }
13
-
14
- export default memo<Props>(({ sidebarKey }) => {
12
+ const Nav = memo(() => {
13
+ const sidebarKey = useActiveTabKey();
15
14
  return (
16
15
  <SideNav
17
16
  avatar={<Avatar />}
@@ -21,3 +20,7 @@ export default memo<Props>(({ sidebarKey }) => {
21
20
  />
22
21
  );
23
22
  });
23
+
24
+ Nav.displayName = 'DesktopNav';
25
+
26
+ export default Nav;
@@ -0,0 +1,71 @@
1
+ 'use client';
2
+
3
+ import { Icon, MobileTabBar, type MobileTabBarProps } from '@lobehub/ui';
4
+ import { createStyles } from 'antd-style';
5
+ import { Bot, MessageSquare, User } from 'lucide-react';
6
+ import { useRouter } from 'next/navigation';
7
+ import { rgba } from 'polished';
8
+ import { memo, useMemo } from 'react';
9
+ import { useTranslation } from 'react-i18next';
10
+
11
+ import { useActiveTabKey } from '@/hooks/useActiveTabKey';
12
+ import { SidebarTabKey } from '@/store/global/initialState';
13
+
14
+ const useStyles = createStyles(({ css, token }) => ({
15
+ active: css`
16
+ svg {
17
+ fill: ${rgba(token.colorPrimary, 0.33)};
18
+ }
19
+ `,
20
+ container: css`
21
+ position: fixed;
22
+ z-index: 100;
23
+ right: 0;
24
+ bottom: 0;
25
+ left: 0;
26
+ `,
27
+ }));
28
+
29
+ const Nav = memo(() => {
30
+ const { t } = useTranslation('common');
31
+ const { styles } = useStyles();
32
+ const activeKey = useActiveTabKey();
33
+ const router = useRouter();
34
+ const items: MobileTabBarProps['items'] = useMemo(
35
+ () => [
36
+ {
37
+ icon: (active) => (
38
+ <Icon className={active ? styles.active : undefined} icon={MessageSquare} />
39
+ ),
40
+ key: SidebarTabKey.Chat,
41
+ onClick: () => {
42
+ router.push('/chat');
43
+ },
44
+ title: t('tab.chat'),
45
+ },
46
+ {
47
+ icon: (active) => <Icon className={active ? styles.active : undefined} icon={Bot} />,
48
+ key: SidebarTabKey.Market,
49
+ onClick: () => {
50
+ router.push('/market');
51
+ },
52
+ title: t('tab.market'),
53
+ },
54
+ {
55
+ icon: (active) => <Icon className={active ? styles.active : undefined} icon={User} />,
56
+ key: SidebarTabKey.Setting,
57
+ onClick: () => {
58
+ router.push('/settings');
59
+ },
60
+ title: t('tab.setting'),
61
+ },
62
+ ],
63
+ [t],
64
+ );
65
+
66
+ return <MobileTabBar activeKey={activeKey} className={styles.container} items={items} safeArea />;
67
+ });
68
+
69
+ Nav.displayName = 'MobileNav';
70
+
71
+ export default Nav;
@@ -0,0 +1,10 @@
1
+ import ServerLayout from '@/components/server/ServerLayout';
2
+
3
+ import Desktop from './_layout/Desktop';
4
+ import Mobile from './_layout/Mobile';
5
+
6
+ const Nav = ServerLayout({ Desktop, Mobile });
7
+
8
+ Nav.displayName = 'Nav';
9
+
10
+ export default Nav;
@@ -0,0 +1,33 @@
1
+ 'use client';
2
+
3
+ import { useTheme } from 'antd-style';
4
+ import { memo } from 'react';
5
+ import { Flexbox } from 'react-layout-kit';
6
+
7
+ import { useIsPWA } from '@/hooks/useIsPWA';
8
+
9
+ import { LayoutProps } from './type';
10
+
11
+ const Layout = memo<LayoutProps>(({ children, nav }) => {
12
+ const isPWA = useIsPWA();
13
+ const theme = useTheme();
14
+
15
+ return (
16
+ <Flexbox
17
+ height={'100%'}
18
+ horizontal
19
+ style={{
20
+ borderTop: isPWA ? `1px solid ${theme.colorBorder}` : undefined,
21
+ position: 'relative',
22
+ }}
23
+ width={'100%'}
24
+ >
25
+ {nav}
26
+ {children}
27
+ </Flexbox>
28
+ );
29
+ });
30
+
31
+ Layout.displayName = 'DesktopMainLayout';
32
+
33
+ export default Layout;
@@ -0,0 +1,24 @@
1
+ 'use client';
2
+
3
+ import { usePathname } from 'next/navigation';
4
+ import { memo } from 'react';
5
+
6
+ import { LayoutProps } from './type';
7
+
8
+ const MOBILE_IGNORE_NAV_ROUTES = ['/settings/', '/chat/'];
9
+
10
+ const Layout = memo(({ children, nav }: LayoutProps) => {
11
+ const pathname = usePathname();
12
+ const hideNav = MOBILE_IGNORE_NAV_ROUTES.some((path) => pathname.startsWith(path));
13
+
14
+ return (
15
+ <>
16
+ {children}
17
+ {!hideNav && nav}
18
+ </>
19
+ );
20
+ });
21
+
22
+ Layout.displayName = 'MobileMainLayout';
23
+
24
+ export default Layout;
@@ -0,0 +1,6 @@
1
+ import { ReactNode } from 'react';
2
+
3
+ export interface LayoutProps {
4
+ children: ReactNode;
5
+ nav: ReactNode;
6
+ }
@@ -27,7 +27,7 @@ const ChatInputMobileLayout = memo(() => {
27
27
  setExpand={setExpand}
28
28
  style={{
29
29
  background: `linear-gradient(to bottom, ${theme.colorFillQuaternary}, transparent)`,
30
- width: '100vw',
30
+ width: '100%',
31
31
  }}
32
32
  textAreaLeftAddons={<STT mobile />}
33
33
  textAreaRightAddons={
@@ -3,6 +3,8 @@
3
3
  import { useRouter } from 'next/navigation';
4
4
  import { memo, useEffect } from 'react';
5
5
 
6
+ import MobileContentLayout from '@/components/server/MobileNavLayout';
7
+
6
8
  import SessionHeader from './features/SessionHeader';
7
9
  import SessionList from './features/SessionList';
8
10
 
@@ -15,10 +17,9 @@ const ChatMobilePage = memo(() => {
15
17
  }, []);
16
18
 
17
19
  return (
18
- <>
19
- <SessionHeader />
20
+ <MobileContentLayout header={<SessionHeader />} withNav>
20
21
  <SessionList />
21
- </>
22
+ </MobileContentLayout>
22
23
  );
23
24
  });
24
25
 
@@ -48,6 +48,7 @@ const MobileHeader = memo(() => {
48
48
  </>
49
49
  }
50
50
  showBackButton
51
+ style={{ width: '100%' }}
51
52
  />
52
53
  );
53
54
  });
@@ -2,8 +2,8 @@
2
2
 
3
3
  import dynamic from 'next/dynamic';
4
4
  import { memo } from 'react';
5
- import { Flexbox } from 'react-layout-kit';
6
5
 
6
+ import MobileContentLayout from '@/components/server/MobileNavLayout';
7
7
  import Conversation from '@/features/Conversation';
8
8
 
9
9
  import SessionHydration from '../../components/SessionHydration';
@@ -15,15 +15,12 @@ const TopicList = dynamic(() => import('../features/TopicList'));
15
15
 
16
16
  const Chat = memo(() => {
17
17
  return (
18
- <>
19
- <ChatHeader />
20
- <Flexbox height={'calc(100% - 44px)'} horizontal>
21
- <Conversation chatInput={<ChatInput />} mobile />
22
- <TopicList />
23
- <TelemetryNotification mobile />
24
- </Flexbox>
18
+ <MobileContentLayout header={<ChatHeader />}>
19
+ <Conversation chatInput={<ChatInput />} mobile />
20
+ <TopicList />
21
+ <TelemetryNotification mobile />
25
22
  <SessionHydration />
26
- </>
23
+ </MobileContentLayout>
27
24
  );
28
25
  });
29
26
  export default Chat;
@@ -2,14 +2,15 @@
2
2
 
3
3
  import { memo } from 'react';
4
4
 
5
+ import MobileContentLayout from '@/components/server/MobileNavLayout';
6
+
5
7
  import EditPage from '../features/EditPage';
6
8
  import Header from './Header';
7
9
 
8
10
  const ChatSettings = memo(() => (
9
- <>
10
- <Header />
11
+ <MobileContentLayout header={<Header />} withNav={false}>
11
12
  <EditPage />
12
- </>
13
+ </MobileContentLayout>
13
14
  ));
14
15
 
15
16
  export default ChatSettings;
@@ -0,0 +1,5 @@
1
+ 'use client';
2
+
3
+ import dynamic from 'next/dynamic';
4
+
5
+ export default dynamic(() => import('@/components/Error'));
@@ -0,0 +1,11 @@
1
+ import ServerLayout from '@/components/server/ServerLayout';
2
+
3
+ import Desktop from './_layout/Desktop';
4
+ import Mobile from './_layout/Mobile';
5
+ import { LayoutProps } from './_layout/type';
6
+
7
+ const MainLayout = ServerLayout<LayoutProps>({ Desktop, Mobile });
8
+
9
+ MainLayout.displayName = 'MainLayout';
10
+
11
+ export default MainLayout;
@@ -1,18 +1,16 @@
1
1
  import { PropsWithChildren } from 'react';
2
- import { Flexbox } from 'react-layout-kit';
2
+
3
+ import MobileContentLayout from '@/components/server/MobileNavLayout';
3
4
 
4
5
  import AgentSearchBar from '../../features/AgentSearchBar';
5
6
  import Header from './Header';
6
7
 
7
8
  const MobileLayout = ({ children }: PropsWithChildren) => {
8
9
  return (
9
- <>
10
- <Header />
11
- <Flexbox flex={1} gap={16} style={{ padding: 16 }}>
12
- <AgentSearchBar mobile />
13
- {children}
14
- </Flexbox>
15
- </>
10
+ <MobileContentLayout gap={16} header={<Header />} style={{ paddingInline: 16 }} withNav>
11
+ <AgentSearchBar mobile />
12
+ {children}
13
+ </MobileContentLayout>
16
14
  );
17
15
  };
18
16
 
@@ -3,6 +3,7 @@
3
3
  import { SearchBar } from '@lobehub/ui';
4
4
  import { memo, useCallback, useState } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
6
+ import { Flexbox } from 'react-layout-kit';
6
7
 
7
8
  import { useIsMobile } from '@/hooks/useIsMobile';
8
9
  import { useMarketStore } from '@/store/market';
@@ -19,18 +20,20 @@ const AgentSearchBar = memo<{ mobile?: boolean }>(({ mobile: controlledMobile })
19
20
  }, [value, setKeywords]);
20
21
 
21
22
  return (
22
- <SearchBar
23
- allowClear
24
- enableShortKey={!mobile}
25
- onChange={(e) => setValue(e.target.value)}
26
- onPressEnter={handleSearch}
27
- onSubmit={handleSearch}
28
- placeholder={t('search.placeholder')}
29
- shortKey={'k'}
30
- spotlight={!mobile}
31
- type={mobile ? 'block' : 'ghost'}
32
- value={value}
33
- />
23
+ <Flexbox flex={'none'} paddingBlock={mobile ? 8 : 0}>
24
+ <SearchBar
25
+ allowClear
26
+ enableShortKey={!mobile}
27
+ onChange={(e) => setValue(e.target.value)}
28
+ onPressEnter={handleSearch}
29
+ onSubmit={handleSearch}
30
+ placeholder={t('search.placeholder')}
31
+ shortKey={'k'}
32
+ spotlight={!mobile}
33
+ type={mobile ? 'block' : 'ghost'}
34
+ value={value}
35
+ />
36
+ </Flexbox>
34
37
  );
35
38
  });
36
39
 
@@ -0,0 +1,3 @@
1
+ import dynamic from 'next/dynamic';
2
+
3
+ export default dynamic(() => import('@/components/404'));
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { PropsWithChildren } from 'react';
4
4
 
5
+ import MobileContentLayout from '@/components/server/MobileNavLayout';
5
6
  import { useIsSubSlug } from '@/hooks/useIsSubSlug';
6
7
 
7
8
  import SubSettingHeader from './SubSettingHeader';
@@ -11,10 +12,9 @@ const MobileLayout = ({ children }: PropsWithChildren) => {
11
12
 
12
13
  if (isSubPath)
13
14
  return (
14
- <>
15
- <SubSettingHeader />
15
+ <MobileContentLayout header={<SubSettingHeader />} withNav={false}>
16
16
  {children}
17
- </>
17
+ </MobileContentLayout>
18
18
  );
19
19
 
20
20
  return children;
@@ -8,7 +8,6 @@ import Analytics from '@/components/Analytics';
8
8
  import { DEFAULT_LANG, LOBE_LOCALE_COOKIE } from '@/const/locale';
9
9
  import AuthProvider from '@/layout/AuthProvider';
10
10
  import GlobalProvider from '@/layout/GlobalProvider';
11
- import LayoutRoutes from '@/layout/routes';
12
11
  import { isMobileDevice } from '@/utils/responsive';
13
12
 
14
13
  const RootLayout = async ({ children }: PropsWithChildren) => {
@@ -21,9 +20,7 @@ const RootLayout = async ({ children }: PropsWithChildren) => {
21
20
  <html dir={direction} lang={lang?.value || DEFAULT_LANG} suppressHydrationWarning>
22
21
  <body>
23
22
  <GlobalProvider>
24
- <AuthProvider>
25
- <LayoutRoutes>{children}</LayoutRoutes>
26
- </AuthProvider>
23
+ <AuthProvider>{children}</AuthProvider>
27
24
  </GlobalProvider>
28
25
  <Analytics />
29
26
  <SpeedInsights />
package/src/app/page.tsx CHANGED
@@ -14,6 +14,8 @@ const Page = () => {
14
14
  );
15
15
  };
16
16
 
17
+ Page.displayName = 'Loading';
18
+
17
19
  export default Page;
18
20
 
19
21
  export const metadata: Metadata = {
@@ -9,7 +9,7 @@ import { Flexbox } from 'react-layout-kit';
9
9
 
10
10
  import { MAX_WIDTH } from '@/const/layoutTokens';
11
11
 
12
- const Page404 = memo(() => {
12
+ const NotFound = memo(() => {
13
13
  const { t } = useTranslation('error');
14
14
  return (
15
15
  <Flexbox align={'center'} justify={'center'} style={{ height: '100%', width: '100%' }}>
@@ -38,4 +38,6 @@ const Page404 = memo(() => {
38
38
  );
39
39
  });
40
40
 
41
- export default Page404;
41
+ NotFound.displayName = 'NotFound';
42
+
43
+ export default NotFound;
@@ -11,12 +11,12 @@ import { MAX_WIDTH } from '@/const/layoutTokens';
11
11
 
12
12
  import { type ErrorType, sentryCaptureException } from './sentryCaptureException';
13
13
 
14
- interface PageErrorProps {
14
+ interface ErrorCaptureProps {
15
15
  error: ErrorType;
16
16
  reset: () => void;
17
17
  }
18
18
 
19
- const PageError = memo<PageErrorProps>(({ reset, error }) => {
19
+ const ErrorCapture = memo<ErrorCaptureProps>(({ reset, error }) => {
20
20
  const { t } = useTranslation('error');
21
21
 
22
22
  useLayoutEffect(() => {
@@ -53,4 +53,6 @@ const PageError = memo<PageErrorProps>(({ reset, error }) => {
53
53
  );
54
54
  });
55
55
 
56
- export default PageError;
56
+ ErrorCapture.displayName = 'ErrorCapture';
57
+
58
+ export default ErrorCapture;
@@ -0,0 +1,60 @@
1
+ import { ReactNode } from 'react';
2
+ import { Flexbox, type FlexboxProps } from 'react-layout-kit';
3
+
4
+ interface MobileContentLayoutProps extends FlexboxProps {
5
+ header?: ReactNode;
6
+ withNav?: boolean;
7
+ }
8
+
9
+ const MobileContentLayout = ({
10
+ children,
11
+ withNav,
12
+ style,
13
+ header,
14
+ ...rest
15
+ }: MobileContentLayoutProps) => {
16
+ const content = (
17
+ <Flexbox
18
+ height="100%"
19
+ style={{
20
+ overflowX: 'hidden',
21
+ overflowY: 'auto',
22
+ position: 'relative',
23
+ ...style,
24
+ // TabNav Height
25
+ paddingBottom: withNav ? 48 : style?.paddingBottom,
26
+ }}
27
+ width="100%"
28
+ {...rest}
29
+ >
30
+ {children}
31
+ </Flexbox>
32
+ );
33
+
34
+ if (!header) return content;
35
+
36
+ return (
37
+ <Flexbox height={'100%'} style={{ overflow: 'hidden', position: 'relative' }} width={'100%'}>
38
+ {header}
39
+ <Flexbox
40
+ height="100%"
41
+ style={{
42
+ overflowX: 'hidden',
43
+ overflowY: 'auto',
44
+ position: 'relative',
45
+ ...style,
46
+ // TabNav Height
47
+ paddingBottom: withNav ? 48 : style?.paddingBottom,
48
+ }}
49
+ width="100%"
50
+ {...rest}
51
+ >
52
+ {children}
53
+ </Flexbox>
54
+ </Flexbox>
55
+ );
56
+ };
57
+
58
+ MobileContentLayout.displayName = 'MobileContentLayout';
59
+
60
+ export default MobileContentLayout;
@@ -2,17 +2,16 @@ import { FC, PropsWithChildren } from 'react';
2
2
 
3
3
  import { isMobileDevice } from '@/utils/responsive';
4
4
 
5
- interface ServerLayoutProps {
6
- Desktop: FC<PropsWithChildren>;
7
- Mobile: FC<PropsWithChildren>;
5
+ interface ServerLayoutProps<T> {
6
+ Desktop: FC<T>;
7
+ Mobile: FC<T>;
8
8
  }
9
9
 
10
10
  const ServerLayout =
11
- ({ Desktop, Mobile }: ServerLayoutProps) =>
12
- ({ children }: PropsWithChildren) => {
11
+ <T extends PropsWithChildren>({ Desktop, Mobile }: ServerLayoutProps<T>): FC<T> =>
12
+ (props: T) => {
13
13
  const mobile = isMobileDevice();
14
-
15
- return mobile ? <Mobile>{children}</Mobile> : <Desktop>{children}</Desktop>;
14
+ return mobile ? <Mobile {...props} /> : <Desktop {...props} />;
16
15
  };
17
16
 
18
17
  ServerLayout.displayName = 'ServerLayout';
@@ -89,6 +89,16 @@ describe('SessionGroupModel', () => {
89
89
  expect(session.group).not.toEqual(createdGroup.id);
90
90
  });
91
91
  });
92
+ it('should update associated sessions to default group when a session group is deleted', async () => {
93
+ const createdGroup = await SessionGroupModel.create(
94
+ sessionGroupData.name,
95
+ sessionGroupData.sort,
96
+ );
97
+ const sessionId = await SessionModel.create('agent', {}, createdGroup.id);
98
+ await SessionGroupModel.delete(createdGroup.id);
99
+ const updatedSession = await SessionModel.findById(sessionId.id);
100
+ expect(updatedSession.group).toEqual('default');
101
+ });
92
102
  });
93
103
 
94
104
  describe('query', () => {
@@ -153,6 +163,14 @@ describe('SessionGroupModel', () => {
153
163
  expect(fetchedGroups[0].id).toEqual(group2.id);
154
164
  expect(fetchedGroups[1].id).toEqual(group1.id);
155
165
  });
166
+
167
+ it('should sort session groups correctly when only b has a sort value', async () => {
168
+ const groupA = await SessionGroupModel.create('groupA'); // sort undefined
169
+ const groupB = await SessionGroupModel.create('groupB', 1); // sort defined
170
+ const fetchedGroups = await SessionGroupModel.query();
171
+ expect(fetchedGroups[0].id).toEqual(groupB.id);
172
+ expect(fetchedGroups[1].id).toEqual(groupA.id);
173
+ });
156
174
  });
157
175
 
158
176
  describe('updateOrder', () => {
@@ -9,14 +9,13 @@ import SkeletonList from './components/SkeletonList';
9
9
  const ChatList = lazy(() => import('./components/VirtualizedList'));
10
10
 
11
11
  const useStyles = createStyles(
12
- ({ css, responsive, stylish }) => css`
12
+ ({ css, responsive }) => css`
13
13
  position: relative;
14
14
  overflow-y: auto;
15
15
  height: 100%;
16
16
 
17
17
  ${responsive.mobile} {
18
- ${stylish.noScrollbar}
19
- width: 100vw;
18
+ width: 100%;
20
19
  }
21
20
  `,
22
21
  );
@@ -32,6 +31,7 @@ const Conversation = memo<ConversationProps>(({ chatInput, mobile }) => {
32
31
  return (
33
32
  <Flexbox
34
33
  flex={1}
34
+ height={'100%'}
35
35
  // `relative` is required, ChatInput's absolute position needs it
36
36
  style={{ position: 'relative' }}
37
37
  >
@@ -223,7 +223,6 @@ describe('LobeMinimaxAI', () => {
223
223
  model: 'text-davinci-003',
224
224
  temperature: 0.5,
225
225
  top_p: 0.8,
226
- max_tokens: 100,
227
226
  };
228
227
 
229
228
  const result = instance['buildCompletionsParams'](payload);
@@ -234,7 +233,6 @@ describe('LobeMinimaxAI', () => {
234
233
  stream: true,
235
234
  temperature: 0.5,
236
235
  top_p: 0.8,
237
- max_tokens: 100,
238
236
  });
239
237
  });
240
238
 
@@ -244,7 +242,6 @@ describe('LobeMinimaxAI', () => {
244
242
  model: 'text-davinci-003',
245
243
  temperature: 0,
246
244
  top_p: 0,
247
- max_tokens: 100,
248
245
  };
249
246
 
250
247
  const result = instance['buildCompletionsParams'](payload);
@@ -253,7 +250,24 @@ describe('LobeMinimaxAI', () => {
253
250
  messages: [{ content: 'Hello', role: 'user' }],
254
251
  model: 'text-davinci-003',
255
252
  stream: true,
256
- max_tokens: 100,
253
+ });
254
+ });
255
+
256
+ it('should include max tokens when model is abab6.5-chat', () => {
257
+ const payload: ChatStreamPayload = {
258
+ messages: [{ content: 'Hello', role: 'user' }],
259
+ model: 'abab6.5-chat',
260
+ temperature: 0,
261
+ top_p: 0,
262
+ };
263
+
264
+ const result = instance['buildCompletionsParams'](payload);
265
+
266
+ expect(result).toEqual({
267
+ messages: [{ content: 'Hello', role: 'user' }],
268
+ model: 'abab6.5-chat',
269
+ stream: true,
270
+ max_tokens: 2048,
257
271
  });
258
272
  });
259
273
  });
@@ -49,7 +49,7 @@ function throwIfErrorResponse(data: MinimaxResponse) {
49
49
  });
50
50
  }
51
51
 
52
- function parseMinimaxResponse(chunk: string): string | undefined {
52
+ function parseMinimaxResponse(chunk: string): MinimaxResponse | undefined {
53
53
  let body = chunk;
54
54
  if (body.startsWith('data:')) {
55
55
  body = body.slice(5).trim();
@@ -57,10 +57,7 @@ function parseMinimaxResponse(chunk: string): string | undefined {
57
57
  if (isEmpty(body)) {
58
58
  return;
59
59
  }
60
- const data = JSON.parse(body) as MinimaxResponse;
61
- if (data.choices?.at(0)?.delta?.content) {
62
- return data.choices.at(0)?.delta.content || undefined;
63
- }
60
+ return JSON.parse(body) as MinimaxResponse;
64
61
  }
65
62
 
66
63
  export class LobeMinimaxAI implements LobeRuntimeAI {
@@ -136,11 +133,25 @@ export class LobeMinimaxAI implements LobeRuntimeAI {
136
133
  }
137
134
  }
138
135
 
136
+ // the document gives the default value of max tokens, but abab6.5 and abab6.5s
137
+ // will meet length finished error, and output is truncationed
138
+ // so here fill the max tokens number to fix it
139
+ // https://www.minimaxi.com/document/guides/chat-model/V2
140
+ private getMaxTokens(model: string): number | undefined {
141
+ switch (model) {
142
+ case 'abab6.5-chat':
143
+ case 'abab6.5s-chat': {
144
+ return 2048;
145
+ }
146
+ }
147
+ }
148
+
139
149
  private buildCompletionsParams(payload: ChatStreamPayload) {
140
150
  const { temperature, top_p, ...params } = payload;
141
151
 
142
152
  return {
143
153
  ...params,
154
+ max_tokens: this.getMaxTokens(payload.model),
144
155
  stream: true,
145
156
  temperature: temperature === 0 ? undefined : temperature,
146
157
  top_p: top_p === 0 ? undefined : top_p,
@@ -159,7 +170,8 @@ export class LobeMinimaxAI implements LobeRuntimeAI {
159
170
  const { value, done: doneReading } = await reader.read();
160
171
  done = doneReading;
161
172
  const chunkValue = decoder.decode(value, { stream: true });
162
- const text = parseMinimaxResponse(chunkValue);
173
+ const data = parseMinimaxResponse(chunkValue);
174
+ const text = data?.choices?.at(0)?.delta?.content || undefined;
163
175
  streamController?.enqueue(encoder.encode(text));
164
176
  }
165
177
 
@@ -173,12 +185,14 @@ export class LobeMinimaxAI implements LobeRuntimeAI {
173
185
  const chunkValue = decoder.decode(value, { stream: true });
174
186
  let data;
175
187
  try {
176
- data = JSON.parse(chunkValue) as MinimaxResponse;
188
+ data = parseMinimaxResponse(chunkValue);
177
189
  } catch {
178
190
  // parse error, skip it
179
191
  return;
180
192
  }
181
- throwIfErrorResponse(data);
193
+ if (data) {
194
+ throwIfErrorResponse(data);
195
+ }
182
196
  }
183
197
  }
184
198
 
@@ -25,6 +25,7 @@ export const getServerGlobalConfig = () => {
25
25
  ENABLED_GROQ,
26
26
  ENABLED_PERPLEXITY,
27
27
  ENABLED_ANTHROPIC,
28
+ ENABLED_MINIMAX,
28
29
  ENABLED_MISTRAL,
29
30
 
30
31
  ENABLED_AZURE_OPENAI,
@@ -64,6 +65,7 @@ export const getServerGlobalConfig = () => {
64
65
  bedrock: { enabled: ENABLED_AWS_BEDROCK },
65
66
  google: { enabled: ENABLED_GOOGLE },
66
67
  groq: { enabled: ENABLED_GROQ },
68
+ minimax: { enabled: ENABLED_MINIMAX },
67
69
  mistral: { enabled: ENABLED_MISTRAL },
68
70
  moonshot: { enabled: ENABLED_MOONSHOT },
69
71
  ollama: {
@@ -3,5 +3,6 @@ import { CSSProperties } from 'react';
3
3
  export const mobileHeaderSticky: CSSProperties = {
4
4
  position: 'sticky',
5
5
  top: 0,
6
+ width: '100%',
6
7
  zIndex: 100,
7
8
  };
@@ -1,32 +0,0 @@
1
- 'use client';
2
-
3
- import { useTheme } from 'antd-style';
4
- import { PropsWithChildren, memo } from 'react';
5
- import { Flexbox } from 'react-layout-kit';
6
-
7
- import ClientResponsiveLayout from '@/components/client/ClientResponsiveLayout';
8
- import { useActiveTabKey } from '@/hooks/useActiveTabKey';
9
- import { useIsPWA } from '@/hooks/useIsPWA';
10
-
11
- import SideBar from './SideBar';
12
-
13
- const Desktop = memo<PropsWithChildren>(({ children }) => {
14
- const isPWA = useIsPWA();
15
- const theme = useTheme();
16
-
17
- const sidebarKey = useActiveTabKey();
18
-
19
- return (
20
- <Flexbox
21
- height={'100%'}
22
- horizontal
23
- style={isPWA ? { borderTop: `1px solid ${theme.colorBorder}` } : {}}
24
- width={'100%'}
25
- >
26
- <SideBar sidebarKey={sidebarKey} />
27
- {children}
28
- </Flexbox>
29
- );
30
- });
31
-
32
- export default ClientResponsiveLayout({ Desktop, Mobile: () => import('../Mobile') });
@@ -1,59 +0,0 @@
1
- 'use client';
2
-
3
- import { type MobileNavBarTitleProps } from '@lobehub/ui';
4
- import { createStyles } from 'antd-style';
5
- import dynamic from 'next/dynamic';
6
- import { CSSProperties, PropsWithChildren, memo } from 'react';
7
- import { Flexbox } from 'react-layout-kit';
8
-
9
- import SafeSpacing from '@/components/SafeSpacing';
10
- import { SidebarTabKey } from '@/store/global/initialState';
11
-
12
- const MobileTabBar = dynamic(() => import('@/features/MobileTabBar'));
13
-
14
- const useStyles = createStyles(({ css, cx, stylish }) => ({
15
- container: cx(
16
- stylish.noScrollbar,
17
- css`
18
- position: relative;
19
- overflow: hidden auto;
20
- width: 100vw;
21
- height: 100%;
22
- `,
23
- ),
24
- mobileTabBar: css`
25
- position: fixed;
26
- z-index: 100;
27
- right: 0;
28
- bottom: 0;
29
- left: 0;
30
- `,
31
- }));
32
-
33
- interface AppMobileLayoutProps extends PropsWithChildren {
34
- className?: string;
35
- showTabBar?: boolean;
36
- style?: CSSProperties;
37
- tabBarKey?: SidebarTabKey;
38
- title?: MobileNavBarTitleProps;
39
- }
40
-
41
- const AppLayoutMobile = memo<AppMobileLayoutProps>(
42
- ({ children, showTabBar, tabBarKey, style, className }) => {
43
- const { styles, cx } = useStyles();
44
-
45
- return (
46
- <Flexbox className={cx(styles.container, className)} style={style}>
47
- {children}
48
- {showTabBar && (
49
- <>
50
- <SafeSpacing mobile position={'bottom'} />
51
- <MobileTabBar className={styles.mobileTabBar} tabBarKey={tabBarKey} />
52
- </>
53
- )}
54
- </Flexbox>
55
- );
56
- },
57
- );
58
-
59
- export default AppLayoutMobile;
@@ -1,2 +0,0 @@
1
- export { default as DefaultLayoutDesktop } from './Desktop';
2
- export { default as DefaultLayoutMobile } from './Mobile';
@@ -1,18 +0,0 @@
1
- 'use client';
2
-
3
- import { usePathname } from 'next/navigation';
4
- import { PropsWithChildren, memo } from 'react';
5
-
6
- import { DefaultLayoutDesktop } from '@/layout/DefaultLayout';
7
-
8
- const defaultLayoutRoutes = new Set(['/']);
9
-
10
- const DesktopLayout = memo<PropsWithChildren>(({ children }) => {
11
- const pathname = usePathname();
12
-
13
- if (defaultLayoutRoutes.has(pathname)) return children;
14
-
15
- return <DefaultLayoutDesktop>{children}</DefaultLayoutDesktop>;
16
- });
17
-
18
- export default DesktopLayout;
@@ -1,24 +0,0 @@
1
- 'use client';
2
-
3
- import { usePathname } from 'next/navigation';
4
- import { PropsWithChildren, memo } from 'react';
5
-
6
- import { useActiveTabKey } from '@/hooks/useActiveTabKey';
7
- import { useIsSubSlug } from '@/hooks/useIsSubSlug';
8
- import { DefaultLayoutMobile } from '@/layout/DefaultLayout';
9
-
10
- const MobileLayout = memo<PropsWithChildren>(({ children }) => {
11
- const pathname = usePathname();
12
- const tabBarKey = useActiveTabKey();
13
- const isSubPath = useIsSubSlug();
14
-
15
- if (pathname === '/') return children;
16
-
17
- return (
18
- <DefaultLayoutMobile showTabBar={!isSubPath} tabBarKey={tabBarKey}>
19
- {children}
20
- </DefaultLayoutMobile>
21
- );
22
- });
23
-
24
- export default MobileLayout;
@@ -1,8 +0,0 @@
1
- import ServerLayout from '@/components/server/ServerLayout';
2
-
3
- import Desktop from './Desktop';
4
- import Mobile from './Mobile';
5
-
6
- const LayoutRoutes = ServerLayout({ Desktop, Mobile });
7
-
8
- export default LayoutRoutes;