Update docs and response tag in share page (#694)

* perf: chunk index show

* share response

* perf: vector query

* web printFinger

* remove log

* fix: bucket name

* perf: training schema

* perf: sort index
This commit is contained in:
Archer
2024-01-05 18:02:37 +08:00
committed by GitHub
parent 331d18c88f
commit 3f088bce6a
23 changed files with 242 additions and 181 deletions

View File

@@ -33,7 +33,7 @@ FastGPT 商业版是基于 FastGPT 开源版的增强版本,增加了一些独
## 商业版软件价格 ## 商业版软件价格
FastGPT 商业版软件根据不同的部署方式,分为 4 类收费模式。下面列举各种部署方式一些常规内容,如仍有问题,可[联系咨询](https://fael3z0zfze.feishu.cn/share/base/form/shrcnRxj3utrzjywsom96Px4sud) FastGPT 商业版软件根据不同的部署方式,分为 3 类收费模式。下面列举各种部署方式一些常规内容,如仍有问题,可[联系咨询](https://fael3z0zfze.feishu.cn/share/base/form/shrcnRxj3utrzjywsom96Px4sud)
**共有服务** **共有服务**
@@ -46,8 +46,7 @@ FastGPT 商业版软件根据不同的部署方式,分为 4 类收费模式。
{{< table "table-hover table-striped-columns" >}} {{< table "table-hover table-striped-columns" >}}
| 部署方式 | 特有服务 | 上线时长 | 价格 | | 部署方式 | 特有服务 | 上线时长 | 价格 |
| ---- | ---- | ---- | ---- | | ---- | ---- | ---- | ---- |
| Sealos公有云部署 | 1. 6个版本的升级服务<br>2. 赠送 8000 元 Sealos 云资源额度。 | 半天 | 10000元/年 | | Sealos全托管 | 1. 有效期内免费升级<br>2. 免运维服务&数据库。 | 半天 | 3000元起/月3个月起<br><br>30000元/年 |
| Sealos全托管 | 1. 有效期内免费升级。<br>2. 免运维服务&数据库。<br>3. 赠送 10000 元 Sealos 云资源额度。 | 半天 | 3000元起/月3个月起<br><br>30000元起/年 |
| 自有服务器-单机版 | 1. 6个版本的升级服务。 | 14天内 | 60000元/套(不限时长) | | 自有服务器-单机版 | 1. 6个版本的升级服务。 | 14天内 | 60000元/套(不限时长) |
| 自有服务器-Sealos版 | 1. 6个版本的升级服务。 | 14天内 | 150000元/套(不限时长)| | 自有服务器-Sealos版 | 1. 6个版本的升级服务。 | 14天内 | 150000元/套(不限时长)|
{{< /table >}} {{< /table >}}

View File

@@ -25,7 +25,7 @@ FastGPT 的 API Key **有 2 类**,一类是全局通用的 key (无法直接
| 通用key | 应用特定 key | | 通用key | 应用特定 key |
| --------------------- | --------------------- | | --------------------- | --------------------- |
| ![](/imgs/fastgpt-api2.png) | ![](/imgs/fastgpt-api.png) | | ![](/imgs/fastgpt-api2.jpg) | ![](/imgs/fastgpt-api.jpg) |
## 基本配置 ## 基本配置

View File

@@ -1,5 +1,10 @@
export enum BucketNameEnum { export enum BucketNameEnum {
dataset = 'dataset' dataset = 'dataset'
} }
export const bucketNameMap = {
[BucketNameEnum.dataset]: {
label: 'common.file.bucket.dataset'
}
};
export const FileBaseUrl = '/api/common/file/read'; export const FileBaseUrl = '/api/common/file/read';

View File

@@ -20,13 +20,13 @@ export const readPdfFile = async ({ pdf }: { pdf: string | URL | ArrayBuffer })
const viewport = page.getViewport({ scale: 1 }); const viewport = page.getViewport({ scale: 1 });
const pageHeight = viewport.height; const pageHeight = viewport.height;
const headerThreshold = pageHeight * 0.07; // 假设页头在页面顶部5%的区域内 const headerThreshold = pageHeight * 0.95;
const footerThreshold = pageHeight * 0.93; // 假设页脚在页面底部5%的区域内 const footerThreshold = pageHeight * 0.05;
const pageTexts: TokenType[] = tokenizedText.items.filter((token: TokenType) => { const pageTexts: TokenType[] = tokenizedText.items.filter((token: TokenType) => {
return ( return (
!token.transform || !token.transform ||
(token.transform[5] > headerThreshold && token.transform[5] < footerThreshold) (token.transform[5] < headerThreshold && token.transform[5] > footerThreshold)
); );
}); });

View File

@@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { customAlphabet } from 'nanoid'; import { customAlphabet } from 'nanoid';
import multer from 'multer'; import multer from 'multer';
import path from 'path'; import path from 'path';
import { BucketNameEnum } from '@fastgpt/global/common/file/constants'; import { BucketNameEnum, bucketNameMap } from '@fastgpt/global/common/file/constants';
import fs from 'fs'; import fs from 'fs';
const nanoid = customAlphabet('1234567890abcdef', 12); const nanoid = customAlphabet('1234567890abcdef', 12);
@@ -45,6 +45,12 @@ export function getUploadModel({ maxSize = 500 }: { maxSize?: number }) {
return reject(error); return reject(error);
} }
// check bucket name
const bucketName = req.body?.bucketName as `${BucketNameEnum}`;
if (bucketName && !bucketNameMap[bucketName]) {
return reject('BucketName is invalid');
}
resolve({ resolve({
...req.body, ...req.body,
files: files:

View File

@@ -48,7 +48,7 @@ export const updateDatasetDataVector = async ({
// get vector // get vector
const { vectors, tokens } = await getVectorsByText({ const { vectors, tokens } = await getVectorsByText({
model, model,
input: [query] input: query
}); });
await getVectorObj().update({ await getVectorObj().update({

View File

@@ -2,7 +2,7 @@ import { getAIApi } from '../config';
export type GetVectorProps = { export type GetVectorProps = {
model: string; model: string;
input: string | string[]; input: string;
}; };
// text to vector // text to vector
@@ -10,24 +10,13 @@ export async function getVectorsByText({
model = 'text-embedding-ada-002', model = 'text-embedding-ada-002',
input input
}: GetVectorProps) { }: GetVectorProps) {
if (typeof input === 'string' && !input) { if (!input) {
return Promise.reject({ return Promise.reject({
code: 500, code: 500,
message: 'input is empty' message: 'input is empty'
}); });
} else if (Array.isArray(input)) {
for (let i = 0; i < input.length; i++) {
if (!input[i]) {
return Promise.reject({
code: 500,
message: 'input array is empty'
});
}
}
}
if (typeof input === 'string') {
input = [input];
} }
try { try {
// 获取 chatAPI // 获取 chatAPI
const ai = getAIApi(); const ai = getAIApi();
@@ -36,7 +25,7 @@ export async function getVectorsByText({
const result = await ai.embeddings const result = await ai.embeddings
.create({ .create({
model, model,
input input: [input]
}) })
.then(async (res) => { .then(async (res) => {
if (!res.data) { if (!res.data) {
@@ -47,6 +36,7 @@ export async function getVectorsByText({
// @ts-ignore // @ts-ignore
return Promise.reject(res.data?.err?.message || 'Embedding API Error'); return Promise.reject(res.data?.err?.message || 'Embedding API Error');
} }
return { return {
tokens: res.usage.total_tokens || 0, tokens: res.usage.total_tokens || 0,
vectors: await Promise.all(res.data.map((item) => unityDimensional(item.embedding))) vectors: await Promise.all(res.data.map((item) => unityDimensional(item.embedding)))

View File

@@ -102,6 +102,7 @@ const TrainingDataSchema = new Schema({
}); });
try { try {
TrainingDataSchema.index({ weight: -1 });
TrainingDataSchema.index({ lockTime: 1 }); TrainingDataSchema.index({ lockTime: 1 });
TrainingDataSchema.index({ datasetId: 1 }); TrainingDataSchema.index({ datasetId: 1 });
TrainingDataSchema.index({ collectionId: 1 }); TrainingDataSchema.index({ collectionId: 1 });

View File

@@ -0,0 +1,9 @@
import FingerprintJS from '@fingerprintjs/fingerprintjs';
const fpPromise = FingerprintJS.load();
export const getUserFingerprint = async () => {
const fp = await fpPromise;
const result = await fp.get();
console.log(result.visitorId);
};

View File

@@ -2,9 +2,6 @@
"name": "@fastgpt/web", "name": "@fastgpt/web",
"version": "1.0.0", "version": "1.0.0",
"dependencies": { "dependencies": {
"@fastgpt/global": "workspace:*",
"joplin-turndown-plugin-gfm": "^1.0.12",
"turndown": "^7.1.2",
"@chakra-ui/anatomy": "^2.2.1", "@chakra-ui/anatomy": "^2.2.1",
"@chakra-ui/icons": "^2.1.1", "@chakra-ui/icons": "^2.1.1",
"@chakra-ui/next-js": "^2.1.5", "@chakra-ui/next-js": "^2.1.5",
@@ -13,16 +10,20 @@
"@chakra-ui/system": "^2.6.1", "@chakra-ui/system": "^2.6.1",
"@emotion/react": "^11.11.1", "@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0", "@emotion/styled": "^11.11.0",
"@fastgpt/global": "workspace:*",
"@fingerprintjs/fingerprintjs": "^4.2.1",
"@monaco-editor/react": "^4.6.0", "@monaco-editor/react": "^4.6.0",
"i18next": "^22.5.1",
"joplin-turndown-plugin-gfm": "^1.0.12",
"next-i18next": "^13.3.0",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"i18next": "^22.5.1", "react-i18next": "^12.3.1",
"next-i18next": "^13.3.0", "turndown": "^7.1.2"
"react-i18next": "^12.3.1"
}, },
"devDependencies": { "devDependencies": {
"@types/turndown": "^5.0.4",
"@types/react": "18.2.0", "@types/react": "18.2.0",
"@types/react-dom": "18.2.0" "@types/react-dom": "18.2.0",
"@types/turndown": "^5.0.4"
} }
} }

11
pnpm-lock.yaml generated
View File

@@ -163,6 +163,9 @@ importers:
'@fastgpt/global': '@fastgpt/global':
specifier: workspace:* specifier: workspace:*
version: link:../global version: link:../global
'@fingerprintjs/fingerprintjs':
specifier: ^4.2.1
version: registry.npmmirror.com/@fingerprintjs/fingerprintjs@4.2.1
'@monaco-editor/react': '@monaco-editor/react':
specifier: ^4.6.0 specifier: ^4.6.0
version: registry.npmmirror.com/@monaco-editor/react@4.6.0(monaco-editor@0.45.0)(react-dom@18.2.0)(react@18.2.0) version: registry.npmmirror.com/@monaco-editor/react@4.6.0(monaco-editor@0.45.0)(react-dom@18.2.0)(react@18.2.0)
@@ -3836,6 +3839,14 @@ packages:
- supports-color - supports-color
dev: true dev: true
registry.npmmirror.com/@fingerprintjs/fingerprintjs@4.2.1:
resolution: {integrity: sha512-uW+GVUNTgCXbVPEbgnbf5Aor22e1dyYR0JRwdUiZBaikfxr7KlhV9y0aahA1FB99fEeQVvhCEvTcPIFSYTy9Pw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@fingerprintjs/fingerprintjs/-/fingerprintjs-4.2.1.tgz}
name: '@fingerprintjs/fingerprintjs'
version: 4.2.1
dependencies:
tslib: registry.npmmirror.com/tslib@2.6.2
dev: false
registry.npmmirror.com/@humanwhocodes/config-array@0.11.13: registry.npmmirror.com/@humanwhocodes/config-array@0.11.13:
resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@humanwhocodes/config-array/-/config-array-0.11.13.tgz} resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@humanwhocodes/config-array/-/config-array-0.11.13.tgz}
name: '@humanwhocodes/config-array' name: '@humanwhocodes/config-array'

View File

@@ -522,9 +522,13 @@
}, },
"score": { "score": {
"embedding": "Embedding", "embedding": "Embedding",
"embedding desc": "",
"fullText": "Full text", "fullText": "Full text",
"fullText desc": "",
"reRank": "ReRank", "reRank": "ReRank",
"rrf": "RRF Merge" "reRank desc": "",
"rrf": "RRF Merge",
"rrf desc": ""
}, },
"search mode": "Search Mode" "search mode": "Search Mode"
}, },
@@ -874,8 +878,6 @@
"QPM": "QPM", "QPM": "QPM",
"QPM Tips": "The maximum number of queries per IP address per minute", "QPM Tips": "The maximum number of queries per IP address per minute",
"QPM is empty": "QPM is empty", "QPM is empty": "QPM is empty",
"Response Detail": "Quote",
"Response Detail tips": "Whether detailed data such as references to be returned",
"token auth": "Token Auth", "token auth": "Token Auth",
"token auth Tips": "Identity verification server address. If this value is set, the server will be specified to send a request for identity verification before each session", "token auth Tips": "Identity verification server address. If this value is set, the server will be specified to send a request for identity verification before each session",
"token auth use cases": "Review the authentication instructions" "token auth use cases": "Review the authentication instructions"
@@ -903,6 +905,12 @@
"Update Your Plugin": "Update Plugin" "Update Your Plugin": "Update Plugin"
}, },
"support": { "support": {
"outlink": {
"share": {
"Response Quote": "Show Quote",
"Response Quote tips": "The referenced content is returned in the share link, but the user is not allowed to download the original document."
}
},
"user": { "user": {
"Price": "Price", "Price": "Price",
"auth": { "auth": {

View File

@@ -878,8 +878,6 @@
"QPM": "", "QPM": "",
"QPM Tips": "每个 IP 每分钟最多提问多少次", "QPM Tips": "每个 IP 每分钟最多提问多少次",
"QPM is empty": "QPM 不能为空", "QPM is empty": "QPM 不能为空",
"Response Detail": "返回详情",
"Response Detail tips": "是否需要返回详情(引用内容,调用时间等,不会返回预设提示词和完整上下文)",
"token auth": "身份验证", "token auth": "身份验证",
"token auth Tips": "身份校验服务器地址,如填写该值,每次对话前都会向指定服务器发送一个请求,进行身份校验", "token auth Tips": "身份校验服务器地址,如填写该值,每次对话前都会向指定服务器发送一个请求,进行身份校验",
"token auth use cases": "查看身份验证使用说明" "token auth use cases": "查看身份验证使用说明"
@@ -907,6 +905,12 @@
"Update Your Plugin": "更新插件" "Update Your Plugin": "更新插件"
}, },
"support": { "support": {
"outlink": {
"share": {
"Response Quote": "返回引用",
"Response Quote tips": "在分享链接中返回引用内容,但不会允许用户下载原文档"
}
},
"user": { "user": {
"Price": "计费标准", "Price": "计费标准",
"auth": { "auth": {

View File

@@ -133,72 +133,69 @@ const ResponseTags = ({
</Flex> </Flex>
</> </>
)} )}
<Flex alignItems={'center'} mt={3} flexWrap={'wrap'}> {!isShare && (
{quoteList.length > 0 && ( <Flex alignItems={'center'} mt={3} flexWrap={'wrap'}>
<MyTooltip label="查看引用"> {quoteList.length > 0 && (
<Tag <MyTooltip label="查看引用">
colorSchema="blue" <Tag
cursor={'pointer'} colorSchema="blue"
{...TagStyles} cursor={'pointer'}
onClick={() => setQuoteModalData({ rawSearch: quoteList })} {...TagStyles}
> onClick={() => setQuoteModalData({ rawSearch: quoteList })}
{quoteList.length} >
{quoteList.length}
</Tag>
</MyTooltip>
)}
{chatAccount === 1 && (
<>
{historyPreview.length > 0 && (
<MyTooltip label={'点击查看完整对话记录'}>
<Tag
colorSchema="green"
cursor={'pointer'}
{...TagStyles}
onClick={() => setContextModalData(historyPreview)}
>
{historyPreview.length}
</Tag>
</MyTooltip>
)}
</>
)}
{chatAccount > 1 && (
<Tag colorSchema="blue" {...TagStyles}>
AI
</Tag>
)}
{isPc && runningTime > 0 && (
<MyTooltip label={'模块运行时间和'}>
<Tag colorSchema="purple" cursor={'default'} {...TagStyles}>
{runningTime}s
</Tag>
</MyTooltip>
)}
<MyTooltip label={t('core.chat.response.Read complete response tips')}>
<Tag colorSchema="gray" cursor={'pointer'} {...TagStyles} onClick={onOpenWholeModal}>
{t('core.chat.response.Read complete response')}
</Tag> </Tag>
</MyTooltip> </MyTooltip>
)} </Flex>
{chatAccount === 1 && ( )}
<> {!!quoteModalData && (
{historyPreview.length > 0 && ( <QuoteModal
<MyTooltip label={'点击查看完整对话记录'}> {...quoteModalData}
<Tag isShare={isShare}
colorSchema="green" onClose={() => setQuoteModalData(undefined)}
cursor={'pointer'} />
{...TagStyles} )}
onClick={() => setContextModalData(historyPreview)} {!!contextModalData && (
> <ContextModal context={contextModalData} onClose={() => setContextModalData(undefined)} />
{historyPreview.length} )}
</Tag> {isOpenWholeModal && (
</MyTooltip> <WholeResponseModal response={responseData} isShare={isShare} onClose={onCloseWholeModal} />
)} )}
</>
)}
{chatAccount > 1 && (
<Tag colorSchema="blue" {...TagStyles}>
AI
</Tag>
)}
{isPc && runningTime > 0 && (
<MyTooltip label={'模块运行时间和'}>
<Tag colorSchema="purple" cursor={'default'} {...TagStyles}>
{runningTime}s
</Tag>
</MyTooltip>
)}
<MyTooltip label={t('core.chat.response.Read complete response tips')}>
<Tag colorSchema="gray" cursor={'pointer'} {...TagStyles} onClick={onOpenWholeModal}>
{t('core.chat.response.Read complete response')}
</Tag>
</MyTooltip>
{!!quoteModalData && (
<QuoteModal
{...quoteModalData}
isShare={isShare}
onClose={() => setQuoteModalData(undefined)}
/>
)}
{!!contextModalData && (
<ContextModal context={contextModalData} onClose={() => setContextModalData(undefined)} />
)}
{isOpenWholeModal && (
<WholeResponseModal
response={responseData}
isShare={isShare}
onClose={onCloseWholeModal}
/>
)}
</Flex>
</> </>
); );
}; };

View File

@@ -126,63 +126,88 @@ const QuoteItem = ({
> >
<Flex alignItems={'center'} mb={3}> <Flex alignItems={'center'} mb={3}>
{score?.primaryScore && ( {score?.primaryScore && (
<MyTooltip label={t(SearchScoreTypeMap[score.primaryScore.type]?.desc)}> <>
<Flex {canViewSource ? (
px={'12px'} <MyTooltip label={t(SearchScoreTypeMap[score.primaryScore.type]?.desc)}>
py={'5px'} <Flex
mr={4} px={'12px'}
borderRadius={'md'} py={'5px'}
color={'primary.700'} mr={4}
bg={'primary.50'} borderRadius={'md'}
borderWidth={'1px'} color={'primary.700'}
borderColor={'primary.200'} bg={'primary.50'}
alignItems={'center'}
fontSize={'sm'}
>
<Box>#{score.primaryScore.index + 1}</Box>
<Box borderRightColor={'primary.700'} borderRightWidth={'1px'} h={'14px'} mx={2} />
<Box>
{t(SearchScoreTypeMap[score.primaryScore.type]?.label)}
{SearchScoreTypeMap[score.primaryScore.type]?.showScore
? ` ${score.primaryScore.value.toFixed(4)}`
: ''}
</Box>
</Flex>
</MyTooltip>
)}
{score.secondaryScore.map((item, i) => (
<MyTooltip key={item.type} label={t(SearchScoreTypeMap[item.type]?.desc)}>
<Box fontSize={'xs'} mr={3}>
<Flex alignItems={'flex-start'} lineHeight={1.2} mb={1}>
<Box
px={'5px'}
borderWidth={'1px'} borderWidth={'1px'}
borderRadius={'sm'} borderColor={'primary.200'}
mr={1} alignItems={'center'}
{...(scoreTheme[i] && scoreTheme[i])} fontSize={'sm'}
> >
<Box transform={'scale(0.9)'}>#{item.index + 1}</Box> <Box>#{score.primaryScore.index + 1}</Box>
</Box> <Box
<Box transform={'scale(0.9)'}> borderRightColor={'primary.700'}
{t(SearchScoreTypeMap[item.type]?.label)}: {item.value.toFixed(4)} borderRightWidth={'1px'}
</Box> h={'14px'}
</Flex> mx={2}
<Box h={'4px'}>
{SearchScoreTypeMap[item.type]?.showScore && (
<Progress
value={item.value * 100}
h={'4px'}
w={'100%'}
size="sm"
borderRadius={'20px'}
colorScheme={scoreTheme[i]?.colorSchema}
bg="#E8EBF0"
/> />
)} <Box>
{t(SearchScoreTypeMap[score.primaryScore.type]?.label)}
{SearchScoreTypeMap[score.primaryScore.type]?.showScore
? ` ${score.primaryScore.value.toFixed(4)}`
: ''}
</Box>
</Flex>
</MyTooltip>
) : (
<Flex
px={'12px'}
py={'1px'}
mr={4}
borderRadius={'md'}
color={'primary.700'}
bg={'primary.50'}
borderWidth={'1px'}
borderColor={'primary.200'}
alignItems={'center'}
fontSize={'sm'}
>
<Box>#{score.primaryScore.index + 1}</Box>
</Flex>
)}
</>
)}
{canViewSource &&
score.secondaryScore.map((item, i) => (
<MyTooltip key={item.type} label={t(SearchScoreTypeMap[item.type]?.desc)}>
<Box fontSize={'xs'} mr={3}>
<Flex alignItems={'flex-start'} lineHeight={1.2} mb={1}>
<Box
px={'5px'}
borderWidth={'1px'}
borderRadius={'sm'}
mr={1}
{...(scoreTheme[i] && scoreTheme[i])}
>
<Box transform={'scale(0.9)'}>#{item.index + 1}</Box>
</Box>
<Box transform={'scale(0.9)'}>
{t(SearchScoreTypeMap[item.type]?.label)}: {item.value.toFixed(4)}
</Box>
</Flex>
<Box h={'4px'}>
{SearchScoreTypeMap[item.type]?.showScore && (
<Progress
value={item.value * 100}
h={'4px'}
w={'100%'}
size="sm"
borderRadius={'20px'}
colorScheme={scoreTheme[i]?.colorSchema}
bg="#E8EBF0"
/>
)}
</Box>
</Box> </Box>
</Box> </MyTooltip>
</MyTooltip> ))}
))}
</Flex> </Flex>
<Box flex={'1 0 0'}> <Box flex={'1 0 0'}>

View File

@@ -28,5 +28,6 @@ export type DatasetDataListItemType = {
collectionId: string; collectionId: string;
q: string; // embedding content q: string; // embedding content
a: string; // bonus content a: string; // bonus content
chunkIndex?: number;
indexes: DatasetDataSchemaType['indexes']; indexes: DatasetDataSchemaType['indexes'];
}; };

View File

@@ -32,7 +32,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
}; };
const [data, total] = await Promise.all([ const [data, total] = await Promise.all([
MongoDatasetData.find(match, '_id datasetId collectionId q a indexes') MongoDatasetData.find(match, '_id datasetId collectionId q a chunkIndex indexes')
.sort({ chunkIndex: 1, updateTime: -1 }) .sort({ chunkIndex: 1, updateTime: -1 })
.skip((pageNum - 1) * pageSize) .skip((pageNum - 1) * pageSize)
.limit(pageSize) .limit(pageSize)

View File

@@ -10,6 +10,7 @@ import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools';
import { getBillSourceByAuthType } from '@fastgpt/global/support/wallet/bill/tools'; import { getBillSourceByAuthType } from '@fastgpt/global/support/wallet/bill/tools';
type Props = GetVectorProps & { type Props = GetVectorProps & {
input: string | string[];
billId?: string; billId?: string;
}; };
@@ -22,6 +23,8 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
throw new Error('input is nor array or string'); throw new Error('input is nor array or string');
} }
const query = Array.isArray(input) ? input[0] : input;
const { teamId, tmbId, apikey, authType } = await authCert({ const { teamId, tmbId, apikey, authType } = await authCert({
req, req,
authToken: true, authToken: true,
@@ -30,7 +33,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
await authTeamBalance(teamId); await authTeamBalance(teamId);
const { tokens, vectors } = await getVectorsByText({ input, model }); const { tokens, vectors } = await getVectorsByText({ input: query, model });
jsonRes(res, { jsonRes(res, {
data: { data: {

View File

@@ -96,7 +96,7 @@ const Share = ({ appId }: { appId: string }) => {
<Tr> <Tr>
<Th></Th> <Th></Th>
<Th></Th> <Th></Th>
<Th></Th> <Th></Th>
{feConfigs?.isPlus && ( {feConfigs?.isPlus && (
<> <>
<Th>IP限流/</Th> <Th>IP限流/</Th>
@@ -373,8 +373,8 @@ function EditLinkModal({
<Flex alignItems={'center'} mt={4}> <Flex alignItems={'center'} mt={4}>
<Flex flex={'0 0 90px'} alignItems={'center'}> <Flex flex={'0 0 90px'} alignItems={'center'}>
{t('outlink.Response Detail')} {t('support.outlink.share.Response Quote')}
<MyTooltip label={t('outlink.Response Detail tips' || '')}> <MyTooltip label={t('support.outlink.share.Response Quote tips' || '')}>
<QuestionOutlineIcon ml={1} /> <QuestionOutlineIcon ml={1} />
</MyTooltip> </MyTooltip>
</Flex> </Flex>

View File

@@ -307,7 +307,7 @@ const DataCard = () => {
> >
<Flex zIndex={1} alignItems={'center'} justifyContent={'space-between'}> <Flex zIndex={1} alignItems={'center'} justifyContent={'space-between'}>
<Box border={theme.borders.base} px={2} fontSize={'sm'} mr={1} borderRadius={'md'}> <Box border={theme.borders.base} px={2} fontSize={'sm'} mr={1} borderRadius={'md'}>
# {index + 1} # {item.chunkIndex ?? '-'}
</Box> </Box>
<Box className={'textEllipsis'} color={'myGray.500'} fontSize={'xs'}> <Box className={'textEllipsis'} color={'myGray.500'} fontSize={'xs'}>
ID:{item._id} ID:{item._id}

View File

@@ -295,7 +295,7 @@ export async function searchDatasetData(props: {
const embeddingRecall = async ({ query, limit }: { query: string; limit: number }) => { const embeddingRecall = async ({ query, limit }: { query: string; limit: number }) => {
const { vectors, tokens } = await getVectorsByText({ const { vectors, tokens } = await getVectorsByText({
model, model,
input: [query] input: query
}); });
const { results } = await recallFromVectorStore({ const { results } = await recallFromVectorStore({

View File

@@ -26,6 +26,8 @@ export async function generateVector(): Promise<any> {
if (global.vectorQueueLen >= global.systemEnv.vectorMaxProcess) return; if (global.vectorQueueLen >= global.systemEnv.vectorMaxProcess) return;
global.vectorQueueLen++; global.vectorQueueLen++;
const start = Date.now();
// get training data // get training data
const { const {
data, data,
@@ -154,6 +156,8 @@ export async function generateVector(): Promise<any> {
await MongoDatasetTraining.findByIdAndDelete(data._id); await MongoDatasetTraining.findByIdAndDelete(data._id);
reduceQueue(); reduceQueue();
generateVector(); generateVector();
console.log(`embedding finished, time: ${Date.now() - start}ms`);
} catch (err: any) { } catch (err: any) {
reduceQueue(true); reduceQueue(true);
// log // log

View File

@@ -1,26 +1,23 @@
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d'; import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
export function selectShareResponse({ export const selectShareResponse = ({
responseData = [] responseData = []
}: { }: {
responseData?: ChatHistoryItemResType[]; responseData?: ChatHistoryItemResType[];
}) { }) => {
const filedList = [ const filedList = ['quoteList', 'moduleType'];
'moduleType', const filterModuleTypeList: any[] = [FlowNodeTypeEnum.chatNode];
'moduleName', return responseData
'moduleLogo', .filter((item) => filterModuleTypeList.includes(item.moduleType))
'runningTime', .map((item) => {
'quoteList', const obj: Record<string, any> = {};
'question' for (let key in item) {
]; if (filedList.includes(key)) {
return responseData.map((item) => { // @ts-ignore
const obj: Record<string, any> = {}; obj[key] = item[key];
for (let key in item) { }
if (filedList.includes(key)) {
// @ts-ignore
obj[key] = item[key];
} }
} return obj as ChatHistoryItemResType;
return obj as ChatHistoryItemResType; });
}); };
}