This commit is contained in:
archer
2023-06-10 14:01:00 +08:00
parent e19ac56fe5
commit 6fd49b0955
11 changed files with 161 additions and 119 deletions

1
.gitignore vendored
View File

@@ -28,5 +28,4 @@ next-env.d.ts
platform.json
testApi/
local/
.husky/
dist/

6
.husky/pre-commit Executable file
View File

@@ -0,0 +1,6 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
if command -v npx >/dev/null 2>&1; then
npx lint-staged
fi

View File

@@ -27,7 +27,7 @@ const Navbar = ({ unread }: { unread: number }) => {
activeLink: ['/chat']
},
{
label: '我的应用',
label: '应用',
icon: 'model',
link: `/model?modelId=${lastModelId}`,
activeLink: ['/model']
@@ -39,7 +39,7 @@ const Navbar = ({ unread }: { unread: number }) => {
activeLink: ['/kb']
},
{
label: '应用市场',
label: '市场',
icon: 'appStore',
link: '/model/share',
activeLink: ['/model/share']
@@ -61,14 +61,15 @@ const Navbar = ({ unread }: { unread: number }) => {
);
const itemStyles: any = {
mb: 3,
my: 3,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
cursor: 'pointer',
w: '60px',
h: '45px',
w: '54px',
h: '54px',
borderRadius: 'md',
_hover: {
color: '#ffffff'
}
@@ -79,7 +80,7 @@ const Navbar = ({ unread }: { unread: number }) => {
flexDirection={'column'}
alignItems={'center'}
pt={6}
backgroundColor={'#465069'}
backgroundImage={'linear-gradient(to bottom right,#465069,#000000)'}
h={'100%'}
w={'100%'}
boxShadow={'4px 0px 4px 0px rgba(43, 45, 55, 0.01)'}
@@ -99,30 +100,26 @@ const Navbar = ({ unread }: { unread: number }) => {
{/* 导航列表 */}
<Box flex={1}>
{navbarList.map((item) => (
<Tooltip
label={item.label}
key={item.label}
placement={'right'}
openDelay={100}
gutter={-10}
<Link
key={item.link}
as={NextLink}
href={item.link}
{...itemStyles}
{...(item.activeLink.includes(router.pathname)
? {
color: '#ffffff ',
backgroundImage: 'linear-gradient(to bottom right, #2152d9 0%, #4e83fd 100%)'
}
: {
color: '#9096a5',
backgroundColor: 'transparent'
})}
>
<Link
as={NextLink}
href={item.link}
{...itemStyles}
{...(item.activeLink.includes(router.pathname)
? {
color: '#ffffff ',
backgroundImage: 'linear-gradient(270deg,#4e83fd,#3370ff)'
}
: {
color: '#9096a5',
backgroundColor: 'transparent'
})}
>
<MyIcon name={item.icon as any} width={'22px'} height={'22px'} />
</Link>
</Tooltip>
<MyIcon name={item.icon as any} width={'20px'} height={'20px'} />
<Box fontSize={'12px'} transform={'scale(0.9)'} mt={'5px'} lineHeight={1}>
{item.label}
</Box>
</Link>
))}
</Box>
{unread > 0 && (

View File

@@ -232,6 +232,10 @@ export const theme = extendTheme({
xl: '1800px',
'2xl': '2100px'
},
active: {
activeBlueGradient: 'linear-gradient(120deg, #d6e8ff 0%, #f0f7ff 100%)',
hoverBlueGradient: 'linear-gradient(60deg, #f0f7ff 0%, #d6e8ff 100%)'
},
components: {
Modal: ModalTheme,
Button,

View File

@@ -40,3 +40,8 @@ export const InformTypeMap = {
label: '系统通知'
}
};
export enum MyModelsTypeEnum {
my = 'my',
collection = 'collection'
}

View File

@@ -7,6 +7,7 @@ import { withNextCors } from '@/service/utils/tools';
import { TrainingModeEnum } from '@/constants/plugin';
import { startQueue } from '@/service/utils/tools';
import { PgClient } from '@/service/pg';
import { modelToolMap } from '@/utils/plugin';
type DateItemType = { a: string; q: string; source?: string };
@@ -21,6 +22,11 @@ export type Response = {
insertLen: number;
};
const modeMaxToken = {
[TrainingModeEnum.index]: 700,
[TrainingModeEnum.qa]: 3300
};
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
const { kbId, data, mode, prompt } = req.body as Props;
@@ -68,7 +74,15 @@ export async function pushDataToKb({
data.forEach((item) => {
const text = item.q + item.a;
if (!set.has(text)) {
// count token
const token = modelToolMap['gpt-3.5-turbo'].countTokens({
messages: [{ obj: 'System', value: item.q }]
});
if (mode === TrainingModeEnum.qa && token > modeMaxToken[TrainingModeEnum.qa]) {
console.log('q is too long');
} else if (!set.has(text)) {
filterData.push(item);
set.add(text);
}

View File

@@ -146,31 +146,26 @@ const PcSliderBar = ({
)}
</Box>
)}
{/* chat history */}
<Box flex={'1 0 0'} h={0} overflow={'overlay'}>
<Box flex={'1 0 0'} h={0} overflow={'overlay'} userSelect={'none'}>
{history.map((item) => (
<Flex
position={'relative'}
key={item._id}
position={'relative'}
alignItems={'center'}
py={3}
pr={[0, 3]}
pl={[6, 3]}
p={3}
mb={[2, 0]}
cursor={'pointer'}
transition={'background-color .2s ease-in'}
borderLeft={['none', '5px solid transparent']}
userSelect={'none'}
_hover={{
bg: ['', '#dee0e3']
backgroundImage: ['', theme.active.hoverBlueGradient]
}}
{...(item._id === chatId
? {
bg: 'myGray.100 !important',
borderLeftColor: 'myBlue.600 !important'
backgroundImage: `${theme.active.activeBlueGradient}`
}
: {
bg: item.top ? 'myBlue.200' : ''
bg: item.top ? 'myGray.200' : ''
})}
onClick={() => {
if (item._id === chatId) return;

View File

@@ -87,7 +87,7 @@ const KbList = ({ kbId }: { kbId: string }) => {
/>
</Tooltip>
</Flex>
<Box flex={'1 0 0'} h={0} overflow={'overlay'}>
<Box flex={'1 0 0'} h={0} overflow={'overlay'} userSelect={'none'}>
{kbs.map((item) => (
<Flex
key={item._id}
@@ -97,14 +97,12 @@ const KbList = ({ kbId }: { kbId: string }) => {
mb={[2, 0]}
cursor={'pointer'}
transition={'background-color .2s ease-in'}
borderLeft={['', '5px solid transparent']}
_hover={{
backgroundColor: ['', '#dee0e3']
backgroundImage: ['', theme.active.hoverBlueGradient]
}}
{...(kbId === item._id
? {
backgroundColor: '#eff0f1',
borderLeftColor: 'myBlue.600 !important'
backgroundImage: `${theme.active.activeBlueGradient} !important`
}
: {})}
onClick={() => {

View File

@@ -1,5 +1,15 @@
import React, { useCallback, useMemo, useState } from 'react';
import { Box, Flex, useTheme, Input, IconButton, Tooltip } from '@chakra-ui/react';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import {
Box,
Flex,
Input,
IconButton,
Tooltip,
Tabs,
TabList,
Tab,
useTheme
} from '@chakra-ui/react';
import { AddIcon } from '@chakra-ui/icons';
import { useRouter } from 'next/router';
import MyIcon from '@/components/Icon';
@@ -9,8 +19,15 @@ import { useToast } from '@/hooks/useToast';
import { useQuery } from '@tanstack/react-query';
import { useUserStore } from '@/store/user';
import Avatar from '@/components/Avatar';
import { MyModelsTypeEnum } from '@/constants/user';
const ModelList = ({ modelId }: { modelId: string }) => {
const tabs = useRef([
{ label: '我的', value: MyModelsTypeEnum.my },
{ label: '收藏', value: MyModelsTypeEnum.collection }
]);
const [currentTab, setCurrentTab] = useState(MyModelsTypeEnum.my);
const theme = useTheme();
const router = useRouter();
const { toast } = useToast();
@@ -42,29 +59,23 @@ const ModelList = ({ modelId }: { modelId: string }) => {
setIsLoading(false);
}, [myModels.length, refreshModel, router, setIsLoading, toast]);
const models = useMemo(
() =>
[
{
label: '我的',
list: myModels.filter((item) =>
new RegExp(searchText, 'ig').test(item.name + item.systemPrompt)
)
},
{
label: '收藏',
list: myCollectionModels.filter((item) =>
new RegExp(searchText, 'ig').test(item.name + item.systemPrompt)
)
}
].filter((item) => item.list.length > 0),
[myCollectionModels, myModels, searchText]
);
const totalModels = useMemo(
() => models.reduce((sum, item) => sum + item.list.length, 0),
[models]
);
const currentModels = useMemo(() => {
const map = {
[MyModelsTypeEnum.my]: {
list: myModels.filter((item) =>
new RegExp(searchText, 'ig').test(item.name + item.systemPrompt)
),
emptyText: '还没有 AI 应用~\n快来创建一个吧'
},
[MyModelsTypeEnum.collection]: {
list: myCollectionModels.filter((item) =>
new RegExp(searchText, 'ig').test(item.name + item.systemPrompt)
),
emptyText: '收藏的 AI 应用为空~\n快去市场找一个吧'
}
};
return map[currentTab];
}, [currentTab, myCollectionModels, myModels, searchText]);
return (
<Flex
@@ -107,55 +118,69 @@ const ModelList = ({ modelId }: { modelId: string }) => {
/>
</Tooltip>
</Flex>
<Box flex={'1 0 0'} h={0} overflow={'overlay'}>
{models.map((item) => (
<Box key={item.label} _notFirst={{ mt: 5 }}>
<Box fontWeight={'bold'} pl={5}>
{item.label}
</Box>
{item.list.map((item) => (
<Flex
key={item._id}
position={'relative'}
alignItems={['flex-start', 'center']}
p={3}
mb={[2, 0]}
cursor={'pointer'}
transition={'background-color .2s ease-in'}
borderLeft={['', '5px solid transparent']}
_hover={{
backgroundColor: ['', '#dee0e3']
}}
{...(modelId === item._id
? {
backgroundColor: '#eff0f1',
borderLeftColor: 'myBlue.600 !important'
}
: {})}
onClick={() => {
if (item._id === modelId) return;
router.push(`/model?modelId=${item._id}`);
}}
<Flex mb={4} userSelect={'none'}>
<Box flex={1}></Box>
<Tabs
variant="unstyled"
defaultIndex={tabs.current.findIndex((item) => item.value === currentTab)}
onChange={(i) => setCurrentTab(tabs.current[i].value)}
>
<TabList whiteSpace={'nowrap'}>
{tabs.current.map((item) => (
<Tab
key={item.value}
py={'2px'}
px={4}
borderRadius={'sm'}
mr={2}
transition={'none'}
_selected={{ color: 'white', bg: 'myBlue.600' }}
>
<Avatar src={item.avatar} w={'34px'} h={'34px'} />
<Box flex={'1 0 0'} w={0} ml={3}>
<Box className="textEllipsis" color={'myGray.1000'}>
{item.name}
</Box>
<Box className="textEllipsis" color={'myGray.400'} fontSize={'sm'}>
{item.systemPrompt || '这个 应用 没有设置提示词~'}
</Box>
</Box>
</Flex>
{item.label}
</Tab>
))}
</Box>
</TabList>
</Tabs>
</Flex>
<Box flex={'1 0 0'} h={0} overflow={'overlay'} userSelect={'none'}>
{currentModels.list.map((item) => (
<Flex
key={item._id}
position={'relative'}
alignItems={['flex-start', 'center']}
p={3}
mb={[2, 0]}
cursor={'pointer'}
transition={'background-color .2s ease-in'}
_hover={{
backgroundImage: ['', theme.active.hoverBlueGradient]
}}
{...(modelId === item._id
? {
backgroundImage: `${theme.active.activeBlueGradient} !important`
}
: {})}
onClick={() => {
if (item._id === modelId) return;
router.push(`/model?modelId=${item._id}`);
}}
>
<Avatar src={item.avatar} w={'34px'} h={'34px'} />
<Box flex={'1 0 0'} w={0} ml={3}>
<Box className="textEllipsis" color={'myGray.1000'}>
{item.name}
</Box>
<Box className="textEllipsis" color={'myGray.400'} fontSize={'sm'}>
{item.systemPrompt || '这个 应用 没有设置提示词~'}
</Box>
</Box>
</Flex>
))}
{!isFetching && totalModels === 0 && (
{!isFetching && currentModels.list.length === 0 && (
<Flex h={'100%'} flexDirection={'column'} alignItems={'center'} pt={'30vh'}>
<MyIcon name="empty" w={'48px'} h={'48px'} color={'transparent'} />
<Box mt={2} color={'myGray.500'}>
AI ~
{currentModels.emptyText}
</Box>
</Flex>
)}

View File

@@ -44,7 +44,7 @@ svg {
div {
&::-webkit-scrollbar-thumb {
background: transparent !important;
transition: 1s;
transition: background 1s;
}
&:hover {
&::-webkit-scrollbar-thumb {

View File

@@ -6,14 +6,13 @@
"prepare": "husky install",
"format": "prettier --config \"./.prettierrc.js\" --write \"./**/src/**/*.{ts,tsx,scss}\""
},
"dependencies": {},
"devDependencies": {
"husky": "^8.0.3",
"lint-staged": "^13.2.1",
"prettier": "^2.8.7"
},
"lint-staged": {
"./**/*.{ts,tsx,scss}": "npm run format"
"./**/**/*.{ts,tsx,scss}": "npm run format"
},
"engines": {
"node": ">=18.0.0"