v4.2.2 (#312)
This commit is contained in:
@@ -13,6 +13,8 @@ import { Props as UpdateDataProps } from '@/pages/api/openapi/kb/updateData';
|
||||
import type { KbUpdateParams, CreateKbParams, GetKbDataListProps } from '../request/kb';
|
||||
import { QuoteItemType } from '@/types/chat';
|
||||
import { KbTypeEnum } from '@/constants/kb';
|
||||
import { getToken } from '@/utils/user';
|
||||
import download from 'downloadjs';
|
||||
|
||||
/* knowledge base */
|
||||
export const getKbList = (data: { parentId?: string; type?: `${KbTypeEnum}` }) =>
|
||||
@@ -35,12 +37,23 @@ export const getKbDataList = (data: GetKbDataListProps) =>
|
||||
POST(`/plugins/kb/data/getDataList`, data);
|
||||
|
||||
/**
|
||||
* 获取导出数据(不分页)
|
||||
* export and download data
|
||||
*/
|
||||
export const getExportDataList = (data: { kbId: string }) =>
|
||||
GET<[string, string, string][]>(`/plugins/kb/data/exportModelData`, data, {
|
||||
timeout: 600000
|
||||
});
|
||||
export const exportDataset = (data: { kbId: string }) =>
|
||||
fetch(`/api/plugins/kb/data/exportAll?kbId=${data.kbId}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
token: getToken()
|
||||
}
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) {
|
||||
const data = await res.json();
|
||||
throw new Error(data?.message || 'Export failed');
|
||||
}
|
||||
return res.blob();
|
||||
})
|
||||
.then((blob) => download(blob, 'dataset.csv', 'text/csv'));
|
||||
|
||||
/**
|
||||
* 获取模型正在拆分数据的数量
|
||||
|
||||
@@ -452,29 +452,22 @@ const ChatBox = (
|
||||
border: theme.borders.base,
|
||||
mr: 3
|
||||
};
|
||||
const controlContainerStyle = useCallback((status: ChatSiteItemType['status']) => {
|
||||
return {
|
||||
className: 'control',
|
||||
color: 'myGray.400',
|
||||
display:
|
||||
status === 'finish'
|
||||
? feedbackType === FeedbackTypeEnum.admin
|
||||
? 'flex'
|
||||
: ['flex', 'none']
|
||||
: 'none',
|
||||
pl: 1,
|
||||
mt: 2
|
||||
};
|
||||
}, []);
|
||||
const controlContainerStyle = {
|
||||
className: 'control',
|
||||
color: 'myGray.400',
|
||||
display: 'flex',
|
||||
pl: 1,
|
||||
mt: 2
|
||||
};
|
||||
const MessageCardStyle: BoxProps = {
|
||||
px: 4,
|
||||
py: 3,
|
||||
borderRadius: '0 8px 8px 8px',
|
||||
boxShadow: '0 0 8px rgba(0,0,0,0.15)'
|
||||
boxShadow: '0 0 8px rgba(0,0,0,0.15)',
|
||||
display: 'inline-block',
|
||||
maxW: ['calc(100% - 25px)', 'calc(100% - 40px)']
|
||||
};
|
||||
|
||||
const messageCardMaxW = ['calc(100% - 25px)', 'calc(100% - 40px)'];
|
||||
|
||||
const showEmpty = useMemo(
|
||||
() =>
|
||||
feConfigs?.show_emptyChat &&
|
||||
@@ -542,84 +535,80 @@ const ChatBox = (
|
||||
{showEmpty && <Empty />}
|
||||
|
||||
{!!welcomeText && (
|
||||
<Flex flexDirection={'column'} alignItems={'flex-start'} py={2}>
|
||||
<Box py={3}>
|
||||
{/* avatar */}
|
||||
<ChatAvatar src={appAvatar} type={'AI'} />
|
||||
{/* message */}
|
||||
<Card order={2} mt={2} {...MessageCardStyle} bg={'white'} maxW={messageCardMaxW}>
|
||||
<Markdown source={`~~~guide \n${welcomeText}`} isChatting={false} />
|
||||
</Card>
|
||||
</Flex>
|
||||
<Box textAlign={'left'}>
|
||||
<Card order={2} mt={2} {...MessageCardStyle} bg={'white'}>
|
||||
<Markdown source={`~~~guide \n${welcomeText}`} isChatting={false} />
|
||||
</Card>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
{/* variable input */}
|
||||
{!!variableModules?.length && (
|
||||
<Flex flexDirection={'column'} alignItems={'flex-start'} py={2}>
|
||||
<Box py={3}>
|
||||
{/* avatar */}
|
||||
<ChatAvatar src={appAvatar} type={'AI'} />
|
||||
{/* message */}
|
||||
<Card
|
||||
order={2}
|
||||
mt={2}
|
||||
bg={'white'}
|
||||
w={'400px'}
|
||||
maxW={messageCardMaxW}
|
||||
{...MessageCardStyle}
|
||||
>
|
||||
{variableModules.map((item) => (
|
||||
<Box key={item.id} mb={4}>
|
||||
<VariableLabel required={item.required}>{item.label}</VariableLabel>
|
||||
{item.type === VariableInputEnum.input && (
|
||||
<Input
|
||||
isDisabled={variableIsFinish}
|
||||
{...register(item.key, {
|
||||
required: item.required
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
{item.type === VariableInputEnum.select && (
|
||||
<MySelect
|
||||
width={'100%'}
|
||||
isDisabled={variableIsFinish}
|
||||
list={(item.enums || []).map((item) => ({
|
||||
label: item.value,
|
||||
value: item.value
|
||||
}))}
|
||||
{...register(item.key, {
|
||||
required: item.required
|
||||
})}
|
||||
value={getValues(item.key)}
|
||||
onchange={(e) => {
|
||||
setValue(item.key, e);
|
||||
setRefresh(!refresh);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
))}
|
||||
{!variableIsFinish && (
|
||||
<Button
|
||||
leftIcon={<MyIcon name={'chatFill'} w={'16px'} />}
|
||||
size={'sm'}
|
||||
maxW={'100px'}
|
||||
borderRadius={'lg'}
|
||||
onClick={handleSubmit((data) => {
|
||||
onUpdateVariable?.(data);
|
||||
setVariables(data);
|
||||
setVariableInputFinish(true);
|
||||
})}
|
||||
>
|
||||
{'开始对话'}
|
||||
</Button>
|
||||
)}
|
||||
</Card>
|
||||
</Flex>
|
||||
<Box textAlign={'left'}>
|
||||
<Card order={2} mt={2} bg={'white'} w={'400px'} {...MessageCardStyle}>
|
||||
{variableModules.map((item) => (
|
||||
<Box key={item.id} mb={4}>
|
||||
<VariableLabel required={item.required}>{item.label}</VariableLabel>
|
||||
{item.type === VariableInputEnum.input && (
|
||||
<Input
|
||||
isDisabled={variableIsFinish}
|
||||
{...register(item.key, {
|
||||
required: item.required
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
{item.type === VariableInputEnum.select && (
|
||||
<MySelect
|
||||
width={'100%'}
|
||||
isDisabled={variableIsFinish}
|
||||
list={(item.enums || []).map((item) => ({
|
||||
label: item.value,
|
||||
value: item.value
|
||||
}))}
|
||||
{...register(item.key, {
|
||||
required: item.required
|
||||
})}
|
||||
value={getValues(item.key)}
|
||||
onchange={(e) => {
|
||||
setValue(item.key, e);
|
||||
setRefresh(!refresh);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
))}
|
||||
{!variableIsFinish && (
|
||||
<Button
|
||||
leftIcon={<MyIcon name={'chatFill'} w={'16px'} />}
|
||||
size={'sm'}
|
||||
maxW={'100px'}
|
||||
borderRadius={'lg'}
|
||||
onClick={handleSubmit((data) => {
|
||||
onUpdateVariable?.(data);
|
||||
setVariables(data);
|
||||
setVariableInputFinish(true);
|
||||
})}
|
||||
>
|
||||
{'开始对话'}
|
||||
</Button>
|
||||
)}
|
||||
</Card>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* chat history */}
|
||||
<Box id={'history'}>
|
||||
{chatHistory.map((item, index) => (
|
||||
<Flex
|
||||
position={'relative'}
|
||||
<Box
|
||||
key={item.dataId}
|
||||
flexDirection={'column'}
|
||||
alignItems={item.obj === 'Human' ? 'flex-end' : 'flex-start'}
|
||||
@@ -633,11 +622,7 @@ const ChatBox = (
|
||||
{item.obj === 'Human' && (
|
||||
<>
|
||||
<Flex w={'100%'} alignItems={'center'} justifyContent={'flex-end'}>
|
||||
<Flex
|
||||
{...controlContainerStyle(item.status)}
|
||||
justifyContent={'flex-end'}
|
||||
mr={3}
|
||||
>
|
||||
<Flex {...controlContainerStyle} justifyContent={'flex-end'} mr={3}>
|
||||
<MyTooltip label={t('common.Copy')}>
|
||||
<MyIcon
|
||||
{...controlIconStyle}
|
||||
@@ -678,7 +663,7 @@ const ChatBox = (
|
||||
</Flex>
|
||||
<ChatAvatar src={userAvatar} type={'Human'} />
|
||||
</Flex>
|
||||
<Box position={'relative'} maxW={messageCardMaxW} mt={['6px', 2]}>
|
||||
<Box mt={['6px', 2]} textAlign={'right'}>
|
||||
<Card
|
||||
className="markdown"
|
||||
whiteSpace={'pre-wrap'}
|
||||
@@ -695,7 +680,7 @@ const ChatBox = (
|
||||
<>
|
||||
<Flex w={'100%'} alignItems={'flex-end'}>
|
||||
<ChatAvatar src={appAvatar} type={'AI'} />
|
||||
<Flex {...controlContainerStyle(item.status)} ml={3}>
|
||||
<Flex {...controlContainerStyle} ml={3}>
|
||||
<MyTooltip label={'复制'}>
|
||||
<MyIcon
|
||||
{...controlIconStyle}
|
||||
@@ -841,7 +826,7 @@ const ChatBox = (
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
<Box position={'relative'} maxW={messageCardMaxW} mt={['6px', 2]}>
|
||||
<Box textAlign={'left'} mt={['6px', 2]}>
|
||||
<Card bg={'white'} {...MessageCardStyle}>
|
||||
<Markdown
|
||||
source={item.value}
|
||||
@@ -869,7 +854,7 @@ const ChatBox = (
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
@@ -14,7 +14,7 @@ export enum PageTypeEnum {
|
||||
}
|
||||
|
||||
export const BillSourceMap: Record<`${BillSourceEnum}`, string> = {
|
||||
[BillSourceEnum.fastgpt]: 'FastGPT 平台',
|
||||
[BillSourceEnum.fastgpt]: '在线使用',
|
||||
[BillSourceEnum.api]: 'Api',
|
||||
[BillSourceEnum.shareLink]: '免登录链接'
|
||||
};
|
||||
|
||||
@@ -251,7 +251,7 @@ const UserInfo = () => {
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
{feConfigs?.show_userDetail && (
|
||||
{feConfigs?.show_openai_account && (
|
||||
<>
|
||||
<Divider my={3} />
|
||||
|
||||
|
||||
@@ -6,8 +6,10 @@ import { useQuery } from '@tanstack/react-query';
|
||||
import { useRouter } from 'next/router';
|
||||
import { getErrText } from '@/utils/tools';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { formatPrice } from '@/utils/user';
|
||||
import Markdown from '@/components/Markdown';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import { vectorModelList, chatModelList, qaModel } from '@/store/static';
|
||||
|
||||
const PayModal = ({ onClose }: { onClose: () => void }) => {
|
||||
const router = useRouter();
|
||||
@@ -69,6 +71,7 @@ const PayModal = ({ onClose }: { onClose: () => void }) => {
|
||||
onClose();
|
||||
}}
|
||||
title={t('user.Pay')}
|
||||
isCentered
|
||||
showCloseBtn={!payId}
|
||||
>
|
||||
<ModalBody py={0}>
|
||||
@@ -100,11 +103,13 @@ const PayModal = ({ onClose }: { onClose: () => void }) => {
|
||||
source={`
|
||||
| 计费项 | 价格: 元/ 1K tokens(包含上下文)|
|
||||
| --- | --- |
|
||||
| 知识库 - 索引 | 0.002 |
|
||||
| FastAI4k - 对话 | 0.015 |
|
||||
| FastAI16k - 对话 | 0.03 |
|
||||
| FastAI-Plus - 对话 | 0.45 |
|
||||
| 文件QA拆分 | 0.03 |`}
|
||||
${vectorModelList
|
||||
.map((item) => `| 索引-${item.name} | ${formatPrice(item.price, 1000)} |`)
|
||||
.join('\n')}
|
||||
${chatModelList
|
||||
.map((item) => `| 对话-${item.name} | ${formatPrice(item.price, 1000)} |`)
|
||||
.join('\n')}
|
||||
| 文件QA拆分 | ${formatPrice(qaModel.price, 1000)} |`}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -31,7 +31,7 @@ enum TabEnum {
|
||||
|
||||
const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
||||
const { t } = useTranslation();
|
||||
const tabList = useRef([
|
||||
const tabList = [
|
||||
{
|
||||
icon: 'meLight',
|
||||
label: t('user.Personal Information'),
|
||||
@@ -67,7 +67,7 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
||||
label: t('user.Sign Out'),
|
||||
id: TabEnum.loginout
|
||||
}
|
||||
]);
|
||||
];
|
||||
|
||||
const { openConfirm, ConfirmModal } = useConfirm({
|
||||
content: '确认退出登录?'
|
||||
@@ -115,7 +115,7 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
||||
mx={'auto'}
|
||||
mt={2}
|
||||
w={'100%'}
|
||||
list={tabList.current}
|
||||
list={tabList}
|
||||
activeId={currentTab}
|
||||
onChange={setCurrentTab}
|
||||
/>
|
||||
@@ -125,7 +125,7 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
||||
<Tabs
|
||||
m={'auto'}
|
||||
size={isPc ? 'md' : 'sm'}
|
||||
list={tabList.current.map((item) => ({
|
||||
list={tabList.map((item) => ({
|
||||
id: item.id,
|
||||
label: item.label
|
||||
}))}
|
||||
|
||||
27
client/src/pages/api/admin/initv442.ts
Normal file
27
client/src/pages/api/admin/initv442.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import { connectToDatabase, Bill } from '@/service/mongo';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
await authUser({ req, authRoot: true });
|
||||
|
||||
try {
|
||||
await Bill.collection.dropIndex('time_1');
|
||||
} catch (error) {}
|
||||
try {
|
||||
await Bill.collection.createIndex({ time: 1 }, { expireAfterSeconds: 90 * 24 * 60 * 60 });
|
||||
} catch (error) {}
|
||||
|
||||
jsonRes(res, {
|
||||
data: {}
|
||||
});
|
||||
} catch (error) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { connectToDatabase, TrainingData } from '@/service/mongo';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import { GridFSStorage } from '@/service/lib/gridfs';
|
||||
import { PgClient } from '@/service/pg';
|
||||
@@ -21,6 +21,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
// 凭证校验
|
||||
const { userId } = await authUser({ req, authToken: true });
|
||||
|
||||
// other data. Delete only vector data
|
||||
if (fileId === OtherFileId) {
|
||||
await PgClient.delete(PgDatasetTableName, {
|
||||
where: [
|
||||
@@ -31,6 +32,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
]
|
||||
});
|
||||
} else {
|
||||
// auth file
|
||||
const gridFs = new GridFSStorage('dataset', userId);
|
||||
const bucket = gridFs.GridFSBucket();
|
||||
|
||||
@@ -40,6 +42,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
await PgClient.delete(PgDatasetTableName, {
|
||||
where: [['user_id', userId], 'AND', ['kb_id', kbId], 'AND', ['file_id', fileId]]
|
||||
});
|
||||
// delete all training data
|
||||
await TrainingData.deleteMany({
|
||||
userId,
|
||||
file_id: fileId
|
||||
});
|
||||
|
||||
// delete file
|
||||
await bucket.delete(new Types.ObjectId(fileId));
|
||||
|
||||
@@ -18,7 +18,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
throw new Error('最多 10 组 API 秘钥');
|
||||
}
|
||||
|
||||
const apiKey = `fastgpt-${nanoid()}`;
|
||||
const apiKey = `${global.systemEnv?.openapiPrefix || 'fastgpt'}-${nanoid()}`;
|
||||
|
||||
await OpenApi.create({
|
||||
userId,
|
||||
|
||||
@@ -116,7 +116,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
appId,
|
||||
userId
|
||||
}),
|
||||
getChatHistory({ chatId, userId })
|
||||
getChatHistory({ chatId, appId, userId })
|
||||
]);
|
||||
|
||||
const isOwner = !shareId && userId === String(app.userId);
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Types } from 'mongoose';
|
||||
import type { ChatItemType } from '@/types/chat';
|
||||
|
||||
export type Props = {
|
||||
appId?: string;
|
||||
chatId?: string;
|
||||
limit?: number;
|
||||
};
|
||||
@@ -36,9 +37,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
export async function getChatHistory({
|
||||
chatId,
|
||||
userId,
|
||||
appId,
|
||||
limit = 30
|
||||
}: Props & { userId: string }): Promise<Response> {
|
||||
if (!chatId) {
|
||||
if (!chatId || !appId) {
|
||||
return { history: [] };
|
||||
}
|
||||
|
||||
@@ -46,6 +48,7 @@ export async function getChatHistory({
|
||||
{
|
||||
$match: {
|
||||
chatId,
|
||||
appId: new Types.ObjectId(appId),
|
||||
userId: new Types.ObjectId(userId)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2,9 +2,10 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase, User } from '@/service/mongo';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import { PgClient } from '@/service/pg';
|
||||
import { PgDatasetTableName } from '@/constants/plugin';
|
||||
import { findAllChildrenIds } from '../delete';
|
||||
import QueryStream from 'pg-query-stream';
|
||||
import Papa from 'papaparse';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
@@ -12,7 +13,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
kbId: string;
|
||||
};
|
||||
|
||||
if (!kbId) {
|
||||
if (!kbId || !global.pgClient) {
|
||||
throw new Error('缺少参数');
|
||||
}
|
||||
|
||||
@@ -22,7 +23,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
const { userId } = await authUser({ req, authToken: true });
|
||||
|
||||
const exportIds = [kbId, ...(await findAllChildrenIds(kbId))];
|
||||
console.log(exportIds);
|
||||
|
||||
const thirtyMinutesAgo = new Date(
|
||||
Date.now() - (global.feConfigs?.limit?.exportLimitMinutes || 0) * 60 * 1000
|
||||
@@ -45,37 +45,50 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
throw new Error(`上次导出未到 ${minutes},每 ${minutes}仅可导出一次。`);
|
||||
}
|
||||
|
||||
const where: any = [
|
||||
['user_id', userId],
|
||||
'AND',
|
||||
`kb_id IN (${exportIds.map((id) => `'${id}'`).join(',')})`
|
||||
];
|
||||
// 从 pg 中获取所有数据
|
||||
const pgData = await PgClient.select<{ q: string; a: string; source: string }>(
|
||||
PgDatasetTableName,
|
||||
{
|
||||
where,
|
||||
fields: ['q', 'a', 'source'],
|
||||
order: [{ field: 'id', mode: 'DESC' }],
|
||||
limit: 1000000
|
||||
// connect pg
|
||||
global.pgClient.connect((err, client, done) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
res.end('Error connecting to database');
|
||||
return;
|
||||
}
|
||||
);
|
||||
// create pg select stream
|
||||
const query = new QueryStream(
|
||||
`SELECT q, a, source FROM ${PgDatasetTableName} where user_id='${userId}' AND kb_id IN (${exportIds
|
||||
.map((id) => `'${id}'`)
|
||||
.join(',')})`
|
||||
);
|
||||
const stream = client.query(query);
|
||||
|
||||
const data: [string, string, string][] = pgData.rows.map((item) => [
|
||||
item.q.replace(/\n/g, '\\n'),
|
||||
item.a.replace(/\n/g, '\\n'),
|
||||
item.source
|
||||
]);
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=dataset.csv');
|
||||
res.setHeader('Content-Type', 'text/csv');
|
||||
|
||||
// update export time
|
||||
await User.findByIdAndUpdate(userId, {
|
||||
'limit.exportKbTime': new Date()
|
||||
});
|
||||
res.write('index,content,source');
|
||||
|
||||
jsonRes(res, {
|
||||
data
|
||||
// parse data every row
|
||||
stream.on('data', (row: { q: string; a: string; source?: string }) => {
|
||||
const csv = Papa.unparse([row], { header: false });
|
||||
res.write(`\n${csv}`);
|
||||
});
|
||||
stream.on('end', async () => {
|
||||
try {
|
||||
// update export time
|
||||
await User.findByIdAndUpdate(userId, {
|
||||
'limit.exportKbTime': new Date()
|
||||
});
|
||||
} catch (error) {}
|
||||
|
||||
// close response
|
||||
done();
|
||||
res.end();
|
||||
});
|
||||
stream.on('error', (err) => {
|
||||
done(err);
|
||||
res.end('Error exporting data');
|
||||
});
|
||||
});
|
||||
} catch (err) {
|
||||
res.status(500);
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
@@ -4,6 +4,7 @@ import { useCopyData } from '@/utils/tools';
|
||||
import dynamic from 'next/dynamic';
|
||||
import MyIcon from '@/components/Icon';
|
||||
import { useGlobalStore } from '@/store/global';
|
||||
import { feConfigs } from '@/store/static';
|
||||
|
||||
const APIKeyModal = dynamic(() => import('@/components/APIKeyModal'), {
|
||||
ssr: false
|
||||
@@ -77,7 +78,10 @@ const API = ({ appId }: { appId: string }) => {
|
||||
width: '100%',
|
||||
height: '100%'
|
||||
}}
|
||||
src="https://kjqvjse66l.feishu.cn/docx/DmLedTWtUoNGX8xui9ocdUEjnNh"
|
||||
src={
|
||||
feConfigs?.openAPIUrl ||
|
||||
'https://kjqvjse66l.feishu.cn/docx/DmLedTWtUoNGX8xui9ocdUEjnNh'
|
||||
}
|
||||
frameBorder="0"
|
||||
onLoad={() => setIsLoaded(true)}
|
||||
onError={() => setIsLoaded(true)}
|
||||
|
||||
@@ -27,6 +27,7 @@ import { KbTypeEnum } from '@/constants/kb';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useDatasetStore } from '@/store/dataset';
|
||||
import { feConfigs } from '@/store/static';
|
||||
|
||||
export type KbParamsType = {
|
||||
searchSimilarity: number;
|
||||
@@ -345,9 +346,7 @@ export const KbParamsModal = ({
|
||||
<Textarea
|
||||
rows={5}
|
||||
maxLength={500}
|
||||
placeholder={
|
||||
'若填写该内容,没有搜索到对应内容时,将直接回复填写的内容。\n为了连贯上下文,FastGPT 会取部分上一个聊天的搜索记录作为补充,因此在连续对话时,该功能可能会失效。'
|
||||
}
|
||||
placeholder={`若填写该内容,没有搜索到对应内容时,将直接回复填写的内容。\n为了连贯上下文,${feConfigs?.systemTitle} 会取部分上一个聊天的搜索记录作为补充,因此在连续对话时,该功能可能会失效。`}
|
||||
{...register('searchEmptyText')}
|
||||
></Textarea>
|
||||
</Box>
|
||||
|
||||
@@ -35,7 +35,8 @@ enum TabEnum {
|
||||
'adEdit' = 'adEdit',
|
||||
'outLink' = 'outLink',
|
||||
'logs' = 'logs',
|
||||
'API' = 'API'
|
||||
'API' = 'API',
|
||||
'startChat' = 'startChat'
|
||||
}
|
||||
|
||||
const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
||||
@@ -64,7 +65,7 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
||||
{ label: '外部使用', id: TabEnum.outLink, icon: 'shareLight' },
|
||||
{ label: 'API访问', id: TabEnum.API, icon: 'apiLight' },
|
||||
{ label: '对话日志', id: TabEnum.logs, icon: 'logsLight' },
|
||||
{ label: '立即对话', id: 'startChat', icon: 'chat' }
|
||||
{ label: '立即对话', id: TabEnum.startChat, icon: 'chat' }
|
||||
],
|
||||
[]
|
||||
);
|
||||
|
||||
@@ -49,7 +49,7 @@ const ChatHeader = ({
|
||||
<MyIcon name={'history'} w={'14px'} />
|
||||
<Box ml={1}>{history.length === 0 ? '新的对话' : `${history.length}条记录`}</Box>
|
||||
</Tag>
|
||||
{!!chatModels && (
|
||||
{!!chatModels && chatModels.length > 0 && (
|
||||
<Tag ml={2} colorSchema={'green'}>
|
||||
<MyIcon name={'chatModelTag'} w={'14px'} />
|
||||
<Box ml={1}>{chatModels.join(',')}</Box>
|
||||
|
||||
@@ -217,7 +217,7 @@ const CsvImport = ({ kbId }: { kbId: string }) => {
|
||||
/>
|
||||
</Flex>
|
||||
<Box px={4} fontSize={'sm'} whiteSpace={'pre-wrap'} wordBreak={'break-all'}>
|
||||
{`q: ${item.q}\na: ${item.a}`}
|
||||
{`${item.q}\n${item.a}`}
|
||||
</Box>
|
||||
</Box>
|
||||
))
|
||||
|
||||
@@ -25,7 +25,7 @@ const UrlFetchModal = dynamic(() => import('./UrlFetchModal'));
|
||||
const CreateFileModal = dynamic(() => import('./CreateFileModal'));
|
||||
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
|
||||
const csvTemplate = `question,answer\n"什么是 laf","laf 是一个云函数开发平台……"\n"什么是 sealos","Sealos 是以 kubernetes 为内核的云操作系统发行版,可以……"`;
|
||||
const csvTemplate = `index,content,source\n"被索引的内容","对应的答案。CSV 中请注意内容不能包含双引号,双引号是列分割符号","来源,可选。"\n"什么是 laf","laf 是一个云函数开发平台……",""\n"什么是 sealos","Sealos 是以 kubernetes 为内核的云操作系统发行版,可以……",""`;
|
||||
|
||||
export type FileItemType = {
|
||||
id: string;
|
||||
@@ -149,8 +149,8 @@ const FileSelect = ({
|
||||
/* csv file */
|
||||
if (extension === 'csv') {
|
||||
const { header, data } = await readCsvContent(file);
|
||||
if (header[0] !== 'question' || header[1] !== 'answer') {
|
||||
throw new Error('csv 文件格式有误,请确保 question 和 answer 两列');
|
||||
if (header[0] !== 'index' || header[1] !== 'content') {
|
||||
throw new Error('csv 文件格式有误,请确保 index 和 content 两列');
|
||||
}
|
||||
const fileItem: FileItemType = {
|
||||
id: filesId[0],
|
||||
@@ -158,12 +158,14 @@ const FileSelect = ({
|
||||
icon,
|
||||
tokens: 0,
|
||||
text: '',
|
||||
chunks: data.map((item) => ({
|
||||
q: item[0],
|
||||
a: item[1],
|
||||
source: item[2] || file.name,
|
||||
file_id: filesId[0]
|
||||
}))
|
||||
chunks: data
|
||||
.filter((item) => item[0])
|
||||
.map((item) => ({
|
||||
q: item[0] || '',
|
||||
a: item[1] || '',
|
||||
source: item[2] || file.name || '',
|
||||
file_id: filesId[0]
|
||||
}))
|
||||
};
|
||||
|
||||
chunkFiles.unshift(fileItem);
|
||||
|
||||
@@ -15,7 +15,7 @@ import PageContainer from '@/components/PageContainer';
|
||||
import { useConfirm } from '@/hooks/useConfirm';
|
||||
import { AddIcon } from '@chakra-ui/icons';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { delKbById, getExportDataList, getKbPaths, putKbById } from '@/api/plugins/kb';
|
||||
import { delKbById, exportDataset, getKbPaths, putKbById } from '@/api/plugins/kb';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Avatar from '@/components/Avatar';
|
||||
import MyIcon from '@/components/Icon';
|
||||
@@ -27,8 +27,6 @@ import MyMenu from '@/components/MyMenu';
|
||||
import { useRequest } from '@/hooks/useRequest';
|
||||
import { useGlobalStore } from '@/store/global';
|
||||
import { useEditTitle } from '@/hooks/useEditTitle';
|
||||
import Papa from 'papaparse';
|
||||
import { fileDownload } from '@/utils/file';
|
||||
import { feConfigs } from '@/store/static';
|
||||
|
||||
const CreateModal = dynamic(() => import('./component/CreateModal'), { ssr: false });
|
||||
@@ -90,19 +88,7 @@ const Kb = () => {
|
||||
const { mutate: onclickExport } = useRequest({
|
||||
mutationFn: (kbId: string) => {
|
||||
setLoading(true);
|
||||
return getExportDataList({ kbId });
|
||||
},
|
||||
onSuccess(res) {
|
||||
const text = Papa.unparse({
|
||||
fields: ['question', 'answer', 'source'],
|
||||
data: res
|
||||
});
|
||||
|
||||
fileDownload({
|
||||
text,
|
||||
type: 'text/csv',
|
||||
filename: 'dataset.csv'
|
||||
});
|
||||
return exportDataset({ kbId });
|
||||
},
|
||||
onSettled() {
|
||||
setLoading(false);
|
||||
|
||||
@@ -65,13 +65,15 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
status: 'success'
|
||||
});
|
||||
// auto register template app
|
||||
appTemplates.forEach((template) => {
|
||||
postCreateApp({
|
||||
avatar: template.avatar,
|
||||
name: template.name,
|
||||
modules: template.modules
|
||||
setTimeout(() => {
|
||||
appTemplates.forEach((template) => {
|
||||
postCreateApp({
|
||||
avatar: template.avatar,
|
||||
name: template.name,
|
||||
modules: template.modules
|
||||
});
|
||||
});
|
||||
});
|
||||
}, 100);
|
||||
} catch (error: any) {
|
||||
toast({
|
||||
title: error.message || '注册异常',
|
||||
|
||||
@@ -36,7 +36,7 @@ const Login = () => {
|
||||
setToken(res.token);
|
||||
setTimeout(() => {
|
||||
router.push(lastRoute ? decodeURIComponent(lastRoute) : '/app/list');
|
||||
}, 100);
|
||||
}, 300);
|
||||
},
|
||||
[lastRoute, router, setLastChatId, setLastChatAppId, setUserInfo]
|
||||
);
|
||||
|
||||
@@ -55,7 +55,7 @@ const BillSchema = new Schema({
|
||||
|
||||
try {
|
||||
BillSchema.index({ userId: 1 });
|
||||
BillSchema.index({ time: 1 }, { expireAfterSeconds: 90 * 24 * 60 });
|
||||
BillSchema.index({ time: 1 }, { expireAfterSeconds: 90 * 24 * 60 * 60 });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
4
client/src/types/index.d.ts
vendored
4
client/src/types/index.d.ts
vendored
@@ -23,9 +23,10 @@ export type FeConfigsType = {
|
||||
show_contact?: boolean;
|
||||
show_git?: boolean;
|
||||
show_doc?: boolean;
|
||||
show_openai_account?: boolean;
|
||||
openAPIUrl?: string;
|
||||
systemTitle?: string;
|
||||
authorText?: string;
|
||||
beianText?: string;
|
||||
googleClientVerKey?: string;
|
||||
oauth?: {
|
||||
github?: string;
|
||||
@@ -38,6 +39,7 @@ export type FeConfigsType = {
|
||||
};
|
||||
export type SystemEnvType = {
|
||||
pluginBaseUrl?: string;
|
||||
openapiPrefix?: string;
|
||||
vectorMaxProcess: number;
|
||||
qaMaxProcess: number;
|
||||
pgIvfflatProbe: number;
|
||||
|
||||
Reference in New Issue
Block a user