4.6.3-website dataset (#532)

This commit is contained in:
Archer 2023-12-03 20:45:57 +08:00 committed by GitHub
parent b916183848
commit a9ae270335
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
122 changed files with 3793 additions and 1360 deletions

View File

@ -59,7 +59,7 @@ Authorization 为 sk-aaabbbcccdddeeefffggghhhiiijjjkkk。model 为刚刚在 One
## 接入 FastGPT ## 接入 FastGPT
修改 config.json 配置文件,在 VectorModels 中加入 chatglm2 和 M3E 模型: 修改 config.json 配置文件,在 ChatModels 中加入 chatglm2, 在 VectorModels 中加入 M3E 模型:
```json ```json
"ChatModels": [ "ChatModels": [

View File

@ -0,0 +1,32 @@
---
title: 'V4.6.3(需要初始化)'
description: 'FastGPT V4.6.3'
icon: 'upgrade'
draft: false
toc: true
weight: 833
---
## 1。执行初始化 API
发起 1 个 HTTP 请求 ({{rootkey}} 替换成环境变量里的 `rootkey`{{host}} 替换成自己域名)
1. https://xxxxx/api/admin/initv463
```bash
curl --location --request POST 'https://{{host}}/api/admin/initv463' \
--header 'rootkey: {{rootkey}}' \
--header 'Content-Type: application/json'
```
初始化说明:
1. 初始化Mongo 中 datasetcollection 和 data 的部分字段
## V4.6.3 功能介绍
1. 商业版新增 - web站点同步
2. 新增 - 集合元数据记录
3. 优化 - url 读取内容
4. 优化 - 流读取文件,防止内存溢出
5. 优化 - 4v模型自动将 url 转 base64本地也可调试
6. 优化 - 图片压缩等级

8
packages/global/common/file/api.d.ts vendored Normal file
View File

@ -0,0 +1,8 @@
export type UrlFetchParams = {
urlList: string[];
selector?: string;
};
export type UrlFetchResponse = {
url: string;
content: string;
}[];

View File

@ -1,3 +1,8 @@
import axios from 'axios';
import { UrlFetchParams, UrlFetchResponse } from './api.d';
import { htmlToMarkdown } from '../string/markdown';
import * as cheerio from 'cheerio';
export const formatFileSize = (bytes: number): string => { export const formatFileSize = (bytes: number): string => {
if (bytes === 0) return '0 B'; if (bytes === 0) return '0 B';
@ -7,3 +12,84 @@ export const formatFileSize = (bytes: number): string => {
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}; };
export const cheerioToHtml = ({
fetchUrl,
$,
selector
}: {
fetchUrl: string;
$: cheerio.CheerioAPI;
selector?: string;
}) => {
// get origin url
const originUrl = new URL(fetchUrl).origin;
// remove i element
$('i,script').remove();
// remove empty a element
$('a')
.filter((i, el) => {
return $(el).text().trim() === '' && $(el).children().length === 0;
})
.remove();
// if link,img startWith /, add origin url
$('a').each((i, el) => {
const href = $(el).attr('href');
if (href && href.startsWith('/')) {
$(el).attr('href', originUrl + href);
}
});
$('img').each((i, el) => {
const src = $(el).attr('src');
if (src && src.startsWith('/')) {
$(el).attr('src', originUrl + src);
}
});
return $(selector || 'body').html();
};
export const urlsFetch = async ({
urlList,
selector
}: UrlFetchParams): Promise<UrlFetchResponse> => {
urlList = urlList.filter((url) => /^(http|https):\/\/[^ "]+$/.test(url));
const response = (
await Promise.all(
urlList.map(async (url) => {
try {
const fetchRes = await axios.get(url, {
timeout: 30000
});
const $ = cheerio.load(fetchRes.data);
const md = htmlToMarkdown(
cheerioToHtml({
fetchUrl: url,
$,
selector
})
);
return {
url,
content: md
};
} catch (error) {
console.log(error, 'fetch error');
return {
url,
content: ''
};
}
})
)
).filter((item) => item.content);
return response;
};

View File

@ -1,4 +0,0 @@
export type FetchResultItem = {
url: string;
content: string;
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,97 @@
import { simpleText } from './tools';
import { NodeHtmlMarkdown } from 'node-html-markdown';
/* Delete redundant text in markdown */
export const simpleMarkdownText = (rawText: string) => {
rawText = simpleText(rawText);
// Remove a line feed from a hyperlink or picture
rawText = rawText.replace(/\[([^\]]+)\]\((.+?)\)/g, (match, linkText, url) => {
const cleanedLinkText = linkText.replace(/\n/g, ' ').trim();
if (!url) {
return '';
}
return `[${cleanedLinkText}](${url})`;
});
// replace special \.* ……
const reg1 = /\\([-.!`_(){}\[\]])/g;
if (reg1.test(rawText)) {
rawText = rawText.replace(/\\([`!*()+-_\[\]{}\\.])/g, '$1');
}
// replace \\n
rawText = rawText.replace(/\\\\n/g, '\\n');
// Remove headings and code blocks front spaces
['####', '###', '##', '#', '```', '~~~'].forEach((item) => {
const reg = new RegExp(`\\n\\s*${item}`, 'g');
if (reg.test(rawText)) {
rawText = rawText.replace(new RegExp(`\\n\\s*(${item})`, 'g'), '\n$1');
}
});
return rawText.trim();
};
/* html string to markdown */
export const htmlToMarkdown = (html?: string | null) => {
if (!html) return '';
const surround = (source: string, surroundStr: string) => `${surroundStr}${source}${surroundStr}`;
const nhm = new NodeHtmlMarkdown(
{
codeFence: '```',
codeBlockStyle: 'fenced',
ignore: ['i', 'script']
},
{
code: ({ node, parent, options: { codeFence, codeBlockStyle }, visitor }) => {
const isCodeBlock = ['PRE', 'WRAPPED-PRE'].includes(parent?.tagName!);
if (!isCodeBlock) {
return {
spaceIfRepeatingChar: true,
noEscape: true,
postprocess: ({ content }) => {
// Find longest occurring sequence of running backticks and add one more (so content is escaped)
const delimiter =
'`' + (content.match(/`+/g)?.sort((a, b) => b.length - a.length)?.[0] || '');
const padding = delimiter.length > 1 ? ' ' : '';
return surround(surround(content, padding), delimiter);
}
};
}
/* Handle code block */
if (codeBlockStyle === 'fenced') {
const language =
node.getAttribute('class')?.match(/language-(\S+)/)?.[1] ||
parent?.getAttribute('class')?.match(/language-(\S+)/)?.[1] ||
'';
return {
noEscape: true,
prefix: `${codeFence}${language}\n`,
postfix: `\n${codeFence}\n`,
childTranslators: visitor.instance.codeBlockTranslators
};
}
return {
noEscape: true,
postprocess: ({ content }) => content.replace(/^/gm, ' '),
childTranslators: visitor.instance.codeBlockTranslators
};
}
}
);
const markdown = nhm.translate(html).trim();
return simpleMarkdownText(markdown);
};

View File

@ -15,11 +15,18 @@ export const splitText2Chunks = (props: {
}): { }): {
chunks: string[]; chunks: string[];
tokens: number; tokens: number;
overlapRatio?: number;
} => { } => {
const { text = '', chunkLen, overlapRatio = 0.2 } = props; let { text = '', chunkLen, overlapRatio = 0.2 } = props;
const splitMarker = 'SPLIT_HERE_SPLIT_HERE'; const splitMarker = 'SPLIT_HERE_SPLIT_HERE';
const codeBlockMarker = 'CODE_BLOCK_LINE_MARKER';
const overlapLen = Math.round(chunkLen * overlapRatio); const overlapLen = Math.round(chunkLen * overlapRatio);
// replace code block all \n to codeBlockMarker
text = text.replace(/(```[\s\S]*?```|~~~[\s\S]*?~~~)/g, function (match) {
return match.replace(/\n/g, codeBlockMarker);
});
// The larger maxLen is, the next sentence is less likely to trigger splitting // The larger maxLen is, the next sentence is less likely to trigger splitting
const stepReges: { reg: RegExp; maxLen: number }[] = [ const stepReges: { reg: RegExp; maxLen: number }[] = [
{ reg: /^(#\s[^\n]+)\n/gm, maxLen: chunkLen * 1.4 }, { reg: /^(#\s[^\n]+)\n/gm, maxLen: chunkLen * 1.4 },
@ -27,8 +34,8 @@ export const splitText2Chunks = (props: {
{ reg: /^(###\s[^\n]+)\n/gm, maxLen: chunkLen * 1.4 }, { reg: /^(###\s[^\n]+)\n/gm, maxLen: chunkLen * 1.4 },
{ reg: /^(####\s[^\n]+)\n/gm, maxLen: chunkLen * 1.4 }, { reg: /^(####\s[^\n]+)\n/gm, maxLen: chunkLen * 1.4 },
{ reg: /([\n]{2})/g, maxLen: chunkLen * 1.4 }, { reg: /([\n](`))/g, maxLen: chunkLen * 4 }, // code block
{ reg: /([\n](?![\*\-|>`0-9]))/g, maxLen: chunkLen * 1.8 }, // (?![\*\-|>`0-9]): markdown special char { reg: /([\n](?![\*\-|>0-9]))/g, maxLen: chunkLen * 1.8 }, // (?![\*\-|>`0-9]): markdown special char
{ reg: /([\n])/g, maxLen: chunkLen * 1.4 }, { reg: /([\n])/g, maxLen: chunkLen * 1.4 },
{ reg: /([。]|([a-zA-Z])\.\s)/g, maxLen: chunkLen * 1.4 }, { reg: /([。]|([a-zA-Z])\.\s)/g, maxLen: chunkLen * 1.4 },
@ -38,9 +45,15 @@ export const splitText2Chunks = (props: {
{ reg: /([]|,\s)/g, maxLen: chunkLen * 2 } { reg: /([]|,\s)/g, maxLen: chunkLen * 2 }
]; ];
// if use markdown title split, Separate record title title
const getSplitTexts = ({ text, step }: { text: string; step: number }) => { const getSplitTexts = ({ text, step }: { text: string; step: number }) => {
if (step >= stepReges.length) { if (step >= stepReges.length) {
return [text]; return [
{
text,
title: ''
}
];
} }
const isMarkdownSplit = step <= 3; const isMarkdownSplit = step <= 3;
const { reg } = stepReges[step]; const { reg } = stepReges[step];
@ -49,7 +62,17 @@ export const splitText2Chunks = (props: {
.replace(reg, isMarkdownSplit ? `${splitMarker}$1` : `$1${splitMarker}`) .replace(reg, isMarkdownSplit ? `${splitMarker}$1` : `$1${splitMarker}`)
.split(`${splitMarker}`) .split(`${splitMarker}`)
.filter((part) => part.trim()); .filter((part) => part.trim());
return splitTexts;
return splitTexts
.map((text) => {
const matchTitle = isMarkdownSplit ? text.match(reg)?.[0] || '' : '';
return {
text: isMarkdownSplit ? text.replace(matchTitle, '') : text,
title: matchTitle
};
})
.filter((item) => item.text.trim());
}; };
const getOneTextOverlapText = ({ text, step }: { text: string; step: number }): string => { const getOneTextOverlapText = ({ text, step }: { text: string; step: number }): string => {
@ -63,7 +86,7 @@ export const splitText2Chunks = (props: {
let overlayText = ''; let overlayText = '';
for (let i = splitTexts.length - 1; i >= 0; i--) { for (let i = splitTexts.length - 1; i >= 0; i--) {
const currentText = splitTexts[i]; const currentText = splitTexts[i].text;
const newText = currentText + overlayText; const newText = currentText + overlayText;
const newTextLen = newText.length; const newTextLen = newText.length;
@ -83,12 +106,16 @@ export const splitText2Chunks = (props: {
const splitTextRecursively = ({ const splitTextRecursively = ({
text = '', text = '',
step, step,
lastText lastText,
mdTitle = ''
}: { }: {
text: string; text: string;
step: number; step: number;
lastText: string; lastText: string;
mdTitle: string;
}): string[] => { }): string[] => {
const isMarkdownSplit = step <= 3;
// mini text // mini text
if (text.length <= chunkLen) { if (text.length <= chunkLen) {
return [text]; return [text];
@ -102,7 +129,7 @@ export const splitText2Chunks = (props: {
// use slice-chunkLen to split text // use slice-chunkLen to split text
const chunks: string[] = []; const chunks: string[] = [];
for (let i = 0; i < text.length; i += chunkLen - overlapLen) { for (let i = 0; i < text.length; i += chunkLen - overlapLen) {
chunks.push(text.slice(i, i + chunkLen)); chunks.push(`${mdTitle}${text.slice(i, i + chunkLen)}`);
} }
return chunks; return chunks;
} }
@ -115,7 +142,10 @@ export const splitText2Chunks = (props: {
const chunks: string[] = []; const chunks: string[] = [];
for (let i = 0; i < splitTexts.length; i++) { for (let i = 0; i < splitTexts.length; i++) {
const currentText = splitTexts[i]; const item = splitTexts[i];
const currentTitle = `${mdTitle}${item.title}`;
const currentText = item.text;
const currentTextLen = currentText.length; const currentTextLen = currentText.length;
const lastTextLen = lastText.length; const lastTextLen = lastText.length;
const newText = lastText + currentText; const newText = lastText + currentText;
@ -125,9 +155,10 @@ export const splitText2Chunks = (props: {
if (newTextLen > maxLen) { if (newTextLen > maxLen) {
// lastText greater minChunkLen, direct push it to chunks, not add to next chunk. (large lastText) // lastText greater minChunkLen, direct push it to chunks, not add to next chunk. (large lastText)
if (lastTextLen > minChunkLen) { if (lastTextLen > minChunkLen) {
chunks.push(lastText); chunks.push(`${currentTitle}${lastText}`);
lastText = getOneTextOverlapText({ text: lastText, step }); // next chunk will start with overlayText lastText = getOneTextOverlapText({ text: lastText, step }); // next chunk will start with overlayText
i--; i--;
continue; continue;
} }
@ -135,11 +166,12 @@ export const splitText2Chunks = (props: {
const innerChunks = splitTextRecursively({ const innerChunks = splitTextRecursively({
text: newText, text: newText,
step: step + 1, step: step + 1,
lastText: '' lastText: '',
mdTitle: currentTitle
}); });
const lastChunk = innerChunks[innerChunks.length - 1]; const lastChunk = innerChunks[innerChunks.length - 1];
// last chunk is too small, concat it to lastText // last chunk is too small, concat it to lastText
if (lastChunk.length < minChunkLen) { if (!isMarkdownSplit && lastChunk.length < minChunkLen) {
chunks.push(...innerChunks.slice(0, -1)); chunks.push(...innerChunks.slice(0, -1));
lastText = lastChunk; lastText = lastChunk;
} else { } else {
@ -156,10 +188,11 @@ export const splitText2Chunks = (props: {
// size less than chunkLen, push text to last chunk. now, text definitely less than maxLen // size less than chunkLen, push text to last chunk. now, text definitely less than maxLen
lastText = newText; lastText = newText;
// If the chunk size reaches, add a chunk // markdown paragraph block: Direct addition; If the chunk size reaches, add a chunk
if (newTextLen >= chunkLen) { if (isMarkdownSplit || newTextLen >= chunkLen) {
chunks.push(lastText); chunks.push(`${currentTitle}${lastText}`);
lastText = getOneTextOverlapText({ text: lastText, step });
lastText = isMarkdownSplit ? '' : getOneTextOverlapText({ text: lastText, step });
} }
} }
@ -168,7 +201,7 @@ export const splitText2Chunks = (props: {
if (lastText.length < chunkLen * 0.4) { if (lastText.length < chunkLen * 0.4) {
chunks[chunks.length - 1] = chunks[chunks.length - 1] + lastText; chunks[chunks.length - 1] = chunks[chunks.length - 1] + lastText;
} else { } else {
chunks.push(lastText); chunks.push(`${mdTitle}${lastText}`);
} }
} }
@ -179,8 +212,9 @@ export const splitText2Chunks = (props: {
const chunks = splitTextRecursively({ const chunks = splitTextRecursively({
text, text,
step: 0, step: 0,
lastText: '' lastText: '',
}); mdTitle: ''
}).map((chunk) => chunk.replaceAll(codeBlockMarker, '\n')); // restore code block
const tokens = chunks.reduce((sum, chunk) => sum + countPromptTokens(chunk, 'system'), 0); const tokens = chunks.reduce((sum, chunk) => sum + countPromptTokens(chunk, 'system'), 0);

View File

@ -0,0 +1,6 @@
export const delay = (ms: number) =>
new Promise((resolve) => {
setTimeout(() => {
resolve('');
}, ms);
});

View File

@ -5,7 +5,6 @@ export enum ChatRoleEnum {
Function = 'Function', Function = 'Function',
Tool = 'Tool' Tool = 'Tool'
} }
export const ChatRoleMap = { export const ChatRoleMap = {
[ChatRoleEnum.System]: { [ChatRoleEnum.System]: {
name: '系统提示词' name: '系统提示词'
@ -30,7 +29,6 @@ export enum ChatSourceEnum {
share = 'share', share = 'share',
api = 'api' api = 'api'
} }
export const ChatSourceMap = { export const ChatSourceMap = {
[ChatSourceEnum.test]: { [ChatSourceEnum.test]: {
name: 'chat.logs.test' name: 'chat.logs.test'

View File

@ -1,8 +1,32 @@
import { DatasetDataIndexItemType } from './type'; import { DatasetDataIndexItemType, DatasetSchemaType } from './type';
import { DatasetCollectionTrainingModeEnum, DatasetCollectionTypeEnum } from './constant';
import type { LLMModelItemType } from '../ai/model.d';
/* ================= dataset ===================== */ /* ================= dataset ===================== */
export type DatasetUpdateBody = {
id: string;
parentId?: string;
tags?: string[];
name?: string;
avatar?: string;
permission?: DatasetSchemaType['permission'];
agentModel?: LLMModelItemType;
websiteConfig?: DatasetSchemaType['websiteConfig'];
status?: DatasetSchemaType['status'];
};
/* ================= collection ===================== */ /* ================= collection ===================== */
export type CreateDatasetCollectionParams = {
datasetId: string;
parentId?: string;
name: string;
type: `${DatasetCollectionTypeEnum}`;
trainingType?: `${DatasetCollectionTrainingModeEnum}`;
chunkSize?: number;
fileId?: string;
rawLink?: string;
metadata?: Record<string, any>;
};
/* ================= data ===================== */ /* ================= data ===================== */
export type PgSearchRawType = { export type PgSearchRawType = {
@ -18,3 +42,8 @@ export type PushDatasetDataChunkProps = {
a?: string; // bonus content a?: string; // bonus content
indexes?: Omit<DatasetDataIndexItemType, 'dataId'>[]; indexes?: Omit<DatasetDataIndexItemType, 'dataId'>[];
}; };
export type PostWebsiteSyncParams = {
datasetId: string;
billId: string;
};

View File

@ -3,15 +3,37 @@ export const PgDatasetTableName = 'modeldata';
/* ------------ dataset -------------- */ /* ------------ dataset -------------- */
export enum DatasetTypeEnum { export enum DatasetTypeEnum {
folder = 'folder', folder = 'folder',
dataset = 'dataset' dataset = 'dataset',
websiteDataset = 'websiteDataset' // depp link
} }
export const DatasetTypeMap = { export const DatasetTypeMap = {
[DatasetTypeEnum.folder]: { [DatasetTypeEnum.folder]: {
name: 'folder' icon: 'core/dataset/folderDataset',
label: 'core.dataset.Folder Dataset',
collectionLabel: 'common.Folder'
}, },
[DatasetTypeEnum.dataset]: { [DatasetTypeEnum.dataset]: {
name: 'dataset' icon: 'core/dataset/commonDataset',
label: 'core.dataset.Common Dataset',
collectionLabel: 'common.File'
},
[DatasetTypeEnum.websiteDataset]: {
icon: 'core/dataset/websiteDataset',
label: 'core.dataset.Website Dataset',
collectionLabel: 'common.Website'
}
};
export enum DatasetStatusEnum {
active = 'active',
syncing = 'syncing'
}
export const DatasetStatusMap = {
[DatasetStatusEnum.active]: {
label: 'core.dataset.status.active'
},
[DatasetStatusEnum.syncing]: {
label: 'core.dataset.status.syncing'
} }
}; };
@ -19,7 +41,7 @@ export const DatasetTypeMap = {
export enum DatasetCollectionTypeEnum { export enum DatasetCollectionTypeEnum {
folder = 'folder', folder = 'folder',
file = 'file', file = 'file',
link = 'link', link = 'link', // one link
virtual = 'virtual' virtual = 'virtual'
} }
export const DatasetCollectionTypeMap = { export const DatasetCollectionTypeMap = {

View File

@ -4,6 +4,7 @@ import { PushDatasetDataChunkProps } from './api';
import { import {
DatasetCollectionTypeEnum, DatasetCollectionTypeEnum,
DatasetDataIndexTypeEnum, DatasetDataIndexTypeEnum,
DatasetStatusEnum,
DatasetTypeEnum, DatasetTypeEnum,
TrainingModeEnum TrainingModeEnum
} from './constant'; } from './constant';
@ -20,9 +21,14 @@ export type DatasetSchemaType = {
name: string; name: string;
vectorModel: string; vectorModel: string;
agentModel: string; agentModel: string;
tags: string[]; intro: string;
type: `${DatasetTypeEnum}`; type: `${DatasetTypeEnum}`;
status: `${DatasetStatusEnum}`;
permission: `${PermissionTypeEnum}`; permission: `${PermissionTypeEnum}`;
websiteConfig?: {
url: string;
selector: string;
};
}; };
export type DatasetCollectionSchemaType = { export type DatasetCollectionSchemaType = {
@ -39,6 +45,7 @@ export type DatasetCollectionSchemaType = {
chunkSize: number; chunkSize: number;
fileId?: string; fileId?: string;
rawLink?: string; rawLink?: string;
metadata?: Record<string, any>;
}; };
export type DatasetDataIndexItemType = { export type DatasetDataIndexItemType = {
@ -91,6 +98,18 @@ export type DatasetDataWithCollectionType = Omit<DatasetDataSchemaType, 'collect
}; };
/* ================= dataset ===================== */ /* ================= dataset ===================== */
export type DatasetListItemType = {
_id: string;
parentId: string;
avatar: string;
name: string;
intro: string;
type: `${DatasetTypeEnum}`;
isOwner: boolean;
canWrite: boolean;
permission: `${PermissionTypeEnum}`;
vectorModel: VectorModelItemType;
};
export type DatasetItemType = Omit<DatasetSchemaType, 'vectorModel' | 'agentModel'> & { export type DatasetItemType = Omit<DatasetSchemaType, 'vectorModel' | 'agentModel'> & {
vectorModel: VectorModelItemType; vectorModel: VectorModelItemType;
agentModel: LLMModelItemType; agentModel: LLMModelItemType;

View File

@ -3,13 +3,16 @@
"version": "1.0.0", "version": "1.0.0",
"dependencies": { "dependencies": {
"axios": "^1.5.1", "axios": "^1.5.1",
"timezones-list": "^3.0.2", "cheerio": "1.0.0-rc.12",
"dayjs": "^1.11.7", "dayjs": "^1.11.7",
"encoding": "^0.1.13", "encoding": "^0.1.13",
"js-tiktoken": "^1.0.7",
"node-html-markdown": "^1.3.0",
"openai": "^4.16.1", "openai": "^4.16.1",
"js-tiktoken": "^1.0.7" "timezones-list": "^3.0.2"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.8.5" "@types/node": "^20.8.5",
"@types/turndown": "^5.0.4"
} }
} }

View File

@ -1,6 +1,7 @@
/* bill common */ /* bill common */
import { PRICE_SCALE } from './constants'; import { PRICE_SCALE } from './constants';
import { BillItemType, BillSchema } from './type'; import { BillSourceEnum } from './constants';
import { AuthUserTypeEnum } from '../../permission/constant';
/** /**
* dataset price / PRICE_SCALE = real price * dataset price / PRICE_SCALE = real price
@ -8,3 +9,15 @@ import { BillItemType, BillSchema } from './type';
export const formatPrice = (val = 0, multiple = 1) => { export const formatPrice = (val = 0, multiple = 1) => {
return Number(((val / PRICE_SCALE) * multiple).toFixed(10)); return Number(((val / PRICE_SCALE) * multiple).toFixed(10));
}; };
export const getBillSourceByAuthType = ({
shareId,
authType
}: {
shareId?: string;
authType?: `${AuthUserTypeEnum}`;
}) => {
if (shareId) return BillSourceEnum.shareLink;
if (authType === AuthUserTypeEnum.apikey) return BillSourceEnum.api;
return BillSourceEnum.fastgpt;
};

View File

@ -101,18 +101,18 @@ export function request(url: string, data: any, config: ConfigType, method: Meth
* @param {Object} config * @param {Object} config
* @returns * @returns
*/ */
export function GET<T>(url: string, params = {}, config: ConfigType = {}): Promise<T> { export function GET<T = undefined>(url: string, params = {}, config: ConfigType = {}): Promise<T> {
return request(url, params, config, 'GET'); return request(url, params, config, 'GET');
} }
export function POST<T>(url: string, data = {}, config: ConfigType = {}): Promise<T> { export function POST<T = undefined>(url: string, data = {}, config: ConfigType = {}): Promise<T> {
return request(url, data, config, 'POST'); return request(url, data, config, 'POST');
} }
export function PUT<T>(url: string, data = {}, config: ConfigType = {}): Promise<T> { export function PUT<T = undefined>(url: string, data = {}, config: ConfigType = {}): Promise<T> {
return request(url, data, config, 'PUT'); return request(url, data, config, 'PUT');
} }
export function DELETE<T>(url: string, data = {}, config: ConfigType = {}): Promise<T> { export function DELETE<T = undefined>(url: string, data = {}, config: ConfigType = {}): Promise<T> {
return request(url, data, config, 'DELETE'); return request(url, data, config, 'DELETE');
} }

View File

@ -89,7 +89,7 @@ export async function delFileById({
return true; return true;
} }
export async function getDownloadBuf({ export async function getDownloadStream({
bucketName, bucketName,
fileId fileId
}: { }: {
@ -98,14 +98,5 @@ export async function getDownloadBuf({
}) { }) {
const bucket = getGridBucket(bucketName); const bucket = getGridBucket(bucketName);
const stream = bucket.openDownloadStream(new Types.ObjectId(fileId)); return bucket.openDownloadStream(new Types.ObjectId(fileId));
const buf: Buffer = await new Promise((resolve, reject) => {
const buffers: Buffer[] = [];
stream.on('data', (data) => buffers.push(data));
stream.on('error', reject);
stream.on('end', () => resolve(Buffer.concat(buffers)));
});
return buf;
} }

View File

@ -3,6 +3,7 @@ import { ChatRoleEnum, IMG_BLOCK_KEY } from '@fastgpt/global/core/chat/constants
import { countMessagesTokens, countPromptTokens } from '@fastgpt/global/common/string/tiktoken'; import { countMessagesTokens, countPromptTokens } from '@fastgpt/global/common/string/tiktoken';
import { adaptRole_Chat2Message } from '@fastgpt/global/core/chat/adapt'; import { adaptRole_Chat2Message } from '@fastgpt/global/core/chat/adapt';
import type { ChatCompletionContentPart } from '@fastgpt/global/core/ai/type.d'; import type { ChatCompletionContentPart } from '@fastgpt/global/core/ai/type.d';
import axios from 'axios';
/* slice chat context by tokens */ /* slice chat context by tokens */
export function ChatContextFilter({ export function ChatContextFilter({
@ -81,11 +82,13 @@ export function ChatContextFilter({
} }
] ]
*/ */
export function formatStr2ChatContent(str: string) { export async function formatStr2ChatContent(str: string) {
const content: ChatCompletionContentPart[] = []; const content: ChatCompletionContentPart[] = [];
let lastIndex = 0; let lastIndex = 0;
const regex = new RegExp(`\`\`\`(${IMG_BLOCK_KEY})\\n([\\s\\S]*?)\`\`\``, 'g'); const regex = new RegExp(`\`\`\`(${IMG_BLOCK_KEY})\\n([\\s\\S]*?)\`\`\``, 'g');
const imgKey: 'image_url' = 'image_url';
let match; let match;
while ((match = regex.exec(str)) !== null) { while ((match = regex.exec(str)) !== null) {
@ -115,7 +118,7 @@ export function formatStr2ChatContent(str: string) {
content.push( content.push(
...jsonLines.map((item) => ({ ...jsonLines.map((item) => ({
type: 'image_url' as any, type: imgKey,
image_url: { image_url: {
url: item.src url: item.src
} }
@ -148,5 +151,18 @@ export function formatStr2ChatContent(str: string) {
if (content.length === 1 && content[0].type === 'text') { if (content.length === 1 && content[0].type === 'text') {
return content[0].text; return content[0].text;
} }
if (!content) return null;
// load img to base64
for await (const item of content) {
if (item.type === imgKey && item[imgKey]?.url) {
const response = await axios.get(item[imgKey].url, {
responseType: 'arraybuffer'
});
const base64 = Buffer.from(response.data).toString('base64');
item[imgKey].url = `data:${response.headers['content-type']};base64,${base64}`;
}
}
return content ? content : null; return content ? content : null;
} }

View File

@ -0,0 +1,73 @@
import {
DatasetCollectionTrainingModeEnum,
DatasetCollectionTypeEnum
} from '@fastgpt/global/core/dataset/constant';
import type { CreateDatasetCollectionParams } from '@fastgpt/global/core/dataset/api.d';
import { MongoDatasetCollection } from './schema';
export async function createOneCollection({
name,
parentId,
datasetId,
type,
trainingType = DatasetCollectionTrainingModeEnum.manual,
chunkSize = 0,
fileId,
rawLink,
teamId,
tmbId,
metadata = {}
}: CreateDatasetCollectionParams & { teamId: string; tmbId: string }) {
const { _id } = await MongoDatasetCollection.create({
name,
teamId,
tmbId,
datasetId,
parentId: parentId || null,
type,
trainingType,
chunkSize,
fileId,
rawLink,
metadata
});
// create default collection
if (type === DatasetCollectionTypeEnum.folder) {
await createDefaultCollection({
datasetId,
parentId: _id,
teamId,
tmbId
});
}
return _id;
}
// create default collection
export function createDefaultCollection({
name = '手动录入',
datasetId,
parentId,
teamId,
tmbId
}: {
name?: '手动录入' | '手动标注';
datasetId: string;
parentId?: string;
teamId: string;
tmbId: string;
}) {
return MongoDatasetCollection.create({
name,
teamId,
tmbId,
datasetId,
parentId,
type: DatasetCollectionTypeEnum.virtual,
trainingType: DatasetCollectionTrainingModeEnum.manual,
chunkSize: 0,
updateTime: new Date('2099')
});
}

View File

@ -39,15 +39,16 @@ const DatasetCollectionSchema = new Schema({
ref: DatasetCollectionName, ref: DatasetCollectionName,
required: true required: true
}, },
name: {
type: String,
required: true
},
type: { type: {
type: String, type: String,
enum: Object.keys(DatasetCollectionTypeMap), enum: Object.keys(DatasetCollectionTypeMap),
required: true required: true
}, },
name: {
type: String,
required: true
},
createTime: { createTime: {
type: Date, type: Date,
default: () => new Date() default: () => new Date()

View File

@ -0,0 +1,75 @@
import { MongoDatasetData } from './schema';
import { deletePgDataById } from './pg';
import { MongoDatasetTraining } from '../training/schema';
import { delFileById } from '../../../common/file/gridfs/controller';
import { BucketNameEnum } from '@fastgpt/global/common/file/constants';
import { MongoDatasetCollection } from '../collection/schema';
import { delDatasetFiles } from '../file/controller';
import { delay } from '@fastgpt/global/common/system/utils';
/* delete all data by datasetIds */
export async function delDatasetRelevantData({ datasetIds }: { datasetIds: string[] }) {
datasetIds = datasetIds.map((item) => String(item));
// delete training data(There could be a training mission)
await MongoDatasetTraining.deleteMany({
datasetId: { $in: datasetIds }
});
// delete related files
await Promise.all(datasetIds.map((id) => delDatasetFiles({ datasetId: id })));
await delay(1000);
// delete pg data
await deletePgDataById(`dataset_id IN ('${datasetIds.join("','")}')`);
// delete dataset.datas
await MongoDatasetData.deleteMany({ datasetId: { $in: datasetIds } });
// delete collections
await MongoDatasetCollection.deleteMany({
datasetId: { $in: datasetIds }
});
}
/**
* delete all data by collectionIds
*/
export async function delCollectionRelevantData({
collectionIds,
fileIds
}: {
collectionIds: string[];
fileIds: string[];
}) {
collectionIds = collectionIds.map((item) => String(item));
const filterFileIds = fileIds.filter(Boolean);
// delete training data
await MongoDatasetTraining.deleteMany({
collectionId: { $in: collectionIds }
});
// delete file
await Promise.all(
filterFileIds.map((fileId) => {
return delFileById({
bucketName: BucketNameEnum.dataset,
fileId
});
})
);
await delay(1000);
// delete pg data
await deletePgDataById(`collection_id IN ('${collectionIds.join("','")}')`);
// delete dataset.datas
await MongoDatasetData.deleteMany({ collectionId: { $in: collectionIds } });
}
/**
* delete one data by mongoDataId
*/
export async function delDatasetDataByDataId(mongoDataId: string) {
await deletePgDataById(['data_id', mongoDataId]);
await MongoDatasetData.findByIdAndDelete(mongoDataId);
}

View File

@ -0,0 +1,28 @@
import { PgDatasetTableName } from '@fastgpt/global/core/dataset/constant';
import { delay } from '@fastgpt/global/common/system/utils';
import { PgClient } from '../../../common/pg';
export async function deletePgDataById(
where: ['id' | 'dataset_id' | 'collection_id' | 'data_id', string] | string
) {
let retry = 2;
async function deleteData(): Promise<any> {
try {
await PgClient.delete(PgDatasetTableName, {
where: [where]
});
} catch (error) {
if (--retry < 0) {
return Promise.reject(error);
}
await delay(500);
return deleteData();
}
}
await deleteData();
return {
tokenLen: 0
};
}

View File

@ -79,6 +79,9 @@ const DatasetDataSchema = new Schema({
chunkIndex: { chunkIndex: {
type: Number, type: Number,
default: 0 default: 0
},
inited: {
type: Boolean
} }
}); });
@ -88,7 +91,7 @@ try {
DatasetDataSchema.index({ collectionId: 1 }); DatasetDataSchema.index({ collectionId: 1 });
// full text index // full text index
DatasetDataSchema.index({ datasetId: 1, fullTextToken: 'text' }); DatasetDataSchema.index({ datasetId: 1, fullTextToken: 'text' });
DatasetDataSchema.index({ fullTextToken: 1 }); DatasetDataSchema.index({ inited: 1 });
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} }

View File

@ -1,7 +1,11 @@
import { connectionMongo, type Model } from '../../common/mongo'; import { connectionMongo, type Model } from '../../common/mongo';
const { Schema, model, models } = connectionMongo; const { Schema, model, models } = connectionMongo;
import { DatasetSchemaType } from '@fastgpt/global/core/dataset/type.d'; import { DatasetSchemaType } from '@fastgpt/global/core/dataset/type.d';
import { DatasetTypeMap } from '@fastgpt/global/core/dataset/constant'; import {
DatasetStatusEnum,
DatasetStatusMap,
DatasetTypeMap
} from '@fastgpt/global/core/dataset/constant';
import { import {
TeamCollectionName, TeamCollectionName,
TeamMemberCollectionName TeamMemberCollectionName
@ -31,9 +35,16 @@ const DatasetSchema = new Schema({
ref: TeamMemberCollectionName, ref: TeamMemberCollectionName,
required: true required: true
}, },
updateTime: { type: {
type: Date, type: String,
default: () => new Date() enum: Object.keys(DatasetTypeMap),
required: true,
default: 'dataset'
},
status: {
type: String,
enum: Object.keys(DatasetStatusMap),
default: DatasetStatusEnum.active
}, },
avatar: { avatar: {
type: String, type: String,
@ -43,6 +54,10 @@ const DatasetSchema = new Schema({
type: String, type: String,
required: true required: true
}, },
updateTime: {
type: Date,
default: () => new Date()
},
vectorModel: { vectorModel: {
type: String, type: String,
required: true, required: true,
@ -53,24 +68,26 @@ const DatasetSchema = new Schema({
required: true, required: true,
default: 'gpt-3.5-turbo-16k' default: 'gpt-3.5-turbo-16k'
}, },
type: { intro: {
type: String, type: String,
enum: Object.keys(DatasetTypeMap), default: ''
required: true,
default: 'dataset'
},
tags: {
type: [String],
default: [],
set(val: string | string[]) {
if (Array.isArray(val)) return val;
return val.split(' ').filter((item) => item);
}
}, },
permission: { permission: {
type: String, type: String,
enum: Object.keys(PermissionTypeMap), enum: Object.keys(PermissionTypeMap),
default: PermissionTypeEnum.private default: PermissionTypeEnum.private
},
websiteConfig: {
type: {
url: {
type: String,
required: true
},
selector: {
type: String,
default: 'body'
}
}
} }
}); });

446
pnpm-lock.yaml generated
View File

@ -1,4 +1,4 @@
lockfileVersion: '6.0' lockfileVersion: '6.1'
settings: settings:
autoInstallPeers: true autoInstallPeers: true
@ -45,6 +45,9 @@ importers:
axios: axios:
specifier: ^1.5.1 specifier: ^1.5.1
version: registry.npmmirror.com/axios@1.5.1 version: registry.npmmirror.com/axios@1.5.1
cheerio:
specifier: 1.0.0-rc.12
version: registry.npmmirror.com/cheerio@1.0.0-rc.12
dayjs: dayjs:
specifier: ^1.11.7 specifier: ^1.11.7
version: registry.npmmirror.com/dayjs@1.11.10 version: registry.npmmirror.com/dayjs@1.11.10
@ -54,6 +57,9 @@ importers:
js-tiktoken: js-tiktoken:
specifier: ^1.0.7 specifier: ^1.0.7
version: registry.npmmirror.com/js-tiktoken@1.0.7 version: registry.npmmirror.com/js-tiktoken@1.0.7
node-html-markdown:
specifier: ^1.3.0
version: registry.npmmirror.com/node-html-markdown@1.3.0
openai: openai:
specifier: ^4.16.1 specifier: ^4.16.1
version: registry.npmmirror.com/openai@4.16.1(encoding@0.1.13) version: registry.npmmirror.com/openai@4.16.1(encoding@0.1.13)
@ -64,6 +70,9 @@ importers:
'@types/node': '@types/node':
specifier: ^20.8.5 specifier: ^20.8.5
version: registry.npmmirror.com/@types/node@20.8.7 version: registry.npmmirror.com/@types/node@20.8.7
'@types/turndown':
specifier: ^5.0.4
version: registry.npmmirror.com/@types/turndown@5.0.4
packages/service: packages/service:
dependencies: dependencies:
@ -161,9 +170,6 @@ importers:
'@fastgpt/web': '@fastgpt/web':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/web version: link:../../packages/web
'@mozilla/readability':
specifier: ^0.4.4
version: registry.npmmirror.com/@mozilla/readability@0.4.4
'@node-rs/jieba': '@node-rs/jieba':
specifier: ^1.7.2 specifier: ^1.7.2
version: registry.npmmirror.com/@node-rs/jieba@1.7.2 version: registry.npmmirror.com/@node-rs/jieba@1.7.2
@ -209,9 +215,6 @@ importers:
jschardet: jschardet:
specifier: ^3.0.0 specifier: ^3.0.0
version: registry.npmmirror.com/jschardet@3.0.0 version: registry.npmmirror.com/jschardet@3.0.0
jsdom:
specifier: ^22.1.0
version: registry.npmmirror.com/jsdom@22.1.0
jsonwebtoken: jsonwebtoken:
specifier: ^9.0.2 specifier: ^9.0.2
version: registry.npmmirror.com/jsonwebtoken@9.0.2 version: registry.npmmirror.com/jsonwebtoken@9.0.2
@ -300,9 +303,6 @@ importers:
'@types/js-cookie': '@types/js-cookie':
specifier: ^3.0.3 specifier: ^3.0.3
version: registry.npmmirror.com/@types/js-cookie@3.0.5 version: registry.npmmirror.com/@types/js-cookie@3.0.5
'@types/jsdom':
specifier: ^21.1.1
version: registry.npmmirror.com/@types/jsdom@21.1.4
'@types/jsonwebtoken': '@types/jsonwebtoken':
specifier: ^9.0.3 specifier: ^9.0.3
version: registry.npmmirror.com/@types/jsonwebtoken@9.0.4 version: registry.npmmirror.com/@types/jsonwebtoken@9.0.4
@ -3575,13 +3575,6 @@ packages:
dev: false dev: false
optional: true optional: true
registry.npmmirror.com/@mozilla/readability@0.4.4:
resolution: {integrity: sha512-MCgZyANpJ6msfvVMi6+A0UAsvZj//4OHREYUB9f2087uXHVoU+H+SWhuihvb1beKpM323bReQPRio0WNk2+V6g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@mozilla/readability/-/readability-0.4.4.tgz}
name: '@mozilla/readability'
version: 0.4.4
engines: {node: '>=14.0.0'}
dev: false
registry.npmmirror.com/@next/env@13.5.2: registry.npmmirror.com/@next/env@13.5.2:
resolution: {integrity: sha512-dUseBIQVax+XtdJPzhwww4GetTjlkRSsXeQnisIJWBaHsnxYcN2RGzsPHi58D6qnkATjnhuAtQTJmR1hKYQQPg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@next/env/-/env-13.5.2.tgz} resolution: {integrity: sha512-dUseBIQVax+XtdJPzhwww4GetTjlkRSsXeQnisIJWBaHsnxYcN2RGzsPHi58D6qnkATjnhuAtQTJmR1hKYQQPg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@next/env/-/env-13.5.2.tgz}
name: '@next/env' name: '@next/env'
@ -4248,13 +4241,6 @@ packages:
use-sync-external-store: registry.npmmirror.com/use-sync-external-store@1.2.0(react@18.2.0) use-sync-external-store: registry.npmmirror.com/use-sync-external-store@1.2.0(react@18.2.0)
dev: false dev: false
registry.npmmirror.com/@tootallnate/once@2.0.0:
resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@tootallnate/once/-/once-2.0.0.tgz}
name: '@tootallnate/once'
version: 2.0.0
engines: {node: '>= 10'}
dev: false
registry.npmmirror.com/@trysound/sax@0.2.0: registry.npmmirror.com/@trysound/sax@0.2.0:
resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@trysound/sax/-/sax-0.2.0.tgz} resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@trysound/sax/-/sax-0.2.0.tgz}
name: '@trysound/sax' name: '@trysound/sax'
@ -4618,16 +4604,6 @@ packages:
version: 3.0.5 version: 3.0.5
dev: true dev: true
registry.npmmirror.com/@types/jsdom@21.1.4:
resolution: {integrity: sha512-NzAMLEV0KQ4cBaDx3Ls8VfJUElyDUm1xrtYRmcMK0gF8L5xYbujFVaQlJ50yinQ/d47j2rEP1XUzkiYrw4YRFA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/jsdom/-/jsdom-21.1.4.tgz}
name: '@types/jsdom'
version: 21.1.4
dependencies:
'@types/node': registry.npmmirror.com/@types/node@20.8.7
'@types/tough-cookie': registry.npmmirror.com/@types/tough-cookie@4.0.4
parse5: registry.npmmirror.com/parse5@7.1.2
dev: true
registry.npmmirror.com/@types/json5@0.0.29: registry.npmmirror.com/@types/json5@0.0.29:
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/json5/-/json5-0.0.29.tgz} resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/json5/-/json5-0.0.29.tgz}
name: '@types/json5' name: '@types/json5'
@ -4826,12 +4802,6 @@ packages:
'@types/node': registry.npmmirror.com/@types/node@20.8.7 '@types/node': registry.npmmirror.com/@types/node@20.8.7
dev: true dev: true
registry.npmmirror.com/@types/tough-cookie@4.0.4:
resolution: {integrity: sha512-95Sfz4nvMAb0Nl9DTxN3j64adfwfbBPEYq14VN7zT5J5O2M9V6iZMIIQU1U+pJyl9agHYHNCqhCXgyEtIRRa5A==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/tough-cookie/-/tough-cookie-4.0.4.tgz}
name: '@types/tough-cookie'
version: 4.0.4
dev: true
registry.npmmirror.com/@types/triple-beam@1.3.4: registry.npmmirror.com/@types/triple-beam@1.3.4:
resolution: {integrity: sha512-HlJjF3wxV4R2VQkFpKe0YqJLilYNgtRtsqqZtby7RkVsSs+i+vbyzjtUwpFEdUCKcrGzCiEJE7F/0mKjh0sunA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/triple-beam/-/triple-beam-1.3.4.tgz} resolution: {integrity: sha512-HlJjF3wxV4R2VQkFpKe0YqJLilYNgtRtsqqZtby7RkVsSs+i+vbyzjtUwpFEdUCKcrGzCiEJE7F/0mKjh0sunA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/triple-beam/-/triple-beam-1.3.4.tgz}
name: '@types/triple-beam' name: '@types/triple-beam'
@ -4846,6 +4816,12 @@ packages:
'@types/node': registry.npmmirror.com/@types/node@20.8.7 '@types/node': registry.npmmirror.com/@types/node@20.8.7
dev: true dev: true
registry.npmmirror.com/@types/turndown@5.0.4:
resolution: {integrity: sha512-28GI33lCCkU4SGH1GvjDhFgOVr+Tym4PXGBIU1buJUa6xQolniPArtUT+kv42RR2N9MsMLInkr904Aq+ESHBJg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/turndown/-/turndown-5.0.4.tgz}
name: '@types/turndown'
version: 5.0.4
dev: true
registry.npmmirror.com/@types/unist@2.0.9: registry.npmmirror.com/@types/unist@2.0.9:
resolution: {integrity: sha512-zC0iXxAv1C1ERURduJueYzkzZ2zaGyc+P2c95hgkikHPr3z8EdUZOlgEQ5X0DRmwDZn+hekycQnoeiiRVrmilQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/unist/-/unist-2.0.9.tgz} resolution: {integrity: sha512-zC0iXxAv1C1ERURduJueYzkzZ2zaGyc+P2c95hgkikHPr3z8EdUZOlgEQ5X0DRmwDZn+hekycQnoeiiRVrmilQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/unist/-/unist-2.0.9.tgz}
name: '@types/unist' name: '@types/unist'
@ -4967,12 +4943,6 @@ packages:
'@zag-js/dom-query': registry.npmmirror.com/@zag-js/dom-query@0.16.0 '@zag-js/dom-query': registry.npmmirror.com/@zag-js/dom-query@0.16.0
dev: false dev: false
registry.npmmirror.com/abab@2.0.6:
resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/abab/-/abab-2.0.6.tgz}
name: abab
version: 2.0.6
dev: false
registry.npmmirror.com/abort-controller@3.0.0: registry.npmmirror.com/abort-controller@3.0.0:
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/abort-controller/-/abort-controller-3.0.0.tgz} resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/abort-controller/-/abort-controller-3.0.0.tgz}
name: abort-controller name: abort-controller
@ -5001,17 +4971,6 @@ packages:
hasBin: true hasBin: true
dev: true dev: true
registry.npmmirror.com/agent-base@6.0.2:
resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz}
name: agent-base
version: 6.0.2
engines: {node: '>= 6.0.0'}
dependencies:
debug: registry.npmmirror.com/debug@4.3.4
transitivePeerDependencies:
- supports-color
dev: false
registry.npmmirror.com/agentkeepalive@4.5.0: registry.npmmirror.com/agentkeepalive@4.5.0:
resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz} resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz}
name: agentkeepalive name: agentkeepalive
@ -5435,7 +5394,6 @@ packages:
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz} resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz}
name: boolbase name: boolbase
version: 1.0.0 version: 1.0.0
dev: true
registry.npmmirror.com/brace-expansion@1.1.11: registry.npmmirror.com/brace-expansion@1.1.11:
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz} resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz}
@ -5720,6 +5678,34 @@ packages:
get-func-name: registry.npmmirror.com/get-func-name@2.0.2 get-func-name: registry.npmmirror.com/get-func-name@2.0.2
dev: true dev: true
registry.npmmirror.com/cheerio-select@2.1.0:
resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/cheerio-select/-/cheerio-select-2.1.0.tgz}
name: cheerio-select
version: 2.1.0
dependencies:
boolbase: registry.npmmirror.com/boolbase@1.0.0
css-select: registry.npmmirror.com/css-select@5.1.0
css-what: registry.npmmirror.com/css-what@6.1.0
domelementtype: registry.npmmirror.com/domelementtype@2.3.0
domhandler: registry.npmmirror.com/domhandler@5.0.3
domutils: registry.npmmirror.com/domutils@3.1.0
dev: false
registry.npmmirror.com/cheerio@1.0.0-rc.12:
resolution: {integrity: sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/cheerio/-/cheerio-1.0.0-rc.12.tgz}
name: cheerio
version: 1.0.0-rc.12
engines: {node: '>= 6'}
dependencies:
cheerio-select: registry.npmmirror.com/cheerio-select@2.1.0
dom-serializer: registry.npmmirror.com/dom-serializer@2.0.0
domhandler: registry.npmmirror.com/domhandler@5.0.3
domutils: registry.npmmirror.com/domutils@3.1.0
htmlparser2: registry.npmmirror.com/htmlparser2@8.0.2
parse5: registry.npmmirror.com/parse5@7.1.2
parse5-htmlparser2-tree-adapter: registry.npmmirror.com/parse5-htmlparser2-tree-adapter@7.0.0
dev: false
registry.npmmirror.com/chokidar@3.5.3: registry.npmmirror.com/chokidar@3.5.3:
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/chokidar/-/chokidar-3.5.3.tgz} resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/chokidar/-/chokidar-3.5.3.tgz}
name: chokidar name: chokidar
@ -6108,6 +6094,18 @@ packages:
nth-check: registry.npmmirror.com/nth-check@2.1.1 nth-check: registry.npmmirror.com/nth-check@2.1.1
dev: true dev: true
registry.npmmirror.com/css-select@5.1.0:
resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/css-select/-/css-select-5.1.0.tgz}
name: css-select
version: 5.1.0
dependencies:
boolbase: registry.npmmirror.com/boolbase@1.0.0
css-what: registry.npmmirror.com/css-what@6.1.0
domhandler: registry.npmmirror.com/domhandler@5.0.3
domutils: registry.npmmirror.com/domutils@3.1.0
nth-check: registry.npmmirror.com/nth-check@2.1.1
dev: false
registry.npmmirror.com/css-tree@1.1.3: registry.npmmirror.com/css-tree@1.1.3:
resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/css-tree/-/css-tree-1.1.3.tgz} resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/css-tree/-/css-tree-1.1.3.tgz}
name: css-tree name: css-tree
@ -6123,7 +6121,6 @@ packages:
name: css-what name: css-what
version: 6.1.0 version: 6.1.0
engines: {node: '>= 6'} engines: {node: '>= 6'}
dev: true
registry.npmmirror.com/csso@4.2.0: registry.npmmirror.com/csso@4.2.0:
resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/csso/-/csso-4.2.0.tgz} resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/csso/-/csso-4.2.0.tgz}
@ -6134,15 +6131,6 @@ packages:
css-tree: registry.npmmirror.com/css-tree@1.1.3 css-tree: registry.npmmirror.com/css-tree@1.1.3
dev: true dev: true
registry.npmmirror.com/cssstyle@3.0.0:
resolution: {integrity: sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/cssstyle/-/cssstyle-3.0.0.tgz}
name: cssstyle
version: 3.0.0
engines: {node: '>=14'}
dependencies:
rrweb-cssom: registry.npmmirror.com/rrweb-cssom@0.6.0
dev: false
registry.npmmirror.com/csstype@3.1.2: registry.npmmirror.com/csstype@3.1.2:
resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/csstype/-/csstype-3.1.2.tgz} resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/csstype/-/csstype-3.1.2.tgz}
name: csstype name: csstype
@ -6539,17 +6527,6 @@ packages:
version: 1.0.8 version: 1.0.8
dev: true dev: true
registry.npmmirror.com/data-urls@4.0.0:
resolution: {integrity: sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/data-urls/-/data-urls-4.0.0.tgz}
name: data-urls
version: 4.0.0
engines: {node: '>=14'}
dependencies:
abab: registry.npmmirror.com/abab@2.0.6
whatwg-mimetype: registry.npmmirror.com/whatwg-mimetype@3.0.0
whatwg-url: registry.npmmirror.com/whatwg-url@12.0.1
dev: false
registry.npmmirror.com/date-fns@2.30.0: registry.npmmirror.com/date-fns@2.30.0:
resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/date-fns/-/date-fns-2.30.0.tgz} resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/date-fns/-/date-fns-2.30.0.tgz}
name: date-fns name: date-fns
@ -6591,12 +6568,6 @@ packages:
dependencies: dependencies:
ms: registry.npmmirror.com/ms@2.1.2 ms: registry.npmmirror.com/ms@2.1.2
registry.npmmirror.com/decimal.js@10.4.3:
resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/decimal.js/-/decimal.js-10.4.3.tgz}
name: decimal.js
version: 10.4.3
dev: false
registry.npmmirror.com/decode-named-character-reference@1.0.2: registry.npmmirror.com/decode-named-character-reference@1.0.2:
resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz} resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz}
name: decode-named-character-reference name: decode-named-character-reference
@ -6769,6 +6740,16 @@ packages:
entities: registry.npmmirror.com/entities@2.2.0 entities: registry.npmmirror.com/entities@2.2.0
dev: true dev: true
registry.npmmirror.com/dom-serializer@2.0.0:
resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/dom-serializer/-/dom-serializer-2.0.0.tgz}
name: dom-serializer
version: 2.0.0
dependencies:
domelementtype: registry.npmmirror.com/domelementtype@2.3.0
domhandler: registry.npmmirror.com/domhandler@5.0.3
entities: registry.npmmirror.com/entities@4.5.0
dev: false
registry.npmmirror.com/domain-browser@4.23.0: registry.npmmirror.com/domain-browser@4.23.0:
resolution: {integrity: sha512-ArzcM/II1wCCujdCNyQjXrAFwS4mrLh4C7DZWlaI8mdh7h3BfKdNd3bKXITfl2PT9FtfQqaGvhi1vPRQPimjGA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/domain-browser/-/domain-browser-4.23.0.tgz} resolution: {integrity: sha512-ArzcM/II1wCCujdCNyQjXrAFwS4mrLh4C7DZWlaI8mdh7h3BfKdNd3bKXITfl2PT9FtfQqaGvhi1vPRQPimjGA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/domain-browser/-/domain-browser-4.23.0.tgz}
name: domain-browser name: domain-browser
@ -6780,16 +6761,6 @@ packages:
resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/domelementtype/-/domelementtype-2.3.0.tgz} resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/domelementtype/-/domelementtype-2.3.0.tgz}
name: domelementtype name: domelementtype
version: 2.3.0 version: 2.3.0
dev: true
registry.npmmirror.com/domexception@4.0.0:
resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/domexception/-/domexception-4.0.0.tgz}
name: domexception
version: 4.0.0
engines: {node: '>=12'}
dependencies:
webidl-conversions: registry.npmmirror.com/webidl-conversions@7.0.0
dev: false
registry.npmmirror.com/domhandler@4.3.1: registry.npmmirror.com/domhandler@4.3.1:
resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/domhandler/-/domhandler-4.3.1.tgz} resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/domhandler/-/domhandler-4.3.1.tgz}
@ -6800,6 +6771,15 @@ packages:
domelementtype: registry.npmmirror.com/domelementtype@2.3.0 domelementtype: registry.npmmirror.com/domelementtype@2.3.0
dev: true dev: true
registry.npmmirror.com/domhandler@5.0.3:
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/domhandler/-/domhandler-5.0.3.tgz}
name: domhandler
version: 5.0.3
engines: {node: '>= 4'}
dependencies:
domelementtype: registry.npmmirror.com/domelementtype@2.3.0
dev: false
registry.npmmirror.com/dompurify@3.0.6: registry.npmmirror.com/dompurify@3.0.6:
resolution: {integrity: sha512-ilkD8YEnnGh1zJ240uJsW7AzE+2qpbOUYjacomn3AvJ6J4JhKGSZ2nh4wUIXPZrEPppaCLx5jFe8T89Rk8tQ7w==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/dompurify/-/dompurify-3.0.6.tgz} resolution: {integrity: sha512-ilkD8YEnnGh1zJ240uJsW7AzE+2qpbOUYjacomn3AvJ6J4JhKGSZ2nh4wUIXPZrEPppaCLx5jFe8T89Rk8tQ7w==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/dompurify/-/dompurify-3.0.6.tgz}
name: dompurify name: dompurify
@ -6816,6 +6796,16 @@ packages:
domhandler: registry.npmmirror.com/domhandler@4.3.1 domhandler: registry.npmmirror.com/domhandler@4.3.1
dev: true dev: true
registry.npmmirror.com/domutils@3.1.0:
resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/domutils/-/domutils-3.1.0.tgz}
name: domutils
version: 3.1.0
dependencies:
dom-serializer: registry.npmmirror.com/dom-serializer@2.0.0
domelementtype: registry.npmmirror.com/domelementtype@2.3.0
domhandler: registry.npmmirror.com/domhandler@5.0.3
dev: false
registry.npmmirror.com/downloadjs@1.4.7: registry.npmmirror.com/downloadjs@1.4.7:
resolution: {integrity: sha512-LN1gO7+u9xjU5oEScGFKvXhYf7Y/empUIIEAGBs1LzUq/rg5duiDrkuH5A2lQGd5jfMOb9X9usDa2oVXwJ0U/Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/downloadjs/-/downloadjs-1.4.7.tgz} resolution: {integrity: sha512-LN1gO7+u9xjU5oEScGFKvXhYf7Y/empUIIEAGBs1LzUq/rg5duiDrkuH5A2lQGd5jfMOb9X9usDa2oVXwJ0U/Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/downloadjs/-/downloadjs-1.4.7.tgz}
name: downloadjs name: downloadjs
@ -8285,6 +8275,13 @@ packages:
space-separated-tokens: registry.npmmirror.com/space-separated-tokens@2.0.2 space-separated-tokens: registry.npmmirror.com/space-separated-tokens@2.0.2
dev: false dev: false
registry.npmmirror.com/he@1.2.0:
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/he/-/he-1.2.0.tgz}
name: he
version: 1.2.0
hasBin: true
dev: false
registry.npmmirror.com/heap@0.2.7: registry.npmmirror.com/heap@0.2.7:
resolution: {integrity: sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/heap/-/heap-0.2.7.tgz} resolution: {integrity: sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/heap/-/heap-0.2.7.tgz}
name: heap name: heap
@ -8321,15 +8318,6 @@ packages:
dependencies: dependencies:
react-is: registry.npmmirror.com/react-is@16.13.1 react-is: registry.npmmirror.com/react-is@16.13.1
registry.npmmirror.com/html-encoding-sniffer@3.0.0:
resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz}
name: html-encoding-sniffer
version: 3.0.0
engines: {node: '>=12'}
dependencies:
whatwg-encoding: registry.npmmirror.com/whatwg-encoding@2.0.0
dev: false
registry.npmmirror.com/html-parse-stringify@3.0.1: registry.npmmirror.com/html-parse-stringify@3.0.1:
resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz} resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz}
name: html-parse-stringify name: html-parse-stringify
@ -8337,17 +8325,15 @@ packages:
dependencies: dependencies:
void-elements: registry.npmmirror.com/void-elements@3.1.0 void-elements: registry.npmmirror.com/void-elements@3.1.0
registry.npmmirror.com/http-proxy-agent@5.0.0: registry.npmmirror.com/htmlparser2@8.0.2:
resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz} resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/htmlparser2/-/htmlparser2-8.0.2.tgz}
name: http-proxy-agent name: htmlparser2
version: 5.0.0 version: 8.0.2
engines: {node: '>= 6'}
dependencies: dependencies:
'@tootallnate/once': registry.npmmirror.com/@tootallnate/once@2.0.0 domelementtype: registry.npmmirror.com/domelementtype@2.3.0
agent-base: registry.npmmirror.com/agent-base@6.0.2 domhandler: registry.npmmirror.com/domhandler@5.0.3
debug: registry.npmmirror.com/debug@4.3.4 domutils: registry.npmmirror.com/domutils@3.1.0
transitivePeerDependencies: entities: registry.npmmirror.com/entities@4.5.0
- supports-color
dev: false dev: false
registry.npmmirror.com/https-browserify@1.0.0: registry.npmmirror.com/https-browserify@1.0.0:
@ -8356,18 +8342,6 @@ packages:
version: 1.0.0 version: 1.0.0
dev: true dev: true
registry.npmmirror.com/https-proxy-agent@5.0.1:
resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz}
name: https-proxy-agent
version: 5.0.1
engines: {node: '>= 6'}
dependencies:
agent-base: registry.npmmirror.com/agent-base@6.0.2
debug: registry.npmmirror.com/debug@4.3.4
transitivePeerDependencies:
- supports-color
dev: false
registry.npmmirror.com/human-signals@4.3.1: registry.npmmirror.com/human-signals@4.3.1:
resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/human-signals/-/human-signals-4.3.1.tgz} resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/human-signals/-/human-signals-4.3.1.tgz}
name: human-signals name: human-signals
@ -8743,12 +8717,6 @@ packages:
engines: {node: '>=12'} engines: {node: '>=12'}
dev: false dev: false
registry.npmmirror.com/is-potential-custom-element-name@1.0.1:
resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz}
name: is-potential-custom-element-name
version: 1.0.1
dev: false
registry.npmmirror.com/is-regex@1.1.4: registry.npmmirror.com/is-regex@1.1.4:
resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/is-regex/-/is-regex-1.1.4.tgz} resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/is-regex/-/is-regex-1.1.4.tgz}
name: is-regex name: is-regex
@ -8921,46 +8889,6 @@ packages:
engines: {node: '>=0.1.90'} engines: {node: '>=0.1.90'}
dev: false dev: false
registry.npmmirror.com/jsdom@22.1.0:
resolution: {integrity: sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/jsdom/-/jsdom-22.1.0.tgz}
name: jsdom
version: 22.1.0
engines: {node: '>=16'}
peerDependencies:
canvas: ^2.5.0
peerDependenciesMeta:
canvas:
optional: true
dependencies:
abab: registry.npmmirror.com/abab@2.0.6
cssstyle: registry.npmmirror.com/cssstyle@3.0.0
data-urls: registry.npmmirror.com/data-urls@4.0.0
decimal.js: registry.npmmirror.com/decimal.js@10.4.3
domexception: registry.npmmirror.com/domexception@4.0.0
form-data: registry.npmmirror.com/form-data@4.0.0
html-encoding-sniffer: registry.npmmirror.com/html-encoding-sniffer@3.0.0
http-proxy-agent: registry.npmmirror.com/http-proxy-agent@5.0.0
https-proxy-agent: registry.npmmirror.com/https-proxy-agent@5.0.1
is-potential-custom-element-name: registry.npmmirror.com/is-potential-custom-element-name@1.0.1
nwsapi: registry.npmmirror.com/nwsapi@2.2.7
parse5: registry.npmmirror.com/parse5@7.1.2
rrweb-cssom: registry.npmmirror.com/rrweb-cssom@0.6.0
saxes: registry.npmmirror.com/saxes@6.0.0
symbol-tree: registry.npmmirror.com/symbol-tree@3.2.4
tough-cookie: registry.npmmirror.com/tough-cookie@4.1.3
w3c-xmlserializer: registry.npmmirror.com/w3c-xmlserializer@4.0.0
webidl-conversions: registry.npmmirror.com/webidl-conversions@7.0.0
whatwg-encoding: registry.npmmirror.com/whatwg-encoding@2.0.0
whatwg-mimetype: registry.npmmirror.com/whatwg-mimetype@3.0.0
whatwg-url: registry.npmmirror.com/whatwg-url@12.0.1
ws: registry.npmmirror.com/ws@8.14.2
xml-name-validator: registry.npmmirror.com/xml-name-validator@4.0.0
transitivePeerDependencies:
- bufferutil
- supports-color
- utf-8-validate
dev: false
registry.npmmirror.com/jsesc@0.5.0: registry.npmmirror.com/jsesc@0.5.0:
resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/jsesc/-/jsesc-0.5.0.tgz} resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/jsesc/-/jsesc-0.5.0.tgz}
name: jsesc name: jsesc
@ -10343,6 +10271,24 @@ packages:
whatwg-url: registry.npmmirror.com/whatwg-url@5.0.0 whatwg-url: registry.npmmirror.com/whatwg-url@5.0.0
dev: false dev: false
registry.npmmirror.com/node-html-markdown@1.3.0:
resolution: {integrity: sha512-OeFi3QwC/cPjvVKZ114tzzu+YoR+v9UXW5RwSXGUqGb0qCl0DvP406tzdL7SFn8pZrMyzXoisfG2zcuF9+zw4g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/node-html-markdown/-/node-html-markdown-1.3.0.tgz}
name: node-html-markdown
version: 1.3.0
engines: {node: '>=10.0.0'}
dependencies:
node-html-parser: registry.npmmirror.com/node-html-parser@6.1.11
dev: false
registry.npmmirror.com/node-html-parser@6.1.11:
resolution: {integrity: sha512-FAgwwZ6h0DSDWxfD0Iq1tsDcBCxdJB1nXpLPPxX8YyVWzbfCjKWEzaynF4gZZ/8hziUmp7ZSaKylcn0iKhufUQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/node-html-parser/-/node-html-parser-6.1.11.tgz}
name: node-html-parser
version: 6.1.11
dependencies:
css-select: registry.npmmirror.com/css-select@5.1.0
he: registry.npmmirror.com/he@1.2.0
dev: false
registry.npmmirror.com/node-releases@2.0.13: registry.npmmirror.com/node-releases@2.0.13:
resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/node-releases/-/node-releases-2.0.13.tgz} resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/node-releases/-/node-releases-2.0.13.tgz}
name: node-releases name: node-releases
@ -10416,13 +10362,6 @@ packages:
version: 2.1.1 version: 2.1.1
dependencies: dependencies:
boolbase: registry.npmmirror.com/boolbase@1.0.0 boolbase: registry.npmmirror.com/boolbase@1.0.0
dev: true
registry.npmmirror.com/nwsapi@2.2.7:
resolution: {integrity: sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/nwsapi/-/nwsapi-2.2.7.tgz}
name: nwsapi
version: 2.2.7
dev: false
registry.npmmirror.com/object-assign@4.1.1: registry.npmmirror.com/object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz} resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz}
@ -10703,12 +10642,22 @@ packages:
json-parse-even-better-errors: registry.npmmirror.com/json-parse-even-better-errors@2.3.1 json-parse-even-better-errors: registry.npmmirror.com/json-parse-even-better-errors@2.3.1
lines-and-columns: registry.npmmirror.com/lines-and-columns@1.2.4 lines-and-columns: registry.npmmirror.com/lines-and-columns@1.2.4
registry.npmmirror.com/parse5-htmlparser2-tree-adapter@7.0.0:
resolution: {integrity: sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz}
name: parse5-htmlparser2-tree-adapter
version: 7.0.0
dependencies:
domhandler: registry.npmmirror.com/domhandler@5.0.3
parse5: registry.npmmirror.com/parse5@7.1.2
dev: false
registry.npmmirror.com/parse5@7.1.2: registry.npmmirror.com/parse5@7.1.2:
resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/parse5/-/parse5-7.1.2.tgz} resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/parse5/-/parse5-7.1.2.tgz}
name: parse5 name: parse5
version: 7.1.2 version: 7.1.2
dependencies: dependencies:
entities: registry.npmmirror.com/entities@4.5.0 entities: registry.npmmirror.com/entities@4.5.0
dev: false
registry.npmmirror.com/path-browserify@1.0.1: registry.npmmirror.com/path-browserify@1.0.1:
resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/path-browserify/-/path-browserify-1.0.1.tgz} resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/path-browserify/-/path-browserify-1.0.1.tgz}
@ -11060,12 +11009,6 @@ packages:
version: 1.1.0 version: 1.1.0
dev: false dev: false
registry.npmmirror.com/psl@1.9.0:
resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/psl/-/psl-1.9.0.tgz}
name: psl
version: 1.9.0
dev: false
registry.npmmirror.com/public-encrypt@4.0.3: registry.npmmirror.com/public-encrypt@4.0.3:
resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/public-encrypt/-/public-encrypt-4.0.3.tgz} resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/public-encrypt/-/public-encrypt-4.0.3.tgz}
name: public-encrypt name: public-encrypt
@ -11106,12 +11049,6 @@ packages:
engines: {node: '>=0.4.x'} engines: {node: '>=0.4.x'}
dev: true dev: true
registry.npmmirror.com/querystringify@2.2.0:
resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/querystringify/-/querystringify-2.2.0.tgz}
name: querystringify
version: 2.2.0
dev: false
registry.npmmirror.com/queue-microtask@1.2.3: registry.npmmirror.com/queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz} resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz}
name: queue-microtask name: queue-microtask
@ -11628,12 +11565,6 @@ packages:
engines: {node: '>=4'} engines: {node: '>=4'}
dev: false dev: false
registry.npmmirror.com/requires-port@1.0.0:
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/requires-port/-/requires-port-1.0.0.tgz}
name: requires-port
version: 1.0.0
dev: false
registry.npmmirror.com/resolve-from@4.0.0: registry.npmmirror.com/resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz} resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz}
name: resolve-from name: resolve-from
@ -11724,12 +11655,6 @@ packages:
fsevents: registry.npmmirror.com/fsevents@2.3.3 fsevents: registry.npmmirror.com/fsevents@2.3.3
dev: true dev: true
registry.npmmirror.com/rrweb-cssom@0.6.0:
resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz}
name: rrweb-cssom
version: 0.6.0
dev: false
registry.npmmirror.com/run-parallel@1.2.0: registry.npmmirror.com/run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz} resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz}
name: run-parallel name: run-parallel
@ -11820,15 +11745,6 @@ packages:
immutable: registry.npmmirror.com/immutable@4.3.4 immutable: registry.npmmirror.com/immutable@4.3.4
source-map-js: registry.npmmirror.com/source-map-js@1.0.2 source-map-js: registry.npmmirror.com/source-map-js@1.0.2
registry.npmmirror.com/saxes@6.0.0:
resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/saxes/-/saxes-6.0.0.tgz}
name: saxes
version: 6.0.0
engines: {node: '>=v12.22.7'}
dependencies:
xmlchars: registry.npmmirror.com/xmlchars@2.2.0
dev: false
registry.npmmirror.com/scheduler@0.23.0: registry.npmmirror.com/scheduler@0.23.0:
resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/scheduler/-/scheduler-0.23.0.tgz} resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/scheduler/-/scheduler-0.23.0.tgz}
name: scheduler name: scheduler
@ -12271,12 +12187,6 @@ packages:
stable: registry.npmmirror.com/stable@0.1.8 stable: registry.npmmirror.com/stable@0.1.8
dev: true dev: true
registry.npmmirror.com/symbol-tree@3.2.4:
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/symbol-tree/-/symbol-tree-3.2.4.tgz}
name: symbol-tree
version: 3.2.4
dev: false
registry.npmmirror.com/tapable@2.2.1: registry.npmmirror.com/tapable@2.2.1:
resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/tapable/-/tapable-2.2.1.tgz} resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/tapable/-/tapable-2.2.1.tgz}
name: tapable name: tapable
@ -12351,18 +12261,6 @@ packages:
version: 1.0.6 version: 1.0.6
dev: false dev: false
registry.npmmirror.com/tough-cookie@4.1.3:
resolution: {integrity: sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/tough-cookie/-/tough-cookie-4.1.3.tgz}
name: tough-cookie
version: 4.1.3
engines: {node: '>=6'}
dependencies:
psl: registry.npmmirror.com/psl@1.9.0
punycode: registry.npmmirror.com/punycode@2.3.0
universalify: registry.npmmirror.com/universalify@0.2.0
url-parse: registry.npmmirror.com/url-parse@1.5.10
dev: false
registry.npmmirror.com/tr46@0.0.3: registry.npmmirror.com/tr46@0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz} resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz}
name: tr46 name: tr46
@ -12378,15 +12276,6 @@ packages:
punycode: registry.npmmirror.com/punycode@2.3.0 punycode: registry.npmmirror.com/punycode@2.3.0
dev: false dev: false
registry.npmmirror.com/tr46@4.1.1:
resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/tr46/-/tr46-4.1.1.tgz}
name: tr46
version: 4.1.1
engines: {node: '>=14'}
dependencies:
punycode: registry.npmmirror.com/punycode@2.3.0
dev: false
registry.npmmirror.com/trim-lines@3.0.1: registry.npmmirror.com/trim-lines@3.0.1:
resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/trim-lines/-/trim-lines-3.0.1.tgz} resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/trim-lines/-/trim-lines-3.0.1.tgz}
name: trim-lines name: trim-lines
@ -12786,13 +12675,6 @@ packages:
unist-util-visit-parents: registry.npmmirror.com/unist-util-visit-parents@5.1.3 unist-util-visit-parents: registry.npmmirror.com/unist-util-visit-parents@5.1.3
dev: false dev: false
registry.npmmirror.com/universalify@0.2.0:
resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/universalify/-/universalify-0.2.0.tgz}
name: universalify
version: 0.2.0
engines: {node: '>= 4.0.0'}
dev: false
registry.npmmirror.com/update-browserslist-db@1.0.13(browserslist@4.22.1): registry.npmmirror.com/update-browserslist-db@1.0.13(browserslist@4.22.1):
resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz} resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz}
id: registry.npmmirror.com/update-browserslist-db/1.0.13 id: registry.npmmirror.com/update-browserslist-db/1.0.13
@ -12814,15 +12696,6 @@ packages:
punycode: registry.npmmirror.com/punycode@2.3.0 punycode: registry.npmmirror.com/punycode@2.3.0
dev: true dev: true
registry.npmmirror.com/url-parse@1.5.10:
resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/url-parse/-/url-parse-1.5.10.tgz}
name: url-parse
version: 1.5.10
dependencies:
querystringify: registry.npmmirror.com/querystringify@2.2.0
requires-port: registry.npmmirror.com/requires-port@1.0.0
dev: false
registry.npmmirror.com/url@0.11.3: registry.npmmirror.com/url@0.11.3:
resolution: {integrity: sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/url/-/url-0.11.3.tgz} resolution: {integrity: sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/url/-/url-0.11.3.tgz}
name: url name: url
@ -13073,15 +12946,6 @@ packages:
version: 3.1.0 version: 3.1.0
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
registry.npmmirror.com/w3c-xmlserializer@4.0.0:
resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz}
name: w3c-xmlserializer
version: 4.0.0
engines: {node: '>=14'}
dependencies:
xml-name-validator: registry.npmmirror.com/xml-name-validator@4.0.0
dev: false
registry.npmmirror.com/watchpack@2.4.0: registry.npmmirror.com/watchpack@2.4.0:
resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/watchpack/-/watchpack-2.4.0.tgz} resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/watchpack/-/watchpack-2.4.0.tgz}
name: watchpack name: watchpack
@ -13130,22 +12994,6 @@ packages:
engines: {node: '>=12'} engines: {node: '>=12'}
dev: false dev: false
registry.npmmirror.com/whatwg-encoding@2.0.0:
resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz}
name: whatwg-encoding
version: 2.0.0
engines: {node: '>=12'}
dependencies:
iconv-lite: registry.npmmirror.com/iconv-lite@0.6.3
dev: false
registry.npmmirror.com/whatwg-mimetype@3.0.0:
resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz}
name: whatwg-mimetype
version: 3.0.0
engines: {node: '>=12'}
dev: false
registry.npmmirror.com/whatwg-url@11.0.0: registry.npmmirror.com/whatwg-url@11.0.0:
resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/whatwg-url/-/whatwg-url-11.0.0.tgz} resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/whatwg-url/-/whatwg-url-11.0.0.tgz}
name: whatwg-url name: whatwg-url
@ -13156,16 +13004,6 @@ packages:
webidl-conversions: registry.npmmirror.com/webidl-conversions@7.0.0 webidl-conversions: registry.npmmirror.com/webidl-conversions@7.0.0
dev: false dev: false
registry.npmmirror.com/whatwg-url@12.0.1:
resolution: {integrity: sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/whatwg-url/-/whatwg-url-12.0.1.tgz}
name: whatwg-url
version: 12.0.1
engines: {node: '>=14'}
dependencies:
tr46: registry.npmmirror.com/tr46@4.1.1
webidl-conversions: registry.npmmirror.com/webidl-conversions@7.0.0
dev: false
registry.npmmirror.com/whatwg-url@5.0.0: registry.npmmirror.com/whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/whatwg-url/-/whatwg-url-5.0.0.tgz} resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/whatwg-url/-/whatwg-url-5.0.0.tgz}
name: whatwg-url name: whatwg-url
@ -13308,28 +13146,6 @@ packages:
name: wrappy name: wrappy
version: 1.0.2 version: 1.0.2
registry.npmmirror.com/ws@8.14.2:
resolution: {integrity: sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/ws/-/ws-8.14.2.tgz}
name: ws
version: 8.14.2
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: '>=5.0.2'
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
dev: false
registry.npmmirror.com/xml-name-validator@4.0.0:
resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz}
name: xml-name-validator
version: 4.0.0
engines: {node: '>=12'}
dev: false
registry.npmmirror.com/xmlbuilder@10.1.1: registry.npmmirror.com/xmlbuilder@10.1.1:
resolution: {integrity: sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/xmlbuilder/-/xmlbuilder-10.1.1.tgz} resolution: {integrity: sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/xmlbuilder/-/xmlbuilder-10.1.1.tgz}
name: xmlbuilder name: xmlbuilder
@ -13337,12 +13153,6 @@ packages:
engines: {node: '>=4.0'} engines: {node: '>=4.0'}
dev: false dev: false
registry.npmmirror.com/xmlchars@2.2.0:
resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/xmlchars/-/xmlchars-2.2.0.tgz}
name: xmlchars
version: 2.2.0
dev: false
registry.npmmirror.com/xtend@4.0.2: registry.npmmirror.com/xtend@4.0.2:
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/xtend/-/xtend-4.0.2.tgz} resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/xtend/-/xtend-4.0.2.tgz}
name: xtend name: xtend

View File

@ -19,7 +19,6 @@
"@fastgpt/global": "workspace:*", "@fastgpt/global": "workspace:*",
"@fastgpt/service": "workspace:*", "@fastgpt/service": "workspace:*",
"@fastgpt/web": "workspace:*", "@fastgpt/web": "workspace:*",
"@mozilla/readability": "^0.4.4",
"@node-rs/jieba": "^1.7.2", "@node-rs/jieba": "^1.7.2",
"@tanstack/react-query": "^4.24.10", "@tanstack/react-query": "^4.24.10",
"@types/nprogress": "^0.2.0", "@types/nprogress": "^0.2.0",
@ -35,7 +34,6 @@
"i18next": "^22.5.1", "i18next": "^22.5.1",
"immer": "^9.0.19", "immer": "^9.0.19",
"jschardet": "^3.0.0", "jschardet": "^3.0.0",
"jsdom": "^22.1.0",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mammoth": "^1.6.0", "mammoth": "^1.6.0",
@ -67,7 +65,6 @@
"@types/downloadjs": "^1.4.3", "@types/downloadjs": "^1.4.3",
"@types/formidable": "^2.0.5", "@types/formidable": "^2.0.5",
"@types/js-cookie": "^3.0.3", "@types/js-cookie": "^3.0.3",
"@types/jsdom": "^21.1.1",
"@types/jsonwebtoken": "^9.0.3", "@types/jsonwebtoken": "^9.0.3",
"@types/lodash": "^4.14.191", "@types/lodash": "^4.14.191",
"@types/multer": "^1.4.10", "@types/multer": "^1.4.10",

View File

@ -1,13 +1,11 @@
### Fast GPT V4.6.2 ### Fast GPT V4.6.2
1. 新增 - 团队空间 1. 商业版新增 - web站点同步
2. 新增 - 多路向量(多个向量映射一组数据) 2. 新增 - 集合元数据记录
3. 新增 - tts语音 3. 优化 - url 读取内容
4. 新增 - 语音输入 4. 优化 - 流读取文件,防止内存溢出
5. 新增 - 增强召回方式,提高召回精度 5. [知识库结构详解](https://doc.fastgpt.in/docs/use-cases/datasetengine/)
6. 优化 - 知识库导出,可直接触发流下载,无需等待转圈圈 6. [知识库提示词详解](https://doc.fastgpt.in/docs/use-cases/ai_settings/#引用模板--引用提示词)
7. [知识库结构详解](https://doc.fastgpt.in/docs/use-cases/datasetengine/) 7. [使用文档](https://doc.fastgpt.in/docs/intro/)
8. [知识库提示词详解](https://doc.fastgpt.in/docs/use-cases/ai_settings/#引用模板--引用提示词) 8. [点击查看高级编排介绍文档](https://doc.fastgpt.in/docs/workflow)
9. [使用文档](https://doc.fastgpt.in/docs/intro/) 9. [点击查看商业版](https://doc.fastgpt.in/docs/commercial/)
10. [点击查看高级编排介绍文档](https://doc.fastgpt.in/docs/workflow)
11. [点击查看商业版](https://doc.fastgpt.in/docs/commercial/)

View File

@ -3,7 +3,6 @@
"Cancel": "No", "Cancel": "No",
"Confirm": "Yes", "Confirm": "Yes",
"Create New": "Create", "Create New": "Create",
"Dataset": "Dataset",
"Export": "Export", "Export": "Export",
"Folder": "Folder", "Folder": "Folder",
"Move": "Move", "Move": "Move",
@ -129,6 +128,7 @@
"Choose": "Choose", "Choose": "Choose",
"Close": "Close", "Close": "Close",
"Collect": "Collect", "Collect": "Collect",
"Config": "Config",
"Confirm": "Confirm", "Confirm": "Confirm",
"Confirm Create": "Create", "Confirm Create": "Create",
"Confirm Move": "Move here", "Confirm Move": "Move here",
@ -138,7 +138,6 @@
"Course": "", "Course": "",
"Create Failed": "Create Failed", "Create Failed": "Create Failed",
"Create Success": "Create Success", "Create Success": "Create Success",
"Create Virtual File Failed": "Create Virtual File Failed",
"Custom Title": "Custom Title", "Custom Title": "Custom Title",
"Delete": "Delete", "Delete": "Delete",
"Delete Failed": "Delete Failed", "Delete Failed": "Delete Failed",
@ -152,6 +151,7 @@
"Filed is repeat": "Filed is repeated", "Filed is repeat": "Filed is repeated",
"Filed is repeated": "", "Filed is repeated": "",
"Input": "Input", "Input": "Input",
"Intro": "Intro",
"Last Step": "Last", "Last Step": "Last",
"Loading": "Loading", "Loading": "Loading",
"Max credit": "Credit", "Max credit": "Credit",
@ -190,6 +190,14 @@
"Update Time": "Update Time", "Update Time": "Update Time",
"Upload File Failed": "Upload File Failed", "Upload File Failed": "Upload File Failed",
"Username": "UserName", "Username": "UserName",
"Website": "Website",
"choosable": "choosable",
"confirm": {
"Common Tip": "Operational Confirm"
},
"empty": {
"Common Tip": "No data"
},
"error": { "error": {
"unKnow": "There was an accident" "unKnow": "There was an accident"
}, },
@ -207,11 +215,15 @@
"input": { "input": {
"Repeat Value": "Repeat Value" "Repeat Value": "Repeat Value"
}, },
"link": {
"UnValid": "UnValid Link"
},
"speech": { "speech": {
"error tip": "Speech Failed" "error tip": "Speech Failed"
} }
}, },
"core": { "core": {
"Max Token": "MaxTokens",
"ai": { "ai": {
"Model": "Model", "Model": "Model",
"Prompt": "Prompt" "Prompt": "Prompt"
@ -238,12 +250,13 @@
"Speech model": "Speech model", "Speech model": "Speech model",
"Speech speed": "Speed", "Speech speed": "Speed",
"Test Listen": "Test", "Test Listen": "Test",
"Test Listen Text": "Hello, this is FastGPT, how can I help you?", "Test Listen Text": "Hello, this is a voice test, if you can hear this sentence, it means that the voice playback function is normal",
"Web": "Browser (free)" "Web": "Browser (free)"
} }
}, },
"chat": { "chat": {
"Audio Speech Error": "Audio Speech Error", "Audio Speech Error": "Audio Speech Error",
"Quote Amount": "Dataset Quote:{{amount}}",
"Record": "Speech", "Record": "Speech",
"Restart": "Restart", "Restart": "Restart",
"Select File": "Select file", "Select File": "Select file",
@ -252,23 +265,46 @@
"Speaking": "I'm listening...", "Speaking": "I'm listening...",
"Stop Speak": "Stop Speak", "Stop Speak": "Stop Speak",
"Type a message": "Input problem", "Type a message": "Input problem",
"quote": {
"Quote Tip": "Only the actual reference content is displayed here. If the data is updated, it will not be updated in real time",
"Read Quote": "Read Quote",
"Read Source": "Read Source"
},
"tts": { "tts": {
"Stop Speech": "Stop" "Stop Speech": "Stop"
} }
}, },
"dataset": { "dataset": {
"All Dataset": "All Dataset",
"Choose Dataset": "Choose Dataset", "Choose Dataset": "Choose Dataset",
"Common Dataset": "Common Dataset",
"Common Dataset Desc": "Knowledge bases can be built by importing files, web links, or manual entry",
"Create dataset": "Create Dataset", "Create dataset": "Create Dataset",
"Dataset": "Dataset", "Dataset": "Dataset",
"Dataset Type": "Dataset Type",
"Delete Website Tips": "Confirm to delete the website",
"Empty Dataset": "",
"Empty Dataset Tips": "There is no knowledge base yet, go create one!",
"Folder Dataset": "Folder",
"Go Dataset": "To Dataset", "Go Dataset": "To Dataset",
"Intro Placeholder": "This dataset has not yet been introduced~",
"Quote Length": "Quote Length", "Quote Length": "Quote Length",
"Read Dataset": "Read Dataset", "Read Dataset": "Read Dataset",
"Search Top K": "Top K", "Search Top K": "Top K",
"Set Empty Result Tip": ",Response empty text", "Set Empty Result Tip": ",Response empty text",
"Set Website Config": "Configuring Website",
"Similarity": "Similarity", "Similarity": "Similarity",
"Sync Time": "Update Time", "Sync Time": "Update Time",
"Virtual File": "Virtual File", "Virtual File": "Virtual File",
"Website Dataset": "Website Sync",
"Website Dataset Desc": "Web site synchronization allows you to build a knowledge base directly from a web link",
"collection": { "collection": {
"Click top config website": "Config",
"Empty Tip": "The collection is empty",
"Website Create Success": "Created successfully, data is being synchronized",
"Website Empty Tip": "No associated website yet,",
"Website Link": "Website Link",
"Website Sync": "Website",
"metadata": { "metadata": {
"Chunk Size": "Chunk Size", "Chunk Size": "Chunk Size",
"Createtime": "Create Time", "Createtime": "Create Time",
@ -281,6 +317,10 @@
"source name": "Source Name", "source name": "Source Name",
"source size": "Source Size" "source size": "Source Size"
}, },
"status": {
"active": "Ready",
"syncing": "Syncing"
},
"training": { "training": {
"type chunk": "Chunk", "type chunk": "Chunk",
"type manual": "Manual", "type manual": "Manual",
@ -295,6 +335,10 @@
"file": "File", "file": "File",
"folder": "Folder", "folder": "Folder",
"import": { "import": {
"Fetch Error": "Get link failed",
"Fetch Url": "Url",
"Fetch url placeholder": "Up to 10 links, one per line.",
"Fetch url tip": "Only static links can be read, please check the results",
"Ideal chunk length": "Ideal chunk length", "Ideal chunk length": "Ideal chunk length",
"Ideal chunk length Tips": "Segment by end symbol. We recommend that your document should be properly punctuated to ensure that each complete sentence length does not exceed this value \n Chinese document recommended 400~1000\n English document recommended 600~1200" "Ideal chunk length Tips": "Segment by end symbol. We recommend that your document should be properly punctuated to ensure that each complete sentence length does not exceed this value \n Chinese document recommended 400~1000\n English document recommended 600~1200"
}, },
@ -316,6 +360,10 @@
}, },
"search mode": "Search Mode" "search mode": "Search Mode"
}, },
"status": {
"active": "Ready",
"syncing": "Syncing"
},
"test": { "test": {
"Test": "Start", "Test": "Start",
"Test Result": "Results", "Test Result": "Results",
@ -327,8 +375,18 @@
"test result tip": "The contents of the knowledge base are sorted according to their similarity to the test text, and you can adjust the corresponding text according to the test results. Note: The data in the test record may have been modified, clicking on a test data will show the latest data." "test result tip": "The contents of the knowledge base are sorted according to their similarity to the test text, and you can adjust the corresponding text according to the test results. Note: The data in the test record may have been modified, clicking on a test data will show the latest data."
}, },
"training": { "training": {
"Website Sync": "Website Sync",
"type chunk": "Chunk", "type chunk": "Chunk",
"type qa": "QA" "type qa": "QA"
},
"website": {
"Base Url": "BaseUrl",
"Config": "Website Configuring",
"Config Description": "The Web site synchronization function allows you to fill in the root address of a website, and the system will automatically crawl the relevant pages deeply for knowledge base training. Only crawls static websites, mainly project documents and blogs.",
"Confirm Create Tips": "Confirm to synchronize the site, the synchronization task will start later, please confirm!",
"Confirm Update Tips": "Are you sure to update the site configuration? The synchronization starts immediately with the new configuration. Please confirm",
"Selector": "Selector",
"Start Sync": "Start Sync"
} }
}, },
"module": { "module": {
@ -434,7 +492,6 @@
"Create Training Data": "Training-{{filename}}", "Create Training Data": "Training-{{filename}}",
"Create Virtual File Success": "Create Virtual File Success", "Create Virtual File Success": "Create Virtual File Success",
"Data Amount": "Data Amount", "Data Amount": "Data Amount",
"Ready": "Ready",
"Select Collection": "Select Collection", "Select Collection": "Select Collection",
"Select One Collection To Store": "Select the collection to store" "Select One Collection To Store": "Select the collection to store"
}, },
@ -466,6 +523,10 @@
"import csv tip": "Ensure that the CSV is in UTF-8 format; otherwise, garbled characters will be displayed", "import csv tip": "Ensure that the CSV is in UTF-8 format; otherwise, garbled characters will be displayed",
"test": { "test": {
"noResult": "Search results are empty" "noResult": "Search results are empty"
},
"website": {
"Base Url": "BaseUrl",
"Selector": "Selector"
} }
}, },
"error": { "error": {
@ -482,7 +543,6 @@
"Fetch Url": "Fetch Url", "Fetch Url": "Fetch Url",
"If the imported file is garbled, please convert CSV to UTF-8 encoding format": "If the imported file is garbled, please convert CSV to UTF-8 encoding format", "If the imported file is garbled, please convert CSV to UTF-8 encoding format": "If the imported file is garbled, please convert CSV to UTF-8 encoding format",
"Parse": "{{name}} Parsing...", "Parse": "{{name}} Parsing...",
"Ready": "Ready",
"Release the mouse to upload the file": "Release the mouse to upload the file", "Release the mouse to upload the file": "Release the mouse to upload the file",
"Select a maximum of 10 files": "Select a maximum of 10 files", "Select a maximum of 10 files": "Select a maximum of 10 files",
"Uploading": "Uploading: {{name}}, Progress: {{percent}}%", "Uploading": "Uploading: {{name}}, Progress: {{percent}}%",
@ -725,6 +785,10 @@
"ReRank": "ReRank", "ReRank": "ReRank",
"Whisper": "Whisper", "Whisper": "Whisper",
"bill username": "User" "bill username": "User"
},
"moduleName": {
"index": "Index Generation",
"qa": "QA Generation"
} }
} }
} }

View File

@ -3,7 +3,6 @@
"Cancel": "取消", "Cancel": "取消",
"Confirm": "确认", "Confirm": "确认",
"Create New": "新建", "Create New": "新建",
"Dataset": "知识库",
"Export": "导出", "Export": "导出",
"Folder": "文件夹", "Folder": "文件夹",
"Move": "移动", "Move": "移动",
@ -129,6 +128,7 @@
"Choose": "选择", "Choose": "选择",
"Close": "关闭", "Close": "关闭",
"Collect": "收藏", "Collect": "收藏",
"Config": "配置",
"Confirm": "确认", "Confirm": "确认",
"Confirm Create": "确认创建", "Confirm Create": "确认创建",
"Confirm Move": "移动到这", "Confirm Move": "移动到这",
@ -138,7 +138,6 @@
"Course": "", "Course": "",
"Create Failed": "创建异常", "Create Failed": "创建异常",
"Create Success": "创建成功", "Create Success": "创建成功",
"Create Virtual File Failed": "创建虚拟文件失败",
"Custom Title": "自定义标题", "Custom Title": "自定义标题",
"Delete": "删除", "Delete": "删除",
"Delete Failed": "删除失败", "Delete Failed": "删除失败",
@ -152,6 +151,7 @@
"Filed is repeat": "", "Filed is repeat": "",
"Filed is repeated": "字段重复了", "Filed is repeated": "字段重复了",
"Input": "输入", "Input": "输入",
"Intro": "介绍",
"Last Step": "上一步", "Last Step": "上一步",
"Loading": "加载中", "Loading": "加载中",
"Max credit": "最大金额", "Max credit": "最大金额",
@ -190,6 +190,14 @@
"Update Time": "更新时间", "Update Time": "更新时间",
"Upload File Failed": "上传文件失败", "Upload File Failed": "上传文件失败",
"Username": "用户名", "Username": "用户名",
"Website": "网站",
"choosable": "可选",
"confirm": {
"Common Tip": "操作确认"
},
"empty": {
"Common Tip": "没有什么数据噢~"
},
"error": { "error": {
"unKnow": "出现了点意外~" "unKnow": "出现了点意外~"
}, },
@ -207,11 +215,15 @@
"input": { "input": {
"Repeat Value": "有重复的值" "Repeat Value": "有重复的值"
}, },
"link": {
"UnValid": "无效的链接"
},
"speech": { "speech": {
"error tip": "语音转文字失败" "error tip": "语音转文字失败"
} }
}, },
"core": { "core": {
"Max Token": "单条数据上限",
"ai": { "ai": {
"Model": "AI 模型", "Model": "AI 模型",
"Prompt": "提示词" "Prompt": "提示词"
@ -238,12 +250,13 @@
"Speech model": "语音模型", "Speech model": "语音模型",
"Speech speed": "语速", "Speech speed": "语速",
"Test Listen": "试听", "Test Listen": "试听",
"Test Listen Text": "你好,我是 FastGPT有什么可以帮助你么", "Test Listen Text": "你好,这是语音测试,如果你能听到这句话,说明语音播放功能正常",
"Web": "浏览器自带(免费)" "Web": "浏览器自带(免费)"
} }
}, },
"chat": { "chat": {
"Audio Speech Error": "语音播报异常", "Audio Speech Error": "语音播报异常",
"Quote Amount": "知识库引用({{amount}}条)",
"Record": "语音输入", "Record": "语音输入",
"Restart": "重开对话", "Restart": "重开对话",
"Select File": "选择文件", "Select File": "选择文件",
@ -252,23 +265,46 @@
"Speaking": "我在听,请说...", "Speaking": "我在听,请说...",
"Stop Speak": "停止录音", "Stop Speak": "停止录音",
"Type a message": "输入问题", "Type a message": "输入问题",
"quote": {
"Quote Tip": "此处仅显示实际引用内容,若数据有更新,此处不会实时更新",
"Read Quote": "查看引用",
"Read Source": "查看来源"
},
"tts": { "tts": {
"Stop Speech": "停止" "Stop Speech": "停止"
} }
}, },
"dataset": { "dataset": {
"All Dataset": "全部知识库",
"Choose Dataset": "关联知识库", "Choose Dataset": "关联知识库",
"Common Dataset": "通用知识库",
"Common Dataset Desc": "可通过导入文件、网页链接或手动录入形式构建知识库",
"Create dataset": "创建一个知识库", "Create dataset": "创建一个知识库",
"Dataset": "知识库", "Dataset": "知识库",
"Dataset Type": "知识库类型",
"Delete Website Tips": "确认删除该站点?",
"Empty Dataset": "",
"Empty Dataset Tips": "还没有知识库,快去创建一个吧!",
"Folder Dataset": "文件夹",
"Go Dataset": "前往知识库", "Go Dataset": "前往知识库",
"Intro Placeholder": "这个知识库还没有介绍~",
"Quote Length": "引用内容长度", "Quote Length": "引用内容长度",
"Read Dataset": "查看知识库详情", "Read Dataset": "查看知识库详情",
"Search Top K": "单次搜索数量", "Search Top K": "单次搜索数量",
"Set Empty Result Tip": ",未搜索到内容时回复指定内容", "Set Empty Result Tip": ",未搜索到内容时回复指定内容",
"Set Website Config": "开始配置网站信息",
"Similarity": "相似度", "Similarity": "相似度",
"Sync Time": "最后更新时间", "Sync Time": "最后更新时间",
"Virtual File": "虚拟文件", "Virtual File": "虚拟文件",
"Website Dataset": "Web 站点同步",
"Website Dataset Desc": "Web 站点同步允许你直接使用一个网页链接构建知识库",
"collection": { "collection": {
"Click top config website": "点击配置网站",
"Empty Tip": "数据集空空如也",
"Website Create Success": "创建成功,正在同步数据",
"Website Empty Tip": "还没有关联网站,",
"Website Link": "Web 站点地址",
"Website Sync": "Web 站点同步",
"metadata": { "metadata": {
"Chunk Size": "分割大小", "Chunk Size": "分割大小",
"Createtime": "创建时间", "Createtime": "创建时间",
@ -281,9 +317,13 @@
"source name": "来源名", "source name": "来源名",
"source size": "来源大小" "source size": "来源大小"
}, },
"status": {
"active": "已就绪",
"syncing": "同步中"
},
"training": { "training": {
"type manual": "手动",
"type chunk": "直接分段", "type chunk": "直接分段",
"type manual": "手动",
"type qa": "问答拆分" "type qa": "问答拆分"
} }
}, },
@ -295,6 +335,10 @@
"file": "文件", "file": "文件",
"folder": "目录", "folder": "目录",
"import": { "import": {
"Fetch Error": "获取链接失败",
"Fetch Url": "网络链接",
"Fetch url placeholder": "最多10个链接每行一个。",
"Fetch url tip": "仅支持读取静态链接,请注意检查结果",
"Ideal chunk length": "理想分块长度", "Ideal chunk length": "理想分块长度",
"Ideal chunk length Tips": "按结束符号进行分段。我们建议您的文档应合理的使用标点符号,以确保每个完整的句子长度不要超过该值\n中文文档建议400~1000\n英文文档建议600~1200" "Ideal chunk length Tips": "按结束符号进行分段。我们建议您的文档应合理的使用标点符号,以确保每个完整的句子长度不要超过该值\n中文文档建议400~1000\n英文文档建议600~1200"
}, },
@ -316,6 +360,10 @@
}, },
"search mode": "检索模式" "search mode": "检索模式"
}, },
"status": {
"active": "已就绪",
"syncing": "同步中"
},
"test": { "test": {
"Test": "测试", "Test": "测试",
"Test Result": "测试结果", "Test Result": "测试结果",
@ -327,8 +375,18 @@
"test result tip": "根据知识库内容与测试文本的相似度进行排序,你可以根据测试结果调整对应的文本。\n注意测试记录中的数据可能已经被修改过点击某条测试数据后将展示最新的数据。" "test result tip": "根据知识库内容与测试文本的相似度进行排序,你可以根据测试结果调整对应的文本。\n注意测试记录中的数据可能已经被修改过点击某条测试数据后将展示最新的数据。"
}, },
"training": { "training": {
"Website Sync": "Web 站点同步",
"type chunk": "直接分段", "type chunk": "直接分段",
"type qa": "问答拆分" "type qa": "问答拆分"
},
"website": {
"Base Url": "根地址",
"Config": "Web站点配置",
"Config Description": "Web 站点同步功能允许你填写一个网站的根地址,系统会自动深度抓取相关的网页进行知识库训练。仅会抓取静态的网站,以项目文档、博客为主。",
"Confirm Create Tips": "确认同步该站点,同步任务将随后开启,请确认!",
"Confirm Update Tips": "确认更新站点配置?会立即按新的配置开始同步,请确认!",
"Selector": "选择器",
"Start Sync": "开始同步"
} }
}, },
"module": { "module": {
@ -434,7 +492,6 @@
"Create Training Data": "文件训练-{{filename}}", "Create Training Data": "文件训练-{{filename}}",
"Create Virtual File Success": "创建虚拟文件成功", "Create Virtual File Success": "创建虚拟文件成功",
"Data Amount": "数据总量", "Data Amount": "数据总量",
"Ready": "已就绪",
"Select Collection": "选择文件", "Select Collection": "选择文件",
"Select One Collection To Store": "选择一个文件进行存储" "Select One Collection To Store": "选择一个文件进行存储"
}, },
@ -466,6 +523,10 @@
"import csv tip": "请确保CSV为UTF-8格式否则会乱码", "import csv tip": "请确保CSV为UTF-8格式否则会乱码",
"test": { "test": {
"noResult": "搜索结果为空" "noResult": "搜索结果为空"
},
"website": {
"Base Url": "",
"Selector": ""
} }
}, },
"error": { "error": {
@ -482,7 +543,6 @@
"Fetch Url": "链接读取", "Fetch Url": "链接读取",
"If the imported file is garbled, please convert CSV to UTF-8 encoding format": "如果导入文件乱码,请将 CSV 转成 UTF-8 编码格式", "If the imported file is garbled, please convert CSV to UTF-8 encoding format": "如果导入文件乱码,请将 CSV 转成 UTF-8 编码格式",
"Parse": "{{name}} 解析中...", "Parse": "{{name}} 解析中...",
"Ready": "",
"Release the mouse to upload the file": "松开鼠标上传文件", "Release the mouse to upload the file": "松开鼠标上传文件",
"Select a maximum of 10 files": "最多选择10个文件", "Select a maximum of 10 files": "最多选择10个文件",
"Uploading": "正在上传 {{name}},进度: {{percent}}%", "Uploading": "正在上传 {{name}},进度: {{percent}}%",
@ -725,6 +785,10 @@
"ReRank": "结果重排", "ReRank": "结果重排",
"Whisper": "语音输入", "Whisper": "语音输入",
"bill username": "用户" "bill username": "用户"
},
"moduleName": {
"index": "索引生成",
"qa": "QA 拆分"
} }
} }
} }

View File

@ -74,8 +74,8 @@ const MessageInput = ({
try { try {
const src = await compressImgFileAndUpload({ const src = await compressImgFileAndUpload({
file: file.rawFile, file: file.rawFile,
maxW: 1000, maxW: 4329,
maxH: 1000, maxH: 4329,
maxSize: 1024 * 1024 * 5, maxSize: 1024 * 1024 * 5,
// 30 day expired. // 30 day expired.
expiredTime: addDays(new Date(), 30) expiredTime: addDays(new Date(), 30)

View File

@ -71,9 +71,9 @@ const QuoteModal = ({
iconSrc="/imgs/modal/quote.svg" iconSrc="/imgs/modal/quote.svg"
title={ title={
<Box> <Box>
({rawSearch.length}) {t('core.chat.Quote Amount', { amount: rawSearch.length })}
<Box fontSize={'10px'} color={'myGray.500'} fontWeight={'normal'}> <Box fontSize={'sm'} color={'myGray.500'} fontWeight={'normal'}>
注意: 修改知识库内容成功后 {t('core.chat.quote.Quote Tip')}
</Box> </Box>
</Box> </Box>
} }

View File

@ -1,7 +1,7 @@
import React, { useMemo, useState } from 'react'; import React, { useMemo, useState } from 'react';
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/api.d'; import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/api.d';
import type { ChatItemType } from '@fastgpt/global/core/chat/type'; import type { ChatItemType } from '@fastgpt/global/core/chat/type';
import { Flex, BoxProps, useDisclosure, Image, useTheme } from '@chakra-ui/react'; import { Flex, BoxProps, useDisclosure, Image, useTheme, Box } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useSystemStore } from '@/web/common/system/useSystemStore';
import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type'; import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
@ -11,6 +11,8 @@ import MyTooltip from '../MyTooltip';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant'; import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
import { getSourceNameIcon } from '@fastgpt/global/core/dataset/utils'; import { getSourceNameIcon } from '@fastgpt/global/core/dataset/utils';
import ChatBoxDivider from '@/components/core/chat/Divider'; import ChatBoxDivider from '@/components/core/chat/Divider';
import MyIcon from '../Icon';
import { getFileAndOpen } from '@/web/core/dataset/utils';
const QuoteModal = dynamic(() => import('./QuoteModal'), { ssr: false }); const QuoteModal = dynamic(() => import('./QuoteModal'), { ssr: false });
const ContextModal = dynamic(() => import('./ContextModal'), { ssr: false }); const ContextModal = dynamic(() => import('./ContextModal'), { ssr: false });
@ -59,6 +61,7 @@ const ResponseTags = ({ responseData = [] }: { responseData?: ChatHistoryItemRes
.flat() .flat()
.map((item) => ({ .map((item) => ({
sourceName: item.sourceName, sourceName: item.sourceName,
sourceId: item.sourceId,
icon: getSourceNameIcon({ sourceId: item.sourceId, sourceName: item.sourceName }) icon: getSourceNameIcon({ sourceId: item.sourceId, sourceName: item.sourceName })
})), })),
historyPreview: chatData?.historyPreview, historyPreview: chatData?.historyPreview,
@ -83,18 +86,67 @@ const ResponseTags = ({ responseData = [] }: { responseData?: ChatHistoryItemRes
alignItems={'center'} alignItems={'center'}
flexWrap={'wrap'} flexWrap={'wrap'}
fontSize={'sm'} fontSize={'sm'}
cursor={'pointer'}
border={theme.borders.sm} border={theme.borders.sm}
py={1} py={1}
px={2} px={2}
borderRadius={'md'} borderRadius={'md'}
_hover={{ _hover={{
bg: 'myBlue.100' '.controller': {
display: 'flex'
}
}} }}
overflow={'hidden'}
position={'relative'}
onClick={() => setQuoteModalData(quoteList)} onClick={() => setQuoteModalData(quoteList)}
> >
<Image src={item.icon} alt={''} mr={1} w={'12px'} /> <Image src={item.icon} alt={''} mr={1} w={'12px'} />
{item.sourceName} <Box className="textEllipsis" flex={'1 0 0'}>
{item.sourceName}
</Box>
<Box
className="controller"
display={['flex', 'none']}
pr={2}
position={'absolute'}
right={0}
left={0}
justifyContent={'flex-end'}
alignItems={'center'}
h={'100%'}
lineHeight={0}
bg={`linear-gradient(to left, white,white ${
item.sourceId ? '60px' : '30px'
}, rgba(255,255,255,0) 80%)`}
>
<MyTooltip label={t('core.chat.quote.Read Quote')}>
<MyIcon
name="common/viewLight"
w={'14px'}
cursor={'pointer'}
_hover={{
color: 'green.600'
}}
/>
</MyTooltip>
{item.sourceId && (
<MyTooltip label={t('core.chat.quote.Read Source')}>
<MyIcon
ml={4}
name="common/routePushLight"
w={'14px'}
cursor={'pointer'}
_hover={{ color: 'myBlue.600' }}
onClick={async (e) => {
e.stopPropagation();
if (!item.sourceId) return;
await getFileAndOpen(item.sourceId);
}}
/>
</MyTooltip>
)}
</Box>
</Flex> </Flex>
))} ))}
</Flex> </Flex>

View File

@ -1,14 +1,6 @@
.stopIcon { .stopIcon {
animation: zoomStopIcon 0.4s infinite alternate; animation: zoomStopIcon 0.4s infinite alternate;
} }
@keyframes zoomStopIcon {
0% {
transform: scale(0.8);
}
100% {
transform: scale(1.2);
}
}
.statusAnimation { .statusAnimation {
animation: statusBox 0.8s linear infinite alternate; animation: statusBox 0.8s linear infinite alternate;

View File

@ -1,17 +1,19 @@
import React from 'react'; import React from 'react';
import { Flex, Box, FlexProps } from '@chakra-ui/react'; import { Flex, Box, FlexProps } from '@chakra-ui/react';
import MyIcon from '../Icon'; import MyIcon from '../Icon';
import { useTranslation } from 'next-i18next';
type Props = FlexProps & { type Props = FlexProps & {
text?: string | null; text?: string | React.ReactNode;
}; };
const EmptyTip = ({ text, ...props }: Props) => { const EmptyTip = ({ text, ...props }: Props) => {
const { t } = useTranslation();
return ( return (
<Flex mt={5} flexDirection={'column'} alignItems={'center'} pt={'10vh'} {...props}> <Flex mt={5} flexDirection={'column'} alignItems={'center'} pt={'10vh'} {...props}>
<MyIcon name="empty" w={'48px'} h={'48px'} color={'transparent'} /> <MyIcon name="empty" w={'48px'} h={'48px'} color={'transparent'} />
<Box mt={2} color={'myGray.500'}> <Box mt={2} color={'myGray.500'}>
{text || '没有什么数据噢~'} {text || t('common.empty.Common Tip')}
</Box> </Box>
</Flex> </Flex>
); );

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1701410305116" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8647" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M138.9056 180.5312m94.7712 0l563.3536 0q94.7712 0 94.7712 94.7712l0 576q0 94.7712-94.7712 94.7712l-563.3536 0q-94.7712 0-94.7712-94.7712l0-576q0-94.7712 94.7712-94.7712Z" fill="#CCDAFF" p-id="8648"></path><path d="M292.3008 81.92m64.768 0l305.3056 0q64.768 0 64.768 64.768l0 70.4q0 64.768-64.768 64.768l-305.3056 0q-64.768 0-64.768-64.768l0-70.4q0-64.768 64.768-64.768Z" fill="#7A7AF9" p-id="8649"></path><path d="M480.768 736.8704a49.408 49.408 0 0 1-34.7648-14.2848L332.8 611.3792a49.6128 49.6128 0 1 1 69.7344-70.7072l77.7216 76.4928L634.5216 460.8a49.6128 49.6128 0 1 1 70.6048 69.6832L516.096 721.92a49.6128 49.6128 0 0 1-35.328 14.9504z" fill="#7A7AF9" p-id="8650"></path></svg>

After

Width:  |  Height:  |  Size: 1017 B

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1701403554068" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5098" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M572.074667 337.134933a57.275733 57.275733 0 1 0-114.176-6.007466h-0.170667l12.936533 272.896v0.443733a44.544 44.544 0 1 0 89.019734-1.194667l12.424533-266.1376z m-196.949334-191.214933c76.151467-130.048 199.816533-129.774933 275.797334 0l340.3776 581.085867c76.117333 130.048 15.7696 235.4176-135.236267 235.4176H169.984c-150.8352 0-211.217067-105.6768-135.202133-235.4176L375.125333 145.92z m140.049067 687.581867a57.275733 57.275733 0 1 0 0-114.517334 57.275733 57.275733 0 0 0 0 114.517334z" fill="#FB6547" p-id="5099"></path></svg>

After

Width:  |  Height:  |  Size: 869 B

View File

@ -0,0 +1,11 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1701418384907"
class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11367"
xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128">
<path
d="M892 928.1H134c-19.9 0-36-16.1-36-36v-758c0-19.9 16.1-36 36-36h314.1c19.9 0 36 16.1 36 36s-16.1 36-36 36H170v686h686V579.6c0-19.9 16.1-36 36-36s36 16.1 36 36v312.5c0 19.9-16.1 36-36 36z"
p-id="11368"></path>
<path
d="M927.9 131.6v-0.5c-0.1-1.7-0.4-3.3-0.7-4.9 0-0.1 0-0.2-0.1-0.3-0.4-1.7-0.9-3.3-1.5-4.9v-0.1c-0.6-1.6-1.4-3.1-2.2-4.6 0-0.1-0.1-0.1-0.1-0.2-0.8-1.4-1.7-2.8-2.7-4.1-0.1-0.1-0.2-0.3-0.3-0.4-0.5-0.6-0.9-1.1-1.4-1.7 0-0.1-0.1-0.1-0.1-0.2-0.5-0.6-1-1.1-1.6-1.6l-0.4-0.4c-0.5-0.5-1.1-1-1.6-1.5l-0.1-0.1c-0.6-0.5-1.2-1-1.9-1.4-0.1-0.1-0.3-0.2-0.4-0.3-1.4-1-2.8-1.8-4.3-2.6l-0.1-0.1c-1.6-0.8-3.2-1.5-4.9-2-1.6-0.5-3.3-1-5-1.2-0.1 0-0.2 0-0.3-0.1l-2.4-0.3h-0.3c-0.7-0.1-1.3-0.1-2-0.1H640.1c-19.9 0-36 16.1-36 36s16.1 36 36 36h165L487.6 487.6c-14.1 14.1-14.1 36.9 0 50.9 7 7 16.2 10.5 25.5 10.5 9.2 0 18.4-3.5 25.5-10.5L856 221v162.8c0 19.9 16.1 36 36 36s36-16.1 36-36V134.1c0-0.8 0-1.7-0.1-2.5z"
p-id="11369"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1701420318127" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="13238" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M987.52 367.36c-108.16-146.56-266.88-239.36-444.16-239.36-177.28 0-335.36 92.8-444.16 239.36-48 64.64-48 160.64 0 225.28C208 739.2 366.72 832 543.36 832c177.28 0 335.36-92.8 444.16-239.36C1035.52 528 1035.52 432 987.52 367.36zM939.52 539.52C839.04 684.8 694.4 768 542.72 768c-151.68 0-296.32-83.2-396.8-228.48C121.6 505.6 121.6 455.04 145.92 420.48 246.4 275.2 391.04 192 542.72 192c151.68 0 296.32 83.2 396.8 228.48C963.2 455.04 963.2 505.6 939.52 539.52zM544 256C420.48 256 320 356.48 320 480S420.48 704 544 704 768 603.52 768 480 667.52 256 544 256zM544 640C455.68 640 384 568.32 384 480S455.68 320 544 320C632.32 320 704 391.68 704 480S632.32 640 544 640z" p-id="13239"></path></svg>

After

Width:  |  Height:  |  Size: 1021 B

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1697622173220" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2398" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M722.36246914 1024.8960632h-415.54678519c-120.26260543 0-218.12969876-97.86709333-218.12969877-218.12969875v-586.42583704c0-120.26260543 97.86709333-218.12969876 218.12969877-218.12969877h415.54678519c120.26260543 0 218.12969876 97.86709333 218.12969876 218.12969877v586.42583704c0 120.26260543-97.86709333 218.12969876-218.12969876 218.12969875z m-415.54678519-898.28010667c-51.65207703 0-93.72457086 42.07249383-93.72457086 93.72457088v586.42583704c0 51.65207703 42.07249383 93.72457086 93.72457086 93.72457086h415.54678519c51.65207703 0 93.72457086-42.07249383 93.72457086-93.72457086v-586.42583704c0-51.65207703-42.07249383-93.72457086-93.72457086-93.72457088h-415.54678519z" fill="#BDD2EF" p-id="2399"></path><path d="M684.0441363 599.76969482H338.27296395c-42.07249383 0-76.11885037-34.04635653-76.11885037-76.11885037s34.04635653-76.11885037 76.11885037-76.11885037h345.77117235c42.07249383 0 76.11885037 34.04635653 76.11885037 76.11885037s-34.17581037 76.11885037-76.11885037 76.11885037zM465.00826075 360.66847605h-126.7352968c-42.07249383 0-76.11885037-34.04635653-76.11885037-76.11885036s34.04635653-76.11885037 76.11885037-76.11885038h126.7352968c42.07249383 0 76.11885037 34.04635653 76.11885036 76.11885038s-34.04635653 76.11885037-76.11885036 76.11885036zM684.0441363 360.66847605h-25.37295013c-42.07249383 0-76.11885037-34.04635653-76.11885036-76.11885036s34.04635653-76.11885037 76.11885036-76.11885038h25.37295013c42.07249383 0 76.11885037 34.04635653 76.11885037 76.11885038s-34.17581037 76.11885037-76.11885037 76.11885036zM684.0441363 838.87091358H338.27296395c-42.07249383 0-76.11885037-34.04635653-76.11885037-76.11885038s34.04635653-76.11885037 76.11885037-76.11885037h345.77117235c42.07249383 0 76.11885037 34.04635653 76.11885037 76.11885037s-34.17581037 76.11885037-76.11885037 76.11885038z" fill="#2867CE" p-id="2400"></path></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1694141197423" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4891" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M855.04 385.024q19.456 2.048 38.912 10.24t33.792 23.04 21.504 37.376 2.048 54.272q-2.048 8.192-8.192 40.448t-14.336 74.24-18.432 86.528-19.456 76.288q-5.12 18.432-14.848 37.888t-25.088 35.328-36.864 26.112-51.2 10.24l-567.296 0q-21.504 0-44.544-9.216t-42.496-26.112-31.744-40.96-12.288-53.76l0-439.296q0-62.464 33.792-97.792t95.232-35.328l503.808 0q22.528 0 46.592 8.704t43.52 24.064 31.744 35.84 12.288 44.032l0 11.264-53.248 0q-40.96 0-95.744-0.512t-116.736-0.512-115.712-0.512-92.672-0.512l-47.104 0q-26.624 0-41.472 16.896t-23.04 44.544q-8.192 29.696-18.432 62.976t-18.432 61.952q-10.24 33.792-20.48 65.536-2.048 8.192-2.048 13.312 0 17.408 11.776 29.184t29.184 11.776q31.744 0 43.008-39.936l54.272-198.656q133.12 1.024 243.712 1.024l286.72 0z" fill="#FFCC66" p-id="4892"></path></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,14 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1701324062325"
class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4651"
xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128">
<path d="M512 512m-512 0a512 512 0 1 0 1024 0 512 512 0 1 0-1024 0Z" fill="#c6dafe" p-id="4652"></path>
<path d="M486.4 665.6h51.2v153.6h-51.2z" fill="#515151" p-id="4653"
data-spm-anchor-id="a313x.search_index.0.i3.5ac83a811R1Mg6" class="selected"></path>
<path
d="M435.2 768h153.6v51.2h-153.6zM230.4 281.6v384h563.2V281.6H230.4z m0-51.2h563.2a51.2 51.2 0 0 1 51.2 51.2v384a51.2 51.2 0 0 1-51.2 51.2H230.4a51.2 51.2 0 0 1-51.2-51.2V281.6a51.2 51.2 0 0 1 51.2-51.2z"
fill="#515151" p-id="4654" data-spm-anchor-id="a313x.search_index.0.i1.5ac83a811R1Mg6" class="selected"></path>
<path
d="M662.272 388.864c6.144-21.5296 8.448-63.0272-29.952-68.3776-30.72-3.84-67.584 16.128-86.8352 29.2096-6.144-0.768-13.056-1.536-19.968-1.536-49.9712-0.7936-82.2016 16.128-109.8496 52.224-9.984 13.056-19.2 35.3536-22.2976 60.672 13.824-23.808 56.8576-67.584 102.1952-85.248 0 0-68.352 49.152-102.1952 118.3232v0.768c-1.5616 3.072-3.072 6.144-4.6336 9.984-33.024 82.2016-6.144 117.5552 19.2256 123.6992 23.04 6.144 55.3216-5.376 81.408-33.024 43.776 9.984 87.6288-1.536 103.7312-9.984 30.72-16.896 52.2496-46.848 56.8832-77.6192h-84.5312s-3.84 26.88-49.1776 26.88c-41.472 0-43.776-48.384-43.776-48.384h178.9952s3.072-52.2496-22.272-86.8352c-14.6176-19.2-33.8176-36.1216-60.6976-45.312 8.448-6.144 22.272-15.4112 34.56-18.4832 23.04-6.144 38.4256-2.304 48.4096 14.592 13.056 23.04-7.68 76.0832-7.68 76.0832s12.288-15.36 18.432-37.632z m-185.1648 203.6224c-35.328 29.2096-64.512 25.3696-76.0576 8.4736-9.984-15.36-11.52-42.2656 0-79.8976 5.376 14.592 13.824 28.416 26.112 40.704 15.36 15.36 32.256 25.344 49.9456 30.72z m-1.536-149.8368s1.536-34.56 39.168-37.632c32.3072-3.072 49.2032 11.52 54.5536 39.1936l-93.7472-1.536z"
fill="#2394FB" p-id="4655"></path>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -114,7 +114,14 @@ const iconPaths = {
'core/dataset/modeEmbFTRerank': () => import('./icons/core/dataset/modeEmbFTRerank.svg'), 'core/dataset/modeEmbFTRerank': () => import('./icons/core/dataset/modeEmbFTRerank.svg'),
'core/app/variable/input': () => import('./icons/core/app/variable/input.svg'), 'core/app/variable/input': () => import('./icons/core/app/variable/input.svg'),
'core/app/variable/textarea': () => import('./icons/core/app/variable/textarea.svg'), 'core/app/variable/textarea': () => import('./icons/core/app/variable/textarea.svg'),
'core/app/variable/select': () => import('./icons/core/app/variable/select.svg') 'core/app/variable/select': () => import('./icons/core/app/variable/select.svg'),
'core/dataset/websiteDataset': () => import('./icons/core/dataset/websiteDataset.svg'),
'core/dataset/commonDataset': () => import('./icons/core/dataset/commonDataset.svg'),
'core/dataset/folderDataset': () => import('./icons/core/dataset/folderDataset.svg'),
'common/confirm/deleteTip': () => import('./icons/common/confirm/deleteTip.svg'),
'common/confirm/commonTip': () => import('./icons/common/confirm/commonTip.svg'),
'common/routePushLight': () => import('./icons/common/routePushLight.svg'),
'common/viewLight': () => import('./icons/common/viewLight.svg')
}; };
export type IconName = keyof typeof iconPaths; export type IconName = keyof typeof iconPaths;

View File

@ -111,7 +111,7 @@ const Layout = ({ children }: { children: JSX.Element }) => {
</> </>
)} )}
</Box> </Box>
<Loading loading={loading} zIndex={9999} /> <Loading loading={loading} zIndex={999999} />
{!!userInfo && <UpdateInviteModal />} {!!userInfo && <UpdateInviteModal />}
</> </>
); );

View File

@ -1,30 +1,33 @@
import { Box, Flex } from '@chakra-ui/react'; import { Box, Flex, Grid } from '@chakra-ui/react';
import MdImage from '../img/Image'; import MdImage from '../img/Image';
import { useMemo } from 'react'; import { useMemo } from 'react';
const ImageBlock = ({ images }: { images: string }) => { const ImageBlock = ({ images }: { images: string }) => {
const formatData = useMemo( const formatData = useMemo(
() => () =>
images.split('\n').map((item) => { images
try { .split('\n')
return JSON.parse(item) as { src: string }; .filter((item) => item)
} catch (error) { .map((item) => {
return { src: '' }; try {
} return JSON.parse(item) as { src: string };
}), } catch (error) {
return { src: '' };
}
}),
[images] [images]
); );
return ( return (
<Flex alignItems={'center'} wrap={'wrap'} gap={4}> <Grid gridTemplateColumns={['1fr', '1fr 1fr']} gap={4}>
{formatData.map(({ src }) => { {formatData.map(({ src }) => {
return ( return (
<Box key={src} rounded={'md'} flex={'0 0 auto'} w={'120px'}> <Box key={src} rounded={'md'} flex={'1 0 0'} minW={'120px'}>
<MdImage src={src} /> <MdImage src={src} />
</Box> </Box>
); );
})} })}
</Flex> </Grid>
); );
}; };

View File

@ -29,7 +29,6 @@ const MdImage = ({ src }: { src?: string }) => {
borderRadius={'md'} borderRadius={'md'}
src={src} src={src}
alt={''} alt={''}
maxH={'150px'}
fallbackSrc={'/imgs/errImg.png'} fallbackSrc={'/imgs/errImg.png'}
fallbackStrategy={'onError'} fallbackStrategy={'onError'}
cursor={succeed ? 'pointer' : 'default'} cursor={succeed ? 'pointer' : 'default'}

View File

@ -9,6 +9,7 @@ import {
Box, Box,
Image Image
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import MyIcon from '../Icon';
export interface MyModalProps extends ModalContentProps { export interface MyModalProps extends ModalContentProps {
iconSrc?: string; iconSrc?: string;
@ -56,7 +57,15 @@ const MyModal = ({
roundedTop={'lg'} roundedTop={'lg'}
py={'10px'} py={'10px'}
> >
{iconSrc && <Image mr={3} objectFit={'contain'} alt="" src={iconSrc} w={'20px'} />} {iconSrc && (
<>
{iconSrc.startsWith('/') ? (
<Image mr={3} objectFit={'contain'} alt="" src={iconSrc} w={'20px'} />
) : (
<MyIcon mr={3} name={iconSrc as any} w={'20px'} />
)}
</>
)}
{title} {title}
<Box flex={1} /> <Box flex={1} />
{onClose && <ModalCloseButton position={'relative'} top={0} right={0} />} {onClose && <ModalCloseButton position={'relative'} top={0} right={0} />}

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Box, Flex, useTheme, Grid, type GridProps, theme } from '@chakra-ui/react'; import { Box, Flex, useTheme, Grid, type GridProps, theme, Image } from '@chakra-ui/react';
import MyIcon from '@/components/Icon'; import MyIcon from '@/components/Icon';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
@ -45,8 +45,7 @@ const MyRadio = ({
: { : {
bg: 'myWhite.300', bg: 'myWhite.300',
_hover: { _hover: {
bg: '#f5f8ff', borderColor: 'myBlue.500'
borderColor: '#b2ccff'
} }
})} })}
_after={{ _after={{
@ -71,7 +70,15 @@ const MyRadio = ({
}} }}
onClick={() => onChange(item.value)} onClick={() => onChange(item.value)}
> >
{!!item.icon && <MyIcon mr={'14px'} name={item.icon as any} w={iconSize} />} {!!item.icon && (
<>
{item.icon.startsWith('/') ? (
<Image src={item.icon} mr={'14px'} w={iconSize} alt={''} />
) : (
<MyIcon mr={'14px'} name={item.icon as any} w={iconSize} />
)}
</>
)}
<Box pr={2}> <Box pr={2}>
<Box>{t(item.title)}</Box> <Box>{t(item.title)}</Box>
{!!item.desc && ( {!!item.desc && (

View File

@ -7,29 +7,20 @@ import {
ModalBody, ModalBody,
ModalFooter, ModalFooter,
useTheme, useTheme,
Textarea,
Grid, Grid,
Divider, Divider
Switch,
Image
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import Avatar from '@/components/Avatar'; import Avatar from '@/components/Avatar';
import { useForm } from 'react-hook-form';
import { QuestionOutlineIcon } from '@chakra-ui/icons';
import type { SelectedDatasetType } from '@fastgpt/global/core/module/api.d'; import type { SelectedDatasetType } from '@fastgpt/global/core/module/api.d';
import { useToast } from '@/web/common/hooks/useToast'; import { useToast } from '@/web/common/hooks/useToast';
import MySlider from '@/components/Slider';
import MyTooltip from '@/components/MyTooltip'; import MyTooltip from '@/components/MyTooltip';
import MyModal from '@/components/MyModal';
import MyIcon from '@/components/Icon'; import MyIcon from '@/components/Icon';
import { DatasetSearchModeEnum, DatasetTypeEnum } from '@fastgpt/global/core/dataset/constant'; import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constant';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { useDatasetStore } from '@/web/core/dataset/store/dataset'; import { useDatasetStore } from '@/web/core/dataset/store/dataset';
import { feConfigs } from '@/web/common/system/staticData';
import DatasetSelectContainer, { useDatasetSelect } from '@/components/core/dataset/SelectModal'; import DatasetSelectContainer, { useDatasetSelect } from '@/components/core/dataset/SelectModal';
import { useLoading } from '@/web/common/hooks/useLoading'; import { useLoading } from '@/web/common/hooks/useLoading';
import EmptyTip from '@/components/EmptyTip'; import EmptyTip from '@/components/EmptyTip';
import { AppSimpleEditFormType } from '@fastgpt/global/core/app/type';
export const DatasetSelectModal = ({ export const DatasetSelectModal = ({
isOpen, isOpen,
@ -132,9 +123,9 @@ export const DatasetSelectModal = ({
<MyTooltip <MyTooltip
key={item._id} key={item._id}
label={ label={
item.type === DatasetTypeEnum.dataset item.type === DatasetTypeEnum.folder
? t('dataset.Select Dataset') ? t('dataset.Select Folder')
: t('dataset.Select Folder') : t('dataset.Select Dataset')
} }
> >
<Card <Card
@ -149,7 +140,7 @@ export const DatasetSelectModal = ({
onClick={() => { onClick={() => {
if (item.type === DatasetTypeEnum.folder) { if (item.type === DatasetTypeEnum.folder) {
setParentId(item._id); setParentId(item._id);
} else if (item.type === DatasetTypeEnum.dataset) { } else {
const vectorModel = selectedDatasets[0]?.vectorModel?.model; const vectorModel = selectedDatasets[0]?.vectorModel?.model;
if (vectorModel && vectorModel !== item.vectorModel.model) { if (vectorModel && vectorModel !== item.vectorModel.model) {

View File

@ -51,8 +51,8 @@ function EditModal({
try { try {
const src = await compressImgFileAndUpload({ const src = await compressImgFileAndUpload({
file, file,
maxW: 100, maxW: 300,
maxH: 100 maxH: 300
}); });
setValue('avatar', src); setValue('avatar', src);
setRefresh((state) => !state); setRefresh((state) => !state);

View File

@ -14,7 +14,8 @@ export const defaultDatasetDetail: DatasetItemType = {
type: 'dataset', type: 'dataset',
avatar: '/icon/logo.svg', avatar: '/icon/logo.svg',
name: '', name: '',
tags: [], intro: '',
status: 'active',
permission: 'private', permission: 'private',
isOwner: false, isOwner: false,
canWrite: false, canWrite: false,
@ -36,7 +37,8 @@ export const defaultCollectionDetail: DatasetCollectionItemType = {
type: 'dataset', type: 'dataset',
avatar: '/icon/logo.svg', avatar: '/icon/logo.svg',
name: '', name: '',
tags: [], intro: '',
status: 'active',
permission: 'private', permission: 'private',
vectorModel: defaultVectorModels[0].model, vectorModel: defaultVectorModels[0].model,
agentModel: defaultQAModels[0].model agentModel: defaultQAModels[0].model

View File

@ -12,15 +12,6 @@ import { PermissionTypeEnum } from '@fastgpt/global/support/permission/constant'
import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d'; import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
/* ===== dataset ===== */ /* ===== dataset ===== */
export type DatasetUpdateParams = {
id: string;
parentId?: string;
tags?: string[];
name?: string;
avatar?: string;
permission?: `${PermissionTypeEnum}`;
agentModel?: LLMModelItemType;
};
/* ======= collections =========== */ /* ======= collections =========== */
export type GetDatasetCollectionsProps = RequestPaging & { export type GetDatasetCollectionsProps = RequestPaging & {
@ -30,16 +21,7 @@ export type GetDatasetCollectionsProps = RequestPaging & {
simple?: boolean; simple?: boolean;
selectFolder?: boolean; selectFolder?: boolean;
}; };
export type CreateDatasetCollectionParams = {
datasetId: string;
parentId?: string;
name: string;
type: `${DatasetCollectionTypeEnum}`;
trainingType?: `${DatasetCollectionTrainingModeEnum}`;
chunkSize?: number;
fileId?: string;
rawLink?: string;
};
export type UpdateDatasetCollectionParams = { export type UpdateDatasetCollectionParams = {
id: string; id: string;
parentId?: string; parentId?: string;

View File

@ -1,5 +1,9 @@
import { PushDatasetDataChunkProps } from '@fastgpt/global/core/dataset/api'; import { PushDatasetDataChunkProps } from '@fastgpt/global/core/dataset/api';
import { DatasetSearchModeEnum, TrainingModeEnum } from '@fastgpt/global/core/dataset/constant'; import {
DatasetSearchModeEnum,
DatasetTypeEnum,
TrainingModeEnum
} from '@fastgpt/global/core/dataset/constant';
import { import {
DatasetDataIndexItemType, DatasetDataIndexItemType,
SearchDataResponseItemType SearchDataResponseItemType
@ -8,8 +12,9 @@ import {
/* ================= dataset ===================== */ /* ================= dataset ===================== */
export type CreateDatasetParams = { export type CreateDatasetParams = {
parentId?: string; parentId?: string;
type: `${DatasetTypeEnum}`;
name: string; name: string;
tags: string; intro: string;
avatar: string; avatar: string;
vectorModel?: string; vectorModel?: string;
agentModel?: string; agentModel?: string;

View File

@ -27,57 +27,56 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [
{ {
title: '标准模板', title: '标准模板',
desc: '', desc: '',
value: `你的背景知识: value: `你的知识:
""" """
{{quote}} {{quote}}
""" """
1. 使 1. 使
2. 使 2.
3. 3.
4. 使markdown内容markdown格式返 4. markdown markdown
:"{{question}}"` :"{{question}}"`
}, },
{ {
title: '问答模板', title: '问答模板',
desc: '', desc: '',
value: `你的背景知识: value: `你的知识:
""" """
{{quote}} {{quote}}
""" """
1. 使 instruction output 1. 使 instruction output
2. 使 2.
3. 3.
4. 使markdown内容markdown格式返 4. markdown markdown
:"{{question}}"` :"{{question}}"`
}, },
{ {
title: '标准严格模板', title: '标准严格模板',
desc: '', desc: '',
value: `你的背景知识: value: `你的知识:
""" """
{{quote}} {{quote}}
""" """
1. 使 1. 使
2. 使 2.
3. xxx 3.
4. 使markdown内容markdown格式返 4. markdown markdown
:"{{question}}"` :"{{question}}"`
}, },
{ {
title: '严格问答模板', title: '严格问答模板',
desc: '', desc: '',
value: `你的背景知识: value: `你的知识:
""" """
{{quote}} {{quote}}
""" """
1. 使 1. instruction answer
2. JSON question answer 2.
3. answer answer 3.
4. answer xxx
:"{{question}}"` :"{{question}}"`
} }
]; ];

View File

@ -35,7 +35,7 @@ const BillDetail = ({ bill, onClose }: { bill: BillItemType; onClose: () => void
<ModalBody> <ModalBody>
<Flex alignItems={'center'} pb={4}> <Flex alignItems={'center'} pb={4}>
<Box flex={'0 0 80px'}>:</Box> <Box flex={'0 0 80px'}>:</Box>
<Box>{bill.memberName}</Box> <Box>{t(bill.memberName)}</Box>
</Flex> </Flex>
<Flex alignItems={'center'} pb={4}> <Flex alignItems={'center'} pb={4}>
<Box flex={'0 0 80px'}>:</Box> <Box flex={'0 0 80px'}>:</Box>

View File

@ -96,8 +96,8 @@ const UserInfo = () => {
try { try {
const src = await compressImgFileAndUpload({ const src = await compressImgFileAndUpload({
file, file,
maxW: 100, maxW: 300,
maxH: 100 maxH: 300
}); });
onclickSave({ onclickSave({

View File

@ -1,7 +1,7 @@
import type { NextApiRequest, NextApiResponse } from 'next'; import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response'; import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo'; import { connectToDatabase } from '@/service/mongo';
import { delay } from '@/utils/tools'; import { delay } from '@fastgpt/global/common/system/utils';
import { PgClient } from '@fastgpt/service/common/pg'; import { PgClient } from '@fastgpt/service/common/pg';
import { import {
DatasetDataIndexTypeEnum, DatasetDataIndexTypeEnum,

View File

@ -1,12 +1,9 @@
import type { NextApiRequest, NextApiResponse } from 'next'; import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response'; import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo'; import { connectToDatabase } from '@/service/mongo';
import { delay } from '@/utils/tools'; import { delay } from '@fastgpt/global/common/system/utils';
import { PgClient } from '@fastgpt/service/common/pg'; import { PgClient } from '@fastgpt/service/common/pg';
import { import { PgDatasetTableName } from '@fastgpt/global/core/dataset/constant';
DatasetDataIndexTypeEnum,
PgDatasetTableName
} from '@fastgpt/global/core/dataset/constant';
import { authCert } from '@fastgpt/service/support/permission/auth/common'; import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema'; import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';

View File

@ -8,7 +8,7 @@ import {
} from '@fastgpt/service/support/user/team/controller'; } from '@fastgpt/service/support/user/team/controller';
import { MongoUser } from '@fastgpt/service/support/user/schema'; import { MongoUser } from '@fastgpt/service/support/user/schema';
import { UserModelSchema } from '@fastgpt/global/support/user/type'; import { UserModelSchema } from '@fastgpt/global/support/user/type';
import { delay } from '@/utils/tools'; import { delay } from '@fastgpt/global/common/system/utils';
import { MongoDataset } from '@fastgpt/service/core/dataset/schema'; import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
import { PermissionTypeEnum } from '@fastgpt/global/support/permission/constant'; import { PermissionTypeEnum } from '@fastgpt/global/support/permission/constant';
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema'; import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';

View File

@ -1,7 +1,7 @@
import type { NextApiRequest, NextApiResponse } from 'next'; import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response'; import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo'; import { connectToDatabase } from '@/service/mongo';
import { delay } from '@/utils/tools'; import { delay } from '@fastgpt/global/common/system/utils';
import { authCert } from '@fastgpt/service/support/permission/auth/common'; import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { MongoApp } from '@fastgpt/service/core/app/schema'; import { MongoApp } from '@fastgpt/service/core/app/schema';
import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant'; import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';

View File

@ -1,7 +1,7 @@
import type { NextApiRequest, NextApiResponse } from 'next'; import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response'; import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo'; import { connectToDatabase } from '@/service/mongo';
import { delay } from '@/utils/tools'; import { delay } from '@fastgpt/global/common/system/utils';
import { authCert } from '@fastgpt/service/support/permission/auth/common'; import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema'; import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
import { jiebaSplit } from '@/service/core/dataset/utils'; import { jiebaSplit } from '@/service/core/dataset/utils';
@ -17,10 +17,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
console.log( console.log(
'total', 'total',
await MongoDatasetData.countDocuments({ fullTextToken: { $exists: false } }) await MongoDatasetData.countDocuments({
fullTextToken: { $exists: false },
updateTime: { $lt: new Date() }
})
); );
await initFullTextToken(limit); await initFullTextToken(limit, new Date());
jsonRes(res, { jsonRes(res, {
message: 'success' message: 'success'
@ -34,9 +37,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
}); });
} }
} }
export async function initFullTextToken(limit = 50): Promise<any> { export async function initFullTextToken(limit = 50, endDate: Date): Promise<any> {
try { try {
const dataList = await MongoDatasetData.find({ fullTextToken: { $exists: false } }, '_id q a') const dataList = await MongoDatasetData.find(
{ fullTextToken: { $exists: false }, updateTime: { $lt: endDate } },
'_id q a'
)
.limit(limit) .limit(limit)
.lean(); .lean();
if (dataList.length === 0) return; if (dataList.length === 0) return;
@ -56,9 +62,9 @@ export async function initFullTextToken(limit = 50): Promise<any> {
success += result.filter((item) => item.status === 'fulfilled').length; success += result.filter((item) => item.status === 'fulfilled').length;
console.log(`success: ${success}`); console.log(`success: ${success}`);
return initFullTextToken(limit); return initFullTextToken(limit, endDate);
} catch (error) { } catch (error) {
await delay(1000); await delay(1000);
return initFullTextToken(limit); return initFullTextToken(limit, endDate);
} }
} }

View File

@ -0,0 +1,62 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import { delay } from '@fastgpt/global/common/system/utils';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
import { jiebaSplit } from '@/service/core/dataset/utils';
let success = 0;
/* pg 中的数据搬到 mongo dataset.datas 中,并做映射 */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
const { limit = 50 } = req.body as { limit: number };
await authCert({ req, authRoot: true });
await connectToDatabase();
success = 0;
console.log('total', await MongoDatasetData.countDocuments({ inited: { $exists: false } }));
await initFullTextToken(limit);
jsonRes(res, {
message: 'success'
});
} catch (error) {
console.log(error);
jsonRes(res, {
code: 500,
error
});
}
}
export async function initFullTextToken(limit = 50): Promise<any> {
try {
const dataList = await MongoDatasetData.find({ inited: { $exists: false } }, '_id q a')
.limit(limit)
.lean();
if (dataList.length === 0) return;
const result = await Promise.allSettled(
dataList.map((item) => {
const text = item.q + (item.a || '');
const tokens = jiebaSplit({ text });
return MongoDatasetData.findByIdAndUpdate(item._id, {
$set: {
inited: true,
fullTextToken: tokens
}
});
})
);
success += result.filter((item) => item.status === 'fulfilled').length;
console.log(`success: ${success}`);
return initFullTextToken(limit);
} catch (error) {
await delay(1000);
return initFullTextToken(limit);
}
}

View File

@ -4,7 +4,8 @@ import { connectToDatabase } from '@/service/mongo';
import { authCert } from '@fastgpt/service/support/permission/auth/common'; import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema'; import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema'; import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
import { TrainingModeEnum } from '@fastgpt/global/core/dataset/constant'; import { DatasetStatusEnum, TrainingModeEnum } from '@fastgpt/global/core/dataset/constant';
import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
let success = 0; let success = 0;
/* pg 中的数据搬到 mongo dataset.datas 中,并做映射 */ /* pg 中的数据搬到 mongo dataset.datas 中,并做映射 */
@ -15,32 +16,85 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
await connectToDatabase(); await connectToDatabase();
success = 0; success = 0;
await MongoDatasetCollection.updateMany({}, [ await MongoDatasetCollection.updateMany({ createTime: { $exists: false } }, [
{
$set: {
createTime: '$updateTime'
}
}
]);
await MongoDatasetCollection.updateMany({ trainingType: { $exists: false } }, [
{ {
$set: { $set: {
createTime: '$updateTime',
trainingType: { trainingType: {
$cond: { $cond: {
if: { $ifNull: ['$a', false] }, if: { $ifNull: ['$a', false] },
then: TrainingModeEnum.qa, then: TrainingModeEnum.qa,
else: TrainingModeEnum.chunk else: TrainingModeEnum.chunk
} }
}, }
chunkSize: 0, }
fileId: '$metadata.fileId', }
]);
await MongoDatasetCollection.updateMany({ chunkSize: { $exists: false } }, [
{
$set: {
chunkSize: 0
}
}
]);
await MongoDatasetCollection.updateMany({ fileId: { $exists: false } }, [
{
$set: {
fileId: '$metadata.fileId'
}
}
]);
await MongoDatasetCollection.updateMany({ rawLink: { $exists: false } }, [
{
$set: {
rawLink: '$metadata.rawLink' rawLink: '$metadata.rawLink'
} }
} }
]); ]);
await MongoDatasetData.updateMany( await MongoDatasetData.updateMany(
{}, { chunkIndex: { $exists: false } },
{
chunkIndex: 0
}
);
await MongoDatasetData.updateMany(
{ updateTime: { $exists: false } },
{ {
chunkIndex: 0,
updateTime: new Date() updateTime: new Date()
} }
); );
await MongoDataset.updateMany(
{ status: { $exists: false } },
{
$set: {
status: DatasetStatusEnum.active
}
}
);
// dataset tags to intro
await MongoDataset.updateMany({ tags: { $exists: true } }, [
{
$set: {
intro: {
$reduce: {
input: '$tags',
initialValue: '',
in: { $concat: ['$$value', ' ', '$$this'] }
}
}
}
}
]);
jsonRes(res, { jsonRes(res, {
message: 'success' message: 'success'
}); });

View File

@ -0,0 +1,92 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { delFileById, getGFSCollection } from '@fastgpt/service/common/file/gridfs/controller';
import { addLog } from '@fastgpt/service/common/mongo/controller';
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
import { delay } from '@fastgpt/global/common/system/utils';
/*
check dataset.files data. If there is no match in dataset.collections, delete it
*/
let deleteFileAmount = 0;
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
const {
startDay = 10,
endDay = 3,
limit = 30
} = req.body as { startDay?: number; endDay?: number; limit?: number };
await authCert({ req, authRoot: true });
await connectToDatabase();
// start: now - maxDay, end: now - 3 day
const start = new Date(Date.now() - startDay * 24 * 60 * 60 * 1000);
const end = new Date(Date.now() - endDay * 24 * 60 * 60 * 1000);
deleteFileAmount = 0;
checkFiles(start, end, limit);
jsonRes(res, {
message: 'success'
});
} catch (error) {
addLog.error(`check valid dataset files error`, error);
jsonRes(res, {
code: 500,
error
});
}
}
export async function checkFiles(start: Date, end: Date, limit: number) {
const collection = getGFSCollection('dataset');
const where = {
uploadDate: { $gte: start, $lte: end }
};
// 1. get all _id
const ids = await collection
.find(where, {
projection: {
_id: 1
}
})
.toArray();
console.log('total files', ids.length);
for (let i = 0; i < limit; i++) {
check(i);
}
async function check(index: number): Promise<any> {
const id = ids[index];
if (!id) {
console.log(`检测完成,共删除 ${deleteFileAmount} 个无效文件`);
return;
}
try {
const { _id } = id;
// 2. find fileId in dataset.collections
const hasCollection = await MongoDatasetCollection.countDocuments({ fileId: _id });
// 3. if not found, delete file
if (hasCollection === 0) {
await delFileById({ bucketName: 'dataset', fileId: String(_id) });
console.log('delete file', _id);
deleteFileAmount++;
}
index % 100 === 0 && console.log(index);
return check(index + limit);
} catch (error) {
console.log(error);
await delay(2000);
return check(index);
}
}
}

View File

@ -2,8 +2,8 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response'; import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo'; import { connectToDatabase } from '@/service/mongo';
import { authFileToken } from '@fastgpt/service/support/permission/controller'; import { authFileToken } from '@fastgpt/service/support/permission/controller';
import jschardet from 'jschardet'; import { detect } from 'jschardet';
import { getDownloadBuf, getFileById } from '@fastgpt/service/common/file/gridfs/controller'; import { getDownloadStream, getFileById } from '@fastgpt/service/common/file/gridfs/controller';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) { export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try { try {
@ -11,24 +11,43 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
const { token } = req.query as { token: string }; const { token } = req.query as { token: string };
const { fileId, teamId, tmbId, bucketName } = await authFileToken(token); const { fileId, bucketName } = await authFileToken(token);
if (!fileId) { if (!fileId) {
throw new Error('fileId is empty'); throw new Error('fileId is empty');
} }
const [file, buffer] = await Promise.all([ const [file, encodeStream] = await Promise.all([
getFileById({ bucketName, fileId }), getFileById({ bucketName, fileId }),
getDownloadBuf({ bucketName, fileId }) getDownloadStream({ bucketName, fileId })
]); ]);
const encoding = jschardet.detect(buffer)?.encoding; // get encoding
let buffers: Buffer = Buffer.from([]);
for await (const chunk of encodeStream) {
buffers = Buffer.concat([buffers, chunk]);
if (buffers.length > 10) {
encodeStream.abort();
break;
}
}
const encoding = detect(buffers)?.encoding || 'utf-8';
res.setHeader('Content-Type', `${file.contentType}; charset=${encoding}`); res.setHeader('Content-Type', `${file.contentType}; charset=${encoding}`);
res.setHeader('Cache-Control', 'public, max-age=3600'); res.setHeader('Cache-Control', 'public, max-age=3600');
res.setHeader('Content-Disposition', `inline; filename="${encodeURIComponent(file.filename)}"`); res.setHeader('Content-Disposition', `inline; filename="${encodeURIComponent(file.filename)}"`);
res.end(buffer); const fileStream = await getDownloadStream({ bucketName, fileId });
fileStream.pipe(res);
fileStream.on('error', () => {
res.status(500).end();
});
fileStream.on('end', () => {
res.end();
});
} catch (error) { } catch (error) {
jsonRes(res, { jsonRes(res, {
code: 500, code: 500,

View File

@ -52,12 +52,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
/* start process */ /* start process */
const { responseData } = await dispatchModules({ const { responseData } = await dispatchModules({
res, res,
appId,
modules,
variables,
teamId, teamId,
tmbId, tmbId,
user, user,
appId,
modules,
variables,
params: { params: {
history, history,
userChatInput: prompt userChatInput: prompt

View File

@ -2,10 +2,11 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response'; import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo'; import { connectToDatabase } from '@/service/mongo';
import { MongoDataset } from '@fastgpt/service/core/dataset/schema'; import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
import { getQAModel, getVectorModel } from '@/service/core/ai/model'; import { getVectorModel } from '@/service/core/ai/model';
import type { DatasetItemType } from '@fastgpt/global/core/dataset/type.d'; import type { DatasetListItemType } from '@fastgpt/global/core/dataset/type.d';
import { mongoRPermission } from '@fastgpt/global/support/permission/utils'; import { mongoRPermission } from '@fastgpt/global/support/permission/utils';
import { authUserRole } from '@fastgpt/service/support/permission/auth/user'; import { authUserRole } from '@fastgpt/service/support/permission/auth/user';
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constant';
/* get all dataset by teamId or tmbId */ /* get all dataset by teamId or tmbId */
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) { export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
@ -16,18 +17,23 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
const datasets = await MongoDataset.find({ const datasets = await MongoDataset.find({
...mongoRPermission({ teamId, tmbId, role }), ...mongoRPermission({ teamId, tmbId, role }),
type: 'dataset' type: { $ne: DatasetTypeEnum.folder }
}).lean(); }).lean();
const data = datasets.map((item) => ({ const data = datasets.map((item) => ({
...item, _id: item._id,
parentId: item.parentId,
avatar: item.avatar,
name: item.name,
intro: item.intro,
type: item.type,
permission: item.permission,
vectorModel: getVectorModel(item.vectorModel), vectorModel: getVectorModel(item.vectorModel),
agentModel: getQAModel(item.agentModel),
canWrite: String(item.tmbId) === tmbId, canWrite: String(item.tmbId) === tmbId,
isOwner: teamOwner || String(item.tmbId) === tmbId isOwner: teamOwner || String(item.tmbId) === tmbId
})); }));
jsonRes<DatasetItemType[]>(res, { jsonRes<DatasetListItemType[]>(res, {
data data
}); });
} catch (err) { } catch (err) {

View File

@ -4,15 +4,10 @@
import type { NextApiRequest, NextApiResponse } from 'next'; import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response'; import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo'; import { connectToDatabase } from '@/service/mongo';
import type { CreateDatasetCollectionParams } from '@/global/core/api/datasetReq.d'; import type { CreateDatasetCollectionParams } from '@fastgpt/global/core/dataset/api.d';
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
import {
TrainingModeEnum,
DatasetCollectionTypeEnum,
DatasetCollectionTrainingModeEnum
} from '@fastgpt/global/core/dataset/constant';
import { authUserNotVisitor } from '@fastgpt/service/support/permission/auth/user'; import { authUserNotVisitor } from '@fastgpt/service/support/permission/auth/user';
import { authDataset } from '@fastgpt/service/support/permission/auth/dataset'; import { authDataset } from '@fastgpt/service/support/permission/auth/dataset';
import { createOneCollection } from '@fastgpt/service/core/dataset/collection/controller';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) { export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try { try {
@ -42,68 +37,3 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
}); });
} }
} }
export async function createOneCollection({
name,
parentId,
datasetId,
type,
trainingType = DatasetCollectionTrainingModeEnum.manual,
chunkSize = 0,
fileId,
rawLink,
teamId,
tmbId
}: CreateDatasetCollectionParams & { teamId: string; tmbId: string }) {
const { _id } = await MongoDatasetCollection.create({
name,
teamId,
tmbId,
datasetId,
parentId: parentId || null,
type,
trainingType,
chunkSize,
fileId,
rawLink
});
// create default collection
if (type === DatasetCollectionTypeEnum.folder) {
await createDefaultCollection({
datasetId,
parentId: _id,
teamId,
tmbId
});
}
return _id;
}
// create default collection
export function createDefaultCollection({
name = '手动录入',
datasetId,
parentId,
teamId,
tmbId
}: {
name?: '手动录入' | '手动标注';
datasetId: string;
parentId?: string;
teamId: string;
tmbId: string;
}) {
return MongoDatasetCollection.create({
name,
teamId,
tmbId,
datasetId,
parentId,
type: DatasetCollectionTypeEnum.virtual,
trainingType: DatasetCollectionTrainingModeEnum.manual,
chunkSize: 0,
updateTime: new Date('2099')
});
}

View File

@ -1,13 +1,10 @@
import type { NextApiRequest, NextApiResponse } from 'next'; import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response'; import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo'; import { connectToDatabase } from '@/service/mongo';
import { MongoDatasetTraining } from '@fastgpt/service/core/dataset/training/schema';
import { findCollectionAndChild } from '@fastgpt/service/core/dataset/collection/utils'; import { findCollectionAndChild } from '@fastgpt/service/core/dataset/collection/utils';
import { delDataByCollectionId } from '@/service/core/dataset/data/controller'; import { delCollectionRelevantData } from '@fastgpt/service/core/dataset/data/controller';
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
import { authDatasetCollection } from '@fastgpt/service/support/permission/auth/dataset'; import { authDatasetCollection } from '@fastgpt/service/support/permission/auth/dataset';
import { delFileById } from '@fastgpt/service/common/file/gridfs/controller'; import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
import { BucketNameEnum } from '@fastgpt/global/common/file/constants';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) { export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try { try {
@ -19,7 +16,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
throw new Error('CollectionIdId is required'); throw new Error('CollectionIdId is required');
} }
const { teamId } = await authDatasetCollection({ await authDatasetCollection({
req, req,
authToken: true, authToken: true,
collectionId, collectionId,
@ -30,26 +27,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
const collections = await findCollectionAndChild(collectionId, '_id metadata'); const collections = await findCollectionAndChild(collectionId, '_id metadata');
const delIdList = collections.map((item) => item._id); const delIdList = collections.map((item) => item._id);
// delete training data // delete
await MongoDatasetTraining.deleteMany({ await delCollectionRelevantData({
collectionId: { $in: delIdList }, collectionIds: delIdList,
teamId fileIds: collections.map((item) => String(item.metadata?.fileId)).filter(Boolean)
}); });
// delete pg data
await delDataByCollectionId({ collectionIds: delIdList });
// delete file
await Promise.all(
collections.map((collection) => {
if (!collection?.fileId) return;
return delFileById({
bucketName: BucketNameEnum.dataset,
fileId: collection.fileId
});
})
);
// delete collection // delete collection
await MongoDatasetCollection.deleteMany({ await MongoDatasetCollection.deleteMany({
_id: { $in: delIdList } _id: { $in: delIdList }

View File

@ -6,7 +6,10 @@ import { Types } from '@fastgpt/service/common/mongo';
import type { DatasetCollectionsListItemType } from '@/global/core/dataset/type.d'; import type { DatasetCollectionsListItemType } from '@/global/core/dataset/type.d';
import type { GetDatasetCollectionsProps } from '@/global/core/api/datasetReq'; import type { GetDatasetCollectionsProps } from '@/global/core/api/datasetReq';
import { PagingData } from '@/types'; import { PagingData } from '@/types';
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema'; import {
DatasetColCollectionName,
MongoDatasetCollection
} from '@fastgpt/service/core/dataset/collection/schema';
import { DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constant'; import { DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constant';
import { startQueue } from '@/service/utils/tools'; import { startQueue } from '@/service/utils/tools';
import { authDataset } from '@fastgpt/service/support/permission/auth/dataset'; import { authDataset } from '@fastgpt/service/support/permission/auth/dataset';
@ -45,7 +48,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
// not count data amount // not count data amount
if (simple) { if (simple) {
const collections = await MongoDatasetCollection.find(match, '_id name type parentId') const collections = await MongoDatasetCollection.find(match, '_id parentId type name')
.sort({ .sort({
updateTime: -1 updateTime: -1
}) })
@ -72,6 +75,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
{ {
$match: match $match: match
}, },
// count training data
{ {
$lookup: { $lookup: {
from: DatasetTrainingCollectionName, from: DatasetTrainingCollectionName,
@ -89,6 +93,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
as: 'trainings' as: 'trainings'
} }
}, },
// count collection total data
{ {
$lookup: { $lookup: {
from: DatasetDataCollectionName, from: DatasetDataCollectionName,
@ -106,7 +111,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
as: 'datas' as: 'datas'
} }
}, },
// 统计子集合的数量和子训练的数量
{ {
$project: { $project: {
_id: 1, _id: 1,
@ -114,6 +118,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
tmbId: 1, tmbId: 1,
name: 1, name: 1,
type: 1, type: 1,
status: 1,
updateTime: 1, updateTime: 1,
dataAmount: { $size: '$datas' }, dataAmount: { $size: '$datas' },
trainingAmount: { $size: '$trainings' }, trainingAmount: { $size: '$trainings' },

View File

@ -3,20 +3,20 @@ import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo'; import { connectToDatabase } from '@/service/mongo';
import { MongoDataset } from '@fastgpt/service/core/dataset/schema'; import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
import type { CreateDatasetParams } from '@/global/core/dataset/api.d'; import type { CreateDatasetParams } from '@/global/core/dataset/api.d';
import { createDefaultCollection } from './collection/create'; import { createDefaultCollection } from '@fastgpt/service/core/dataset/collection/controller';
import { authUserNotVisitor } from '@fastgpt/service/support/permission/auth/user'; import { authUserNotVisitor } from '@fastgpt/service/support/permission/auth/user';
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constant';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) { export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try { try {
await connectToDatabase(); await connectToDatabase();
const { const {
parentId,
name, name,
tags, type,
avatar, avatar,
vectorModel = global.vectorModels[0].model, vectorModel = global.vectorModels[0].model,
agentModel, agentModel
parentId,
type
} = req.body as CreateDatasetParams; } = req.body as CreateDatasetParams;
// 凭证校验 // 凭证校验
@ -26,7 +26,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
name, name,
teamId, teamId,
tmbId, tmbId,
tags,
vectorModel, vectorModel,
agentModel, agentModel,
avatar, avatar,
@ -34,11 +33,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
type type
}); });
await createDefaultCollection({ if (type === DatasetTypeEnum.dataset) {
datasetId: _id, await createDefaultCollection({
teamId, datasetId: _id,
tmbId teamId,
}); tmbId
});
}
jsonRes(res, { data: _id }); jsonRes(res, { data: _id });
} catch (err) { } catch (err) {

View File

@ -3,7 +3,7 @@ import { jsonRes } from '@fastgpt/service/common/response';
import { withNextCors } from '@fastgpt/service/common/middle/cors'; import { withNextCors } from '@fastgpt/service/common/middle/cors';
import { connectToDatabase } from '@/service/mongo'; import { connectToDatabase } from '@/service/mongo';
import { authDatasetData } from '@/service/support/permission/auth/dataset'; import { authDatasetData } from '@/service/support/permission/auth/dataset';
import { deleteDataByDataId } from '@/service/core/dataset/data/controller'; import { delDatasetDataByDataId } from '@fastgpt/service/core/dataset/data/controller';
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) { export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try { try {
@ -19,7 +19,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
// 凭证校验 // 凭证校验
await authDatasetData({ req, authToken: true, dataId, per: 'w' }); await authDatasetData({ req, authToken: true, dataId, per: 'w' });
await deleteDataByDataId(dataId); await delDatasetDataByDataId(dataId);
jsonRes(res, { jsonRes(res, {
data: 'success' data: 'success'

View File

@ -1,13 +1,10 @@
import type { NextApiRequest, NextApiResponse } from 'next'; import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response'; import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo'; import { connectToDatabase } from '@/service/mongo';
import { MongoDatasetTraining } from '@fastgpt/service/core/dataset/training/schema';
import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
import { delDatasetFiles } from '@fastgpt/service/core/dataset/file/controller';
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
import { authDataset } from '@fastgpt/service/support/permission/auth/dataset'; import { authDataset } from '@fastgpt/service/support/permission/auth/dataset';
import { delDataByDatasetId } from '@/service/core/dataset/data/controller'; import { delDatasetRelevantData } from '@fastgpt/service/core/dataset/data/controller';
import { findDatasetIdTreeByTopDatasetId } from '@fastgpt/service/core/dataset/controller'; import { findDatasetIdTreeByTopDatasetId } from '@fastgpt/service/core/dataset/controller';
import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) { export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try { try {
@ -25,21 +22,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
const deletedIds = await findDatasetIdTreeByTopDatasetId(id); const deletedIds = await findDatasetIdTreeByTopDatasetId(id);
// delete training data(There could be a training mission)
await MongoDatasetTraining.deleteMany({
datasetId: { $in: deletedIds }
});
// delete all dataset.data and pg data // delete all dataset.data and pg data
await delDataByDatasetId({ datasetIds: deletedIds }); await delDatasetRelevantData({ datasetIds: deletedIds });
// delete related files
await delDatasetFiles({ datasetId: id });
// delete collections
await MongoDatasetCollection.deleteMany({
datasetId: { $in: deletedIds }
});
// delete dataset data // delete dataset data
await MongoDataset.deleteMany({ await MongoDataset.deleteMany({

View File

@ -1,12 +1,12 @@
import type { NextApiRequest, NextApiResponse } from 'next'; import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response'; import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo'; import { connectToDatabase } from '@/service/mongo';
import { getQAModel, getVectorModel } from '@/service/core/ai/model'; import type { DatasetListItemType } from '@fastgpt/global/core/dataset/type.d';
import type { DatasetItemType } from '@fastgpt/global/core/dataset/type.d';
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constant'; import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constant';
import { MongoDataset } from '@fastgpt/service/core/dataset/schema'; import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
import { mongoRPermission } from '@fastgpt/global/support/permission/utils'; import { mongoRPermission } from '@fastgpt/global/support/permission/utils';
import { authUserRole } from '@fastgpt/service/support/permission/auth/user'; import { authUserRole } from '@fastgpt/service/support/permission/auth/user';
import { getVectorModel } from '@/service/core/ai/model';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) { export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try { try {
@ -27,16 +27,21 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
.lean(); .lean();
const data = await Promise.all( const data = await Promise.all(
datasets.map(async (item) => ({ datasets.map<DatasetListItemType>((item) => ({
...item, _id: item._id,
vectorModel: getVectorModel(item.vectorModel), parentId: item.parentId,
agentModel: getQAModel(item.agentModel), avatar: item.avatar,
name: item.name,
intro: item.intro,
type: item.type,
permission: item.permission,
canWrite, canWrite,
isOwner: teamOwner || String(item.tmbId) === tmbId isOwner: teamOwner || String(item.tmbId) === tmbId,
vectorModel: getVectorModel(item.vectorModel)
})) }))
); );
jsonRes<DatasetItemType[]>(res, { jsonRes<DatasetListItemType[]>(res, {
data data
}); });
} catch (err) { } catch (err) {

View File

@ -44,7 +44,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
}); });
// push bill // push bill
pushGenerateVectorBill({ const { total } = pushGenerateVectorBill({
teamId, teamId,
tmbId, tmbId,
tokenLen: tokenLen, tokenLen: tokenLen,
@ -54,11 +54,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
if (apikey) { if (apikey) {
updateApiKeyUsage({ updateApiKeyUsage({
apikey, apikey,
usage: countModelPrice({ usage: total
model: dataset.vectorModel,
tokens: tokenLen,
type: ModelTypeEnum.vector
})
}); });
} }

View File

@ -2,14 +2,14 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response'; import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo'; import { connectToDatabase } from '@/service/mongo';
import { MongoDataset } from '@fastgpt/service/core/dataset/schema'; import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
import type { DatasetUpdateParams } from '@/global/core/api/datasetReq.d'; import type { DatasetUpdateBody } from '@fastgpt/global/core/dataset/api.d';
import { authDataset } from '@fastgpt/service/support/permission/auth/dataset'; import { authDataset } from '@fastgpt/service/support/permission/auth/dataset';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) { export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try { try {
await connectToDatabase(); await connectToDatabase();
const { id, parentId, name, avatar, tags, permission, agentModel } = const { id, parentId, name, avatar, tags, permission, agentModel, websiteConfig, status } =
req.body as DatasetUpdateParams; req.body as DatasetUpdateBody;
if (!id) { if (!id) {
throw new Error('缺少参数'); throw new Error('缺少参数');
@ -28,7 +28,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
...(avatar && { avatar }), ...(avatar && { avatar }),
...(tags && { tags }), ...(tags && { tags }),
...(permission && { permission }), ...(permission && { permission }),
...(agentModel && { agentModel: agentModel.model }) ...(agentModel && { agentModel: agentModel.model }),
...(websiteConfig && { websiteConfig }),
...(status && { status })
} }
); );

View File

@ -1,73 +0,0 @@
// pages/api/fetchContent.ts
import { NextApiRequest, NextApiResponse } from 'next';
import axios from 'axios';
import { JSDOM } from 'jsdom';
import { Readability } from '@mozilla/readability';
import { jsonRes } from '@fastgpt/service/common/response';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import type { FetchResultItem } from '@fastgpt/global/common/plugin/types/pluginRes.d';
import { simpleText } from '@fastgpt/global/common/string/tools';
import { connectToDatabase } from '@/service/mongo';
export type UrlFetchResponse = FetchResultItem[];
const fetchContent = async (req: NextApiRequest, res: NextApiResponse) => {
try {
await connectToDatabase();
let { urlList = [] } = req.body as { urlList: string[] };
if (!urlList || urlList.length === 0) {
throw new Error('urlList is empty');
}
await authCert({ req, authToken: true });
urlList = urlList.filter((url) => /^(http|https):\/\/[^ "]+$/.test(url));
const response = (
await Promise.allSettled(
urlList.map(async (url) => {
try {
const fetchRes = await axios.get(url, {
timeout: 30000
});
const dom = new JSDOM(fetchRes.data, {
url,
contentType: 'text/html'
});
const reader = new Readability(dom.window.document);
const article = reader.parse();
const content = article?.textContent || '';
return {
url,
content: simpleText(`${article?.title}\n${content}`)
};
} catch (error) {
return {
url,
content: ''
};
}
})
)
)
.filter((item) => item.status === 'fulfilled')
.map((item: any) => item.value)
.filter((item) => item.content);
jsonRes<UrlFetchResponse>(res, {
data: response
});
} catch (error: any) {
jsonRes(res, {
code: 500,
error: error
});
}
};
export default fetchContent;

View File

@ -24,13 +24,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
source: BillSourceEnum.training, source: BillSourceEnum.training,
list: [ list: [
{ {
moduleName: '索引生成', moduleName: 'wallet.moduleName.index',
model: vectorModelData.name, model: vectorModelData.name,
amount: 0, amount: 0,
tokenLen: 0 tokenLen: 0
}, },
{ {
moduleName: 'QA 拆分', moduleName: 'wallet.moduleName.qa',
model: agentModelData.name, model: agentModelData.name,
amount: 0, amount: 0,
tokenLen: 0 tokenLen: 0

View File

@ -0,0 +1,34 @@
// pages/api/fetchContent.ts
import { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { connectToDatabase } from '@/service/mongo';
import { UrlFetchParams, UrlFetchResponse } from '@fastgpt/global/common/file/api.d';
import { urlsFetch } from '@fastgpt/global/common/file/tools';
const fetchContent = async (req: NextApiRequest, res: NextApiResponse) => {
try {
await connectToDatabase();
let { urlList = [], selector } = req.body as UrlFetchParams;
if (!urlList || urlList.length === 0) {
throw new Error('urlList is empty');
}
await authCert({ req, authToken: true });
jsonRes<UrlFetchResponse>(res, {
data: await urlsFetch({
urlList,
selector
})
});
} catch (error: any) {
jsonRes(res, {
code: 500,
error: error
});
}
};
export default fetchContent;

View File

@ -14,10 +14,10 @@ import { getChatHistory } from './getHistory';
import { saveChat } from '@/service/utils/chat/saveChat'; import { saveChat } from '@/service/utils/chat/saveChat';
import { responseWrite } from '@fastgpt/service/common/response'; import { responseWrite } from '@fastgpt/service/common/response';
import { pushChatBill } from '@/service/support/wallet/bill/push'; import { pushChatBill } from '@/service/support/wallet/bill/push';
import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants';
import { authOutLinkChat } from '@/service/support/permission/auth/outLink'; import { authOutLinkChat } from '@/service/support/permission/auth/outLink';
import { pushResult2Remote, updateOutLinkUsage } from '@fastgpt/service/support/outLink/tools'; import { pushResult2Remote, updateOutLinkUsage } from '@fastgpt/service/support/outLink/tools';
import requestIp from 'request-ip'; import requestIp from 'request-ip';
import { getBillSourceByAuthType } from '@fastgpt/global/support/wallet/bill/tools';
import { selectShareResponse } from '@/utils/service/core/chat'; import { selectShareResponse } from '@/utils/service/core/chat';
import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools'; import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools';
@ -276,11 +276,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
appId: app._id, appId: app._id,
teamId: user.team.teamId, teamId: user.team.teamId,
tmbId: user.team.tmbId, tmbId: user.team.tmbId,
source: (() => { source: getBillSourceByAuthType({ shareId, authType }),
if (authType === 'apikey') return BillSourceEnum.api;
if (shareId) return BillSourceEnum.shareLink;
return BillSourceEnum.fastgpt;
})(),
response: responseData response: responseData
}); });

View File

@ -6,6 +6,8 @@ import { pushGenerateVectorBill } from '@/service/support/wallet/bill/push';
import { connectToDatabase } from '@/service/mongo'; import { connectToDatabase } from '@/service/mongo';
import { authTeamBalance } from '@/service/support/permission/auth/bill'; import { authTeamBalance } from '@/service/support/permission/auth/bill';
import { getVectorsByText, GetVectorProps } from '@/service/core/ai/vector'; import { getVectorsByText, GetVectorProps } from '@/service/core/ai/vector';
import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools';
import { getBillSourceByAuthType } from '@fastgpt/global/support/wallet/bill/tools';
type Props = GetVectorProps & { type Props = GetVectorProps & {
billId?: string; billId?: string;
@ -15,24 +17,21 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
try { try {
let { input, model, billId } = req.body as Props; let { input, model, billId } = req.body as Props;
await connectToDatabase(); await connectToDatabase();
const { teamId, tmbId } = await authCert({ req, authToken: true, authApiKey: true });
if (!Array.isArray(input) || typeof input !== 'string') { if (!Array.isArray(input) && typeof input !== 'string') {
throw new Error('input is nor array or string'); throw new Error('input is nor array or string');
} }
const { teamId, tmbId, apikey, authType } = await authCert({
req,
authToken: true,
authApiKey: true
});
await authTeamBalance(teamId); await authTeamBalance(teamId);
const { tokenLen, vectors } = await getVectorsByText({ input, model }); const { tokenLen, vectors } = await getVectorsByText({ input, model });
pushGenerateVectorBill({
teamId,
tmbId,
tokenLen: tokenLen,
model,
billId
});
jsonRes(res, { jsonRes(res, {
data: { data: {
object: 'list', object: 'list',
@ -48,6 +47,22 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
} }
} }
}); });
const { total } = pushGenerateVectorBill({
teamId,
tmbId,
tokenLen,
model,
billId,
source: getBillSourceByAuthType({ authType })
});
if (apikey) {
updateApiKeyUsage({
apikey,
usage: total
});
}
} catch (err) { } catch (err) {
console.log(err); console.log(err);
jsonRes(res, { jsonRes(res, {

View File

@ -7,12 +7,13 @@ import { connectToDatabase } from '@/service/mongo';
import { authTeamBalance } from '@/service/support/permission/auth/bill'; import { authTeamBalance } from '@/service/support/permission/auth/bill';
import { PostReRankProps, PostReRankResponse } from '@fastgpt/global/core/ai/api'; import { PostReRankProps, PostReRankResponse } from '@fastgpt/global/core/ai/api';
import { reRankRecall } from '@/service/core/ai/rerank'; import { reRankRecall } from '@/service/core/ai/rerank';
import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools';
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) { export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
let { query, inputs } = req.body as PostReRankProps; let { query, inputs } = req.body as PostReRankProps;
try { try {
await connectToDatabase(); await connectToDatabase();
const { teamId, tmbId } = await authCert({ const { teamId, tmbId, apikey } = await authCert({
req, req,
authApiKey: true authApiKey: true
}); });
@ -23,12 +24,19 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
const result = await reRankRecall({ query, inputs }); const result = await reRankRecall({ query, inputs });
pushReRankBill({ const { total } = pushReRankBill({
teamId, teamId,
tmbId, tmbId,
source: 'api' source: 'api'
}); });
if (apikey) {
updateApiKeyUsage({
apikey,
usage: total
});
}
jsonRes<PostReRankResponse>(res, { jsonRes<PostReRankResponse>(res, {
data: result data: result
}); });

View File

@ -103,8 +103,8 @@ const InfoModal = ({
try { try {
const src = await compressImgFileAndUpload({ const src = await compressImgFileAndUpload({
file, file,
maxW: 100, maxW: 300,
maxH: 100 maxH: 300
}); });
setValue('avatar', src); setValue('avatar', src);
setRefresh((state) => !state); setRefresh((state) => !state);

View File

@ -60,8 +60,8 @@ const CreateModal = ({ onClose, onSuccess }: { onClose: () => void; onSuccess: (
try { try {
const src = await compressImgFileAndUpload({ const src = await compressImgFileAndUpload({
file, file,
maxW: 100, maxW: 300,
maxH: 100 maxH: 300
}); });
setValue('avatar', src); setValue('avatar', src);
setRefresh((state) => !state); setRefresh((state) => !state);

View File

@ -11,14 +11,18 @@ import {
Tbody, Tbody,
Image, Image,
MenuButton, MenuButton,
useDisclosure useDisclosure,
Button,
Link,
useTheme
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { import {
getDatasetCollections, getDatasetCollections,
delDatasetCollectionById, delDatasetCollectionById,
putDatasetCollectionById, putDatasetCollectionById,
postDatasetCollection, postDatasetCollection,
getDatasetCollectionPathById getDatasetCollectionPathById,
postWebsiteSync
} from '@/web/core/dataset/api'; } from '@/web/core/dataset/api';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
@ -39,7 +43,10 @@ import EmptyTip from '@/components/EmptyTip';
import { import {
FolderAvatarSrc, FolderAvatarSrc,
DatasetCollectionTypeEnum, DatasetCollectionTypeEnum,
TrainingModeEnum DatasetCollectionTrainingModeEnum,
DatasetTypeEnum,
DatasetTypeMap,
DatasetStatusEnum
} from '@fastgpt/global/core/dataset/constant'; } from '@fastgpt/global/core/dataset/constant';
import { getCollectionIcon } from '@fastgpt/global/core/dataset/utils'; import { getCollectionIcon } from '@fastgpt/global/core/dataset/utils';
import EditFolderModal, { useEditFolder } from '../../component/EditFolderModal'; import EditFolderModal, { useEditFolder } from '../../component/EditFolderModal';
@ -52,13 +59,18 @@ import { useToast } from '@/web/common/hooks/useToast';
import MyTooltip from '@/components/MyTooltip'; import MyTooltip from '@/components/MyTooltip';
import { useUserStore } from '@/web/support/user/useUserStore'; import { useUserStore } from '@/web/support/user/useUserStore';
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant'; import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
import { DatasetSchemaType } from '@fastgpt/global/core/dataset/type';
import { postCreateTrainingBill } from '@/web/support/wallet/bill/api';
const FileImportModal = dynamic(() => import('./Import/ImportModal'), {}); const FileImportModal = dynamic(() => import('./Import/ImportModal'), {});
const WebSiteConfigModal = dynamic(() => import('./Import/WebsiteConfig'), {});
const CollectionCard = () => { const CollectionCard = () => {
const BoxRef = useRef<HTMLDivElement>(null); const BoxRef = useRef<HTMLDivElement>(null);
const lastSearch = useRef(''); const lastSearch = useRef('');
const router = useRouter(); const router = useRouter();
const theme = useTheme();
const { toast } = useToast(); const { toast } = useToast();
const { parentId = '', datasetId } = router.query as { parentId: string; datasetId: string }; const { parentId = '', datasetId } = router.query as { parentId: string; datasetId: string };
const { t } = useTranslation(); const { t } = useTranslation();
@ -66,7 +78,7 @@ const CollectionCard = () => {
const { isPc } = useSystemStore(); const { isPc } = useSystemStore();
const { userInfo } = useUserStore(); const { userInfo } = useUserStore();
const [searchText, setSearchText] = useState(''); const [searchText, setSearchText] = useState('');
const { setLoading } = useSystemStore(); const { datasetDetail, updateDataset, loadDatasetDetail } = useDatasetStore();
const { openConfirm, ConfirmModal } = useConfirm({ const { openConfirm, ConfirmModal } = useConfirm({
content: t('dataset.Confirm to delete the file') content: t('dataset.Confirm to delete the file')
@ -76,11 +88,18 @@ const CollectionCard = () => {
onOpen: onOpenFileImportModal, onOpen: onOpenFileImportModal,
onClose: onCloseFileImportModal onClose: onCloseFileImportModal
} = useDisclosure(); } = useDisclosure();
const {
isOpen: isOpenWebsiteModal,
onOpen: onOpenWebsiteModal,
onClose: onCloseWebsiteModal
} = useDisclosure();
const { onOpenModal: onOpenCreateVirtualFileModal, EditModal: EditCreateVirtualFileModal } = const { onOpenModal: onOpenCreateVirtualFileModal, EditModal: EditCreateVirtualFileModal } =
useEditTitle({ useEditTitle({
title: t('dataset.Create Virtual File'), title: t('dataset.Create Virtual File'),
tip: t('dataset.Virtual File Tip') tip: t('dataset.Virtual File Tip'),
canEmpty: false
}); });
const { onOpenModal: onOpenEditTitleModal, EditModal: EditTitleModal } = useEditTitle({ const { onOpenModal: onOpenEditTitleModal, EditModal: EditTitleModal } = useEditTitle({
title: t('Rename') title: t('Rename')
}); });
@ -128,48 +147,60 @@ const CollectionCard = () => {
() => () =>
collections.map((collection) => { collections.map((collection) => {
const icon = getCollectionIcon(collection.type, collection.name); const icon = getCollectionIcon(collection.type, collection.name);
const status = (() => {
if (collection.trainingAmount > 0) {
return {
statusText: t('dataset.collections.Collection Embedding', {
total: collection.trainingAmount
}),
color: 'myGray.500'
};
}
return {
statusText: t('core.dataset.collection.status.active'),
color: 'green.500'
};
})();
return { return {
...collection, ...collection,
icon, icon,
...(collection.trainingAmount > 0 ...status
? {
statusText: t('dataset.collections.Collection Embedding', {
total: collection.trainingAmount
}),
color: 'myGray.500'
}
: {
statusText: t('dataset.collections.Ready'),
color: 'green.500'
})
}; };
}), }),
[collections, t] [collections, t]
); );
const hasTrainingData = useMemo(
() => !!formatCollections.find((item) => item.trainingAmount > 0),
[formatCollections]
);
const { mutate: onCreateVirtualFile } = useRequest({ const { mutate: onCreateCollection, isLoading: isCreating } = useRequest({
mutationFn: ({ name }: { name: string }) => { mutationFn: async ({
setLoading(true); name,
return postDatasetCollection({ type,
callback,
...props
}: {
name: string;
type: `${DatasetCollectionTypeEnum}`;
callback?: (id: string) => void;
trainingType?: `${DatasetCollectionTrainingModeEnum}`;
rawLink?: string;
chunkSize?: number;
}) => {
const id = await postDatasetCollection({
parentId, parentId,
datasetId, datasetId,
name, name,
type: DatasetCollectionTypeEnum.virtual type,
...props
}); });
callback?.(id);
return id;
}, },
onSuccess() { onSuccess() {
getData(pageNum); getData(pageNum);
}, },
onSettled() {
setLoading(false); successToast: t('common.Create Success'),
}, errorToast: t('common.Create Failed')
successToast: t('dataset.collections.Create Virtual File Success'),
errorToast: t('common.Create Virtual File Failed')
}); });
const { mutate: onUpdateCollectionName } = useRequest({ const { mutate: onUpdateCollectionName } = useRequest({
mutationFn: ({ collectionId, name }: { collectionId: string; name: string }) => { mutationFn: ({ collectionId, name }: { collectionId: string; name: string }) => {
@ -185,9 +216,8 @@ const CollectionCard = () => {
successToast: t('common.Rename Success'), successToast: t('common.Rename Success'),
errorToast: t('common.Rename Failed') errorToast: t('common.Rename Failed')
}); });
const { mutate: onDelCollection } = useRequest({ const { mutate: onDelCollection, isLoading: isDeleting } = useRequest({
mutationFn: (collectionId: string) => { mutationFn: (collectionId: string) => {
setLoading(true);
return delDatasetCollectionById({ return delDatasetCollectionById({
collectionId collectionId
}); });
@ -195,26 +225,54 @@ const CollectionCard = () => {
onSuccess() { onSuccess() {
getData(pageNum); getData(pageNum);
}, },
onSettled() {
setLoading(false);
},
successToast: t('common.Delete Success'), successToast: t('common.Delete Success'),
errorToast: t('common.Delete Failed') errorToast: t('common.Delete Failed')
}); });
const { mutate: onUpdateDatasetWebsiteConfig, isLoading: isUpdating } = useRequest({
mutationFn: async (websiteConfig: DatasetSchemaType['websiteConfig']) => {
onCloseWebsiteModal();
const [_, billId] = await Promise.all([
updateDataset({
id: datasetDetail._id,
websiteConfig,
status: DatasetStatusEnum.syncing
}),
postCreateTrainingBill({
name: 'core.dataset.training.Website Sync',
vectorModel: datasetDetail.vectorModel.model,
agentModel: datasetDetail.agentModel.model
})
]);
return billId;
},
onSuccess(billId: string) {
try {
postWebsiteSync({ datasetId: datasetDetail._id, billId });
} catch (error) {}
},
errorToast: t('common.Update Failed')
});
const { data: paths = [] } = useQuery(['getDatasetCollectionPathById', parentId], () => const { data: paths = [] } = useQuery(['getDatasetCollectionPathById', parentId], () =>
getDatasetCollectionPathById(parentId) getDatasetCollectionPathById(parentId)
); );
const hasTrainingData = useMemo(
() => !!formatCollections.find((item) => item.trainingAmount > 0),
[formatCollections]
);
useQuery( useQuery(
['refreshCollection'], ['refreshCollection'],
() => { () => {
getData(1); getData(1);
if (datasetDetail.status === DatasetStatusEnum.syncing) {
loadDatasetDetail(datasetId, true);
}
return null; return null;
}, },
{ {
refetchInterval: 6000, refetchInterval: 6000,
enabled: hasTrainingData enabled: hasTrainingData || datasetDetail.status === DatasetStatusEnum.syncing
} }
); );
@ -224,17 +282,33 @@ const CollectionCard = () => {
return ( return (
<Flex flexDirection={'column'} ref={BoxRef} py={[1, 3]} h={'100%'}> <Flex flexDirection={'column'} ref={BoxRef} py={[1, 3]} h={'100%'}>
<Flex px={[2, 5]} alignItems={['flex-start', 'center']}> <Flex px={[2, 5]} alignItems={['flex-start', 'center']} h={'35px'}>
<Box flex={1}> <Box flex={1}>
<ParentPath <ParentPath
paths={paths.map((path, i) => ({ paths={paths.map((path, i) => ({
parentId: path.parentId, parentId: path.parentId,
parentName: i === paths.length - 1 ? `${path.parentName}(${total})` : path.parentName parentName: i === paths.length - 1 ? `${path.parentName}` : path.parentName
}))} }))}
FirstPathDom={ FirstPathDom={
<Box fontWeight={'bold'} fontSize={['sm', 'lg']}> <>
{t('common.File')}({total}) <Box fontWeight={'bold'} fontSize={['sm', 'lg']}>
</Box> {t(DatasetTypeMap[datasetDetail?.type]?.collectionLabel)}({total})
</Box>
{datasetDetail?.websiteConfig?.url && (
<Flex fontSize={'sm'}>
{t('core.dataset.website.Base Url')}:
<Link
href={datasetDetail.websiteConfig.url}
target="_blank"
mr={2}
textDecoration={'underline'}
color={'myBlue.700'}
>
{datasetDetail.websiteConfig.url}
</Link>
</Flex>
)}
</>
} }
onClick={(e) => { onClick={(e) => {
router.replace({ router.replace({
@ -279,68 +353,109 @@ const CollectionCard = () => {
/> />
</Flex> </Flex>
)} )}
{userInfo?.team?.role !== TeamMemberRoleEnum.visitor && ( {datasetDetail?.type === DatasetTypeEnum.dataset && (
<MyMenu <>
offset={[-40, 10]} {userInfo?.team?.role !== TeamMemberRoleEnum.visitor && (
width={120} <MyMenu
Button={ offset={[-40, 10]}
<MenuButton width={120}
_hover={{ Button={
color: 'myBlue.600' <MenuButton
}} _hover={{
fontSize={['sm', 'md']} color: 'myBlue.600'
> }}
<Flex fontSize={['sm', 'md']}
alignItems={'center'} >
px={5} <Flex
py={2} alignItems={'center'}
borderRadius={'md'} px={5}
cursor={'pointer'} py={2}
bg={'myBlue.600'} borderRadius={'md'}
overflow={'hidden'} cursor={'pointer'}
color={'white'} bg={'myBlue.600'}
h={['28px', '35px']} overflow={'hidden'}
> color={'white'}
<MyIcon name={'importLight'} mr={2} w={'14px'} /> h={['28px', '35px']}
<Box>{t('dataset.collections.Create And Import')}</Box> >
</Flex> <MyIcon name={'importLight'} mr={2} w={'14px'} />
</MenuButton> <Box>{t('dataset.collections.Create And Import')}</Box>
} </Flex>
menuList={[ </MenuButton>
{
child: (
<Flex>
<Image src={FolderAvatarSrc} alt={''} w={'20px'} mr={2} />
{t('Folder')}
</Flex>
),
onClick: () => setEditFolderData({})
},
{
child: (
<Flex>
<Image src={'/imgs/files/collection.svg'} alt={''} w={'20px'} mr={2} />
{t('dataset.Create Virtual File')}
</Flex>
),
onClick: () => {
onOpenCreateVirtualFileModal({
defaultVal: '',
onSuccess: (name) => onCreateVirtualFile({ name })
});
} }
}, menuList={[
{ {
child: ( child: (
<Flex> <Flex>
<Image src={'/imgs/files/file.svg'} alt={''} w={'20px'} mr={2} /> <Image src={FolderAvatarSrc} alt={''} w={'20px'} mr={2} />
{t('dataset.File Input')} {t('Folder')}
</Flex>
),
onClick: () => setEditFolderData({})
},
{
child: (
<Flex>
<Image src={'/imgs/files/collection.svg'} alt={''} w={'20px'} mr={2} />
{t('dataset.Create Virtual File')}
</Flex>
),
onClick: () => {
onOpenCreateVirtualFileModal({
defaultVal: '',
onSuccess: (name) => {
onCreateCollection({ name, type: DatasetCollectionTypeEnum.virtual });
}
});
}
},
{
child: (
<Flex>
<Image src={'/imgs/files/file.svg'} alt={''} w={'20px'} mr={2} />
{t('dataset.File Input')}
</Flex>
),
onClick: onOpenFileImportModal
}
]}
/>
)}
</>
)}
{datasetDetail?.type === DatasetTypeEnum.websiteDataset && (
<>
{datasetDetail?.websiteConfig?.url ? (
<Flex alignItems={'center'}>
{datasetDetail.status === DatasetStatusEnum.active && (
<Button onClick={onOpenWebsiteModal}>{t('common.Config')}</Button>
)}
{datasetDetail.status === DatasetStatusEnum.syncing && (
<Flex
ml={3}
alignItems={'center'}
px={3}
py={1}
borderRadius="md"
border={theme.borders.base}
>
<Box
animation={'zoomStopIcon 0.5s infinite alternate'}
bg={'myGray.700'}
w="8px"
h="8px"
borderRadius={'50%'}
mt={'1px'}
></Box>
<Box ml={2} color={'myGray.600'}>
{t('core.dataset.status.syncing')}
</Box>
</Flex> </Flex>
), )}
onClick: onOpenFileImportModal </Flex>
} ) : (
]} <Button onClick={onOpenWebsiteModal}>{t('core.dataset.Set Website Config')}</Button>
/> )}
</>
)} )}
</Flex> </Flex>
@ -428,11 +543,7 @@ const CollectionCard = () => {
</MyTooltip> </MyTooltip>
</Flex> </Flex>
</Td> </Td>
<Td fontSize={'md'}> <Td fontSize={'md'}>{collection.dataAmount || '-'}</Td>
{collection.type === DatasetCollectionTypeEnum.folder
? '-'
: collection.dataAmount}
</Td>
<Td>{dayjs(collection.updateTime).format('YYYY/MM/DD HH:mm')}</Td> <Td>{dayjs(collection.updateTime).format('YYYY/MM/DD HH:mm')}</Td>
<Td> <Td>
<Flex <Flex
@ -443,10 +554,10 @@ const CollectionCard = () => {
h: '10px', h: '10px',
mr: 2, mr: 2,
borderRadius: 'lg', borderRadius: 'lg',
bg: collection?.color bg: collection.color
}} }}
> >
{collection?.statusText} {t(collection.statusText)}
</Flex> </Flex>
</Td> </Td>
<Td onClick={(e) => e.stopPropagation()}> <Td onClick={(e) => e.stopPropagation()}>
@ -536,14 +647,31 @@ const CollectionCard = () => {
))} ))}
</Tbody> </Tbody>
</Table> </Table>
<Loading loading={isLoading && collections.length === 0} fixed={false} />
{total > pageSize && ( {total > pageSize && (
<Flex mt={2} justifyContent={'center'}> <Flex mt={2} justifyContent={'center'}>
<Pagination /> <Pagination />
</Flex> </Flex>
)} )}
{total === 0 && <EmptyTip text="数据集空空如也" />} {total === 0 && (
<EmptyTip
text={
datasetDetail.type === DatasetTypeEnum.dataset ? (
t('core.dataset.collection.Empty Tip')
) : (
<Flex>
{t('core.dataset.collection.Website Empty Tip')}
<Box textDecoration={'underline'} cursor={'pointer'} onClick={onOpenWebsiteModal}>
{t('core.dataset.collection.Click top config website')}
</Box>
</Flex>
)
}
/>
)}
</TableContainer> </TableContainer>
<Loading
loading={isCreating || isDeleting || isUpdating || (isLoading && collections.length === 0)}
/>
<ConfirmModal /> <ConfirmModal />
<EditTitleModal /> <EditTitleModal />
@ -559,7 +687,6 @@ const CollectionCard = () => {
onClose={onCloseFileImportModal} onClose={onCloseFileImportModal}
/> />
)} )}
{!!editFolderData && ( {!!editFolderData && (
<EditFolderModal <EditFolderModal
onClose={() => setEditFolderData(undefined)} onClose={() => setEditFolderData(undefined)}
@ -570,15 +697,13 @@ const CollectionCard = () => {
id: editFolderData.id, id: editFolderData.id,
name name
}); });
getData(pageNum);
} else { } else {
await postDatasetCollection({ onCreateCollection({
parentId,
datasetId,
name, name,
type: DatasetCollectionTypeEnum.folder type: DatasetCollectionTypeEnum.folder
}); });
} }
getData(pageNum);
} catch (error) { } catch (error) {
return Promise.reject(error); return Promise.reject(error);
} }
@ -587,7 +712,6 @@ const CollectionCard = () => {
name={editFolderData.name} name={editFolderData.name}
/> />
)} )}
{!!moveCollectionData && ( {!!moveCollectionData && (
<SelectCollections <SelectCollections
datasetId={datasetId} datasetId={datasetId}
@ -608,6 +732,16 @@ const CollectionCard = () => {
}} }}
/> />
)} )}
{isOpenWebsiteModal && (
<WebSiteConfigModal
onClose={onCloseWebsiteModal}
onSuccess={onUpdateDatasetWebsiteConfig}
defaultValue={{
url: datasetDetail?.websiteConfig?.url,
selector: datasetDetail?.websiteConfig?.selector
}}
/>
)}
</Flex> </Flex>
); );
}; };

View File

@ -55,7 +55,10 @@ const DataCard = () => {
const router = useRouter(); const router = useRouter();
const { userInfo } = useUserStore(); const { userInfo } = useUserStore();
const { isPc } = useSystemStore(); const { isPc } = useSystemStore();
const { collectionId = '' } = router.query as { collectionId: string }; const { collectionId = '', datasetId } = router.query as {
collectionId: string;
datasetId: string;
};
const { Loading, setIsLoading } = useLoading({ defaultLoading: true }); const { Loading, setIsLoading } = useLoading({ defaultLoading: true });
const { t } = useTranslation(); const { t } = useTranslation();
const [searchText, setSearchText] = useState(''); const [searchText, setSearchText] = useState('');
@ -99,8 +102,18 @@ const DataCard = () => {
); );
// get file info // get file info
const { data: collection } = useQuery(['getDatasetCollectionById', collectionId], () => const { data: collection } = useQuery(
getDatasetCollectionById(collectionId) ['getDatasetCollectionById', collectionId],
() => getDatasetCollectionById(collectionId),
{
onError: () => {
router.replace({
query: {
datasetId
}
});
}
}
); );
const canWrite = useMemo( const canWrite = useMemo(
@ -290,6 +303,7 @@ const DataCard = () => {
</Flex> </Flex>
<Box <Box
maxH={'135px'} maxH={'135px'}
minH={'90px'}
overflow={'hidden'} overflow={'hidden'}
wordBreak={'break-all'} wordBreak={'break-all'}
pt={1} pt={1}

View File

@ -18,13 +18,13 @@ import { useTranslation } from 'next-i18next';
import { customAlphabet } from 'nanoid'; import { customAlphabet } from 'nanoid';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import MyTooltip from '@/components/MyTooltip'; import MyTooltip from '@/components/MyTooltip';
import type { FetchResultItem } from '@fastgpt/global/common/plugin/types/pluginRes.d';
import { getErrText } from '@fastgpt/global/common/error/utils'; import { getErrText } from '@fastgpt/global/common/error/utils';
import { useDatasetStore } from '@/web/core/dataset/store/dataset'; import { useDatasetStore } from '@/web/core/dataset/store/dataset';
import { getFileIcon } from '@fastgpt/global/common/file/icon'; import { getFileIcon } from '@fastgpt/global/common/file/icon';
import { countPromptTokens } from '@fastgpt/global/common/string/tiktoken'; import { countPromptTokens } from '@fastgpt/global/common/string/tiktoken';
import { DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constant'; import { DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constant';
import type { PushDatasetDataChunkProps } from '@fastgpt/global/core/dataset/api.d'; import type { PushDatasetDataChunkProps } from '@fastgpt/global/core/dataset/api.d';
import { UrlFetchResponse } from '@fastgpt/global/common/file/api.d';
const UrlFetchModal = dynamic(() => import('./UrlFetchModal')); const UrlFetchModal = dynamic(() => import('./UrlFetchModal'));
const CreateFileModal = dynamic(() => import('./CreateFileModal')); const CreateFileModal = dynamic(() => import('./CreateFileModal'));
@ -215,7 +215,7 @@ const FileSelect = ({
); );
// link fetch // link fetch
const onUrlFetch = useCallback( const onUrlFetch = useCallback(
(e: FetchResultItem[]) => { (e: UrlFetchResponse) => {
const result: FileItemType[] = e.map<FileItemType>(({ url, content }) => { const result: FileItemType[] = e.map<FileItemType>(({ url, content }) => {
const splitRes = splitText2Chunks({ const splitRes = splitText2Chunks({
text: content, text: content,

View File

@ -188,6 +188,8 @@ const Provider = ({
const onReSplitChunks = useCallback(async () => { const onReSplitChunks = useCallback(async () => {
try { try {
setPreviewFile(undefined);
setFiles((state) => setFiles((state) =>
state.map((file) => { state.map((file) => {
const splitRes = splitText2Chunks({ const splitRes = splitText2Chunks({
@ -490,6 +492,7 @@ export const SelectorContainer = ({
display={['block', 'none']} display={['block', 'none']}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
setPreviewFile(undefined);
setFiles((state) => state.filter((file) => file.id !== item.id)); setFiles((state) => state.filter((file) => file.id !== item.id));
}} }}
/> />

View File

@ -1,31 +1,39 @@
import React, { useRef } from 'react'; import React from 'react';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import MyModal from '@/components/MyModal'; import MyModal from '@/components/MyModal';
import { Box, Button, ModalBody, ModalFooter, Textarea } from '@chakra-ui/react'; import { Box, Button, Input, ModalBody, ModalFooter, Textarea } from '@chakra-ui/react';
import type { FetchResultItem } from '@fastgpt/global/common/plugin/types/pluginRes.d';
import { useRequest } from '@/web/common/hooks/useRequest'; import { useRequest } from '@/web/common/hooks/useRequest';
import { postFetchUrls } from '@/web/common/plugin/api'; import { postFetchUrls } from '@/web/common/tools/api';
import { useForm } from 'react-hook-form';
import { UrlFetchResponse } from '@fastgpt/global/common/file/api.d';
const UrlFetchModal = ({ const UrlFetchModal = ({
onClose, onClose,
onSuccess onSuccess
}: { }: {
onClose: () => void; onClose: () => void;
onSuccess: (e: FetchResultItem[]) => void; onSuccess: (e: UrlFetchResponse) => void;
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const Dom = useRef<HTMLTextAreaElement>(null); const { register, handleSubmit } = useForm({
defaultValues: {
urls: '',
selector: ''
}
});
const { mutate, isLoading } = useRequest({ const { mutate, isLoading } = useRequest({
mutationFn: async () => { mutationFn: async ({ urls, selector }: { urls: string; selector: string }) => {
const val = Dom.current?.value || ''; const urlList = urls.split('\n').filter((e) => e);
const urls = val.split('\n').filter((e) => e); const res = await postFetchUrls({
const res = await postFetchUrls(urls); urlList,
selector
});
onSuccess(res); onSuccess(res);
onClose(); onClose();
}, },
errorToast: '获取链接失败' errorToast: t('core.dataset.import.Fetch Error')
}); });
return ( return (
@ -34,8 +42,8 @@ const UrlFetchModal = ({
title={ title={
<Box> <Box>
<Box>{t('file.Fetch Url')}</Box> <Box>{t('file.Fetch Url')}</Box>
<Box fontWeight={'normal'} fontSize={'sm'} color={'myGray.500'} mt={1}> <Box fontWeight={'normal'} fontSize={'sm'} color={'myGray.500'}>
{t('core.dataset.import.Fetch url tip')}
</Box> </Box>
</Box> </Box>
} }
@ -45,20 +53,31 @@ const UrlFetchModal = ({
w={'600px'} w={'600px'}
> >
<ModalBody> <ModalBody>
<Textarea <Box>
ref={Dom} <Box fontWeight={'bold'}>{t('core.dataset.import.Fetch Url')}</Box>
rows={12} <Textarea
whiteSpace={'nowrap'} {...register('urls', {
resize={'both'} required: true
placeholder={'最多10个链接每行一个。'} })}
/> rows={11}
whiteSpace={'nowrap'}
resize={'both'}
placeholder={t('core.dataset.import.Fetch url placeholder')}
/>
</Box>
<Box mt={4}>
<Box fontWeight={'bold'}>
{t('core.dataset.website.Selector')}({t('common.choosable')})
</Box>{' '}
<Input {...register('selector')} placeholder="body .content #document" />
</Box>
</ModalBody> </ModalBody>
<ModalFooter> <ModalFooter>
<Button variant={'base'} mr={4} onClick={onClose}> <Button variant={'base'} mr={4} onClick={onClose}>
{t('common.Close')}
</Button> </Button>
<Button isLoading={isLoading} onClick={mutate}> <Button isLoading={isLoading} onClick={handleSubmit((data) => mutate(data))}>
{t('common.Confirm')}
</Button> </Button>
</ModalFooter> </ModalFooter>
</MyModal> </MyModal>

View File

@ -0,0 +1,101 @@
import React from 'react';
import MyModal from '@/components/MyModal';
import { useTranslation } from 'next-i18next';
import { Box, Button, Input, ModalBody, ModalFooter } from '@chakra-ui/react';
import { strIsLink } from '@fastgpt/global/common/string/tools';
import { useToast } from '@/web/common/hooks/useToast';
import { useForm } from 'react-hook-form';
import { useConfirm } from '@/web/common/hooks/useConfirm';
type FormType = {
url?: string | undefined;
selector?: string | undefined;
};
const WebsiteConfigModal = ({
onClose,
onSuccess,
defaultValue = {
url: '',
selector: ''
}
}: {
onClose: () => void;
onSuccess: (data: FormType) => void;
defaultValue?: FormType;
}) => {
const { t } = useTranslation();
const { toast } = useToast();
const { register, handleSubmit } = useForm({
defaultValues: defaultValue
});
const isEdit = !!defaultValue.url;
const confirmTip = isEdit
? t('core.dataset.website.Confirm Update Tips')
: t('core.dataset.website.Confirm Create Tips');
const { ConfirmModal, openConfirm } = useConfirm({
type: 'common'
});
return (
<MyModal
isOpen
iconSrc="core/dataset/websiteDataset"
title={t('core.dataset.website.Config')}
onClose={onClose}
maxW={'500px'}
>
<ModalBody>
<Box fontSize={'sm'} color={'myGray.600'}>
{t('core.dataset.website.Config Description')}
</Box>
<Box mt={2}>
<Box>{t('core.dataset.website.Base Url')}</Box>
<Input
placeholder={t('core.dataset.collection.Website Link')}
{...register('url', {
required: true
})}
/>
</Box>
<Box mt={3}>
<Box>
{t('core.dataset.website.Selector')}({t('common.choosable')})
</Box>
<Input {...register('selector')} placeholder="body .content #document" />
</Box>
</ModalBody>
<ModalFooter>
<Button variant={'base'} onClick={onClose}>
{t('common.Close')}
</Button>
<Button
ml={2}
onClick={handleSubmit((data) => {
if (!data.url) return;
// check is link
if (!strIsLink(data.url)) {
return toast({
status: 'warning',
title: t('common.link.UnValid')
});
}
openConfirm(
() => {
onSuccess(data);
},
undefined,
confirmTip
)();
})}
>
{t('core.dataset.website.Start Sync')}
</Button>
</ModalFooter>
<ConfirmModal />
</MyModal>
);
};
export default WebsiteConfigModal;

View File

@ -7,7 +7,7 @@ import React, {
ForwardedRef ForwardedRef
} from 'react'; } from 'react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { Box, Flex, Button, FormControl, IconButton, Input } from '@chakra-ui/react'; import { Box, Flex, Button, FormControl, IconButton, Input, Textarea } from '@chakra-ui/react';
import { QuestionOutlineIcon, DeleteIcon } from '@chakra-ui/icons'; import { QuestionOutlineIcon, DeleteIcon } from '@chakra-ui/icons';
import { delDatasetById } from '@/web/core/dataset/api'; import { delDatasetById } from '@/web/core/dataset/api';
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile'; import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
@ -18,21 +18,19 @@ import { UseFormReturn } from 'react-hook-form';
import { compressImgFileAndUpload } from '@/web/common/file/controller'; import { compressImgFileAndUpload } from '@/web/common/file/controller';
import type { DatasetItemType } from '@fastgpt/global/core/dataset/type.d'; import type { DatasetItemType } from '@fastgpt/global/core/dataset/type.d';
import Avatar from '@/components/Avatar'; import Avatar from '@/components/Avatar';
import Tag from '@/components/Tag';
import MyTooltip from '@/components/MyTooltip'; import MyTooltip from '@/components/MyTooltip';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import PermissionRadio from '@/components/support/permission/Radio'; import PermissionRadio from '@/components/support/permission/Radio';
import MySelect from '@/components/Select'; import MySelect from '@/components/Select';
import { qaModelList } from '@/web/common/system/staticData'; import { qaModelList } from '@/web/common/system/staticData';
export interface ComponentRef { const Info = ({
initInput: (tags: string) => void; datasetId,
} form
}: {
const Info = ( datasetId: string;
{ datasetId, form }: { datasetId: string; form: UseFormReturn<DatasetItemType, any> }, form: UseFormReturn<DatasetItemType, any>;
ref: ForwardedRef<ComponentRef> }) => {
) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { getValues, formState, setValue, register, handleSubmit } = form; const { getValues, formState, setValue, register, handleSubmit } = form;
const InputRef = useRef<HTMLInputElement>(null); const InputRef = useRef<HTMLInputElement>(null);
@ -52,7 +50,7 @@ const Info = (
multiple: false multiple: false
}); });
const { datasetDetail, loadDatasetDetail, loadDatasets, updateDataset } = useDatasetStore(); const { datasetDetail, loadDatasets, updateDataset } = useDatasetStore();
/* 点击删除 */ /* 点击删除 */
const onclickDelKb = useCallback(async () => { const onclickDelKb = useCallback(async () => {
@ -121,8 +119,8 @@ const Info = (
try { try {
const src = await compressImgFileAndUpload({ const src = await compressImgFileAndUpload({
file, file,
maxW: 100, maxW: 300,
maxH: 100 maxH: 300
}); });
setValue('avatar', src); setValue('avatar', src);
@ -138,14 +136,6 @@ const Info = (
[setRefresh, setValue, toast] [setRefresh, setValue, toast]
); );
useImperativeHandle(ref, () => ({
initInput: (tags: string) => {
if (InputRef.current) {
InputRef.current.value = tags;
}
}
}));
return ( return (
<Box py={5} px={[5, 10]}> <Box py={5} px={[5, 10]}>
<Flex mt={5} w={'100%'} alignItems={'center'}> <Flex mt={5} w={'100%'} alignItems={'center'}>
@ -154,18 +144,7 @@ const Info = (
</Box> </Box>
<Box flex={1}>{datasetDetail._id}</Box> <Box flex={1}>{datasetDetail._id}</Box>
</Flex> </Flex>
<Flex mt={8} w={'100%'} alignItems={'center'}>
<Box flex={['0 0 90px', '0 0 160px']} w={0}>
</Box>
<Box flex={[1, '0 0 300px']}>{getValues('vectorModel').name}</Box>
</Flex>
<Flex mt={8} w={'100%'} alignItems={'center'}>
<Box flex={['0 0 90px', '0 0 160px']} w={0}>
MaxTokens
</Box>
<Box flex={[1, '0 0 300px']}>{getValues('vectorModel').maxToken}</Box>
</Flex>
<Flex mt={5} w={'100%'} alignItems={'center'}> <Flex mt={5} w={'100%'} alignItems={'center'}>
<Box flex={['0 0 90px', '0 0 160px']} w={0}> <Box flex={['0 0 90px', '0 0 160px']} w={0}>
@ -183,7 +162,7 @@ const Info = (
</MyTooltip> </MyTooltip>
</Box> </Box>
</Flex> </Flex>
<FormControl mt={8} w={'100%'} display={'flex'} alignItems={'center'}> <Flex mt={8} w={'100%'} alignItems={'center'}>
<Box flex={['0 0 90px', '0 0 160px']} w={0}> <Box flex={['0 0 90px', '0 0 160px']} w={0}>
</Box> </Box>
@ -194,7 +173,19 @@ const Info = (
required: '知识库名称不能为空' required: '知识库名称不能为空'
})} })}
/> />
</FormControl> </Flex>
<Flex mt={8} w={'100%'} alignItems={'center'}>
<Box flex={['0 0 90px', '0 0 160px']} w={0}>
</Box>
<Box flex={[1, '0 0 300px']}>{getValues('vectorModel').name}</Box>
</Flex>
<Flex mt={8} w={'100%'} alignItems={'center'}>
<Box flex={['0 0 90px', '0 0 160px']} w={0}>
{t('core.Max Token')}
</Box>
<Box flex={[1, '0 0 300px']}>{getValues('vectorModel').maxToken}</Box>
</Flex>
<Flex mt={6} alignItems={'center'}> <Flex mt={6} alignItems={'center'}>
<Box flex={['0 0 90px', '0 0 160px']} w={0}> <Box flex={['0 0 90px', '0 0 160px']} w={0}>
{t('dataset.Agent Model')} {t('dataset.Agent Model')}
@ -216,33 +207,9 @@ const Info = (
/> />
</Box> </Box>
</Flex> </Flex>
<Flex mt={8} alignItems={'center'} w={'100%'} flexWrap={'wrap'}> <Flex mt={8} alignItems={'center'} w={'100%'}>
<Box flex={['0 0 90px', '0 0 160px']} w={0}> <Box flex={['0 0 90px', '0 0 160px']}>{t('common.Intro')}</Box>
<Textarea flex={[1, '0 0 300px']} {...register('intro')} placeholder={t('common.Intro')} />
<MyTooltip label={'用空格隔开多个标签,便于搜索'} forceShow>
<QuestionOutlineIcon ml={1} />
</MyTooltip>
</Box>
<Input
flex={[1, '0 0 300px']}
ref={InputRef}
defaultValue={getValues('tags')}
placeholder={'标签,使用空格分割。'}
maxLength={30}
onChange={(e) => {
setValue('tags', e.target.value.split(' ').filter(Boolean));
setRefresh(!refresh);
}}
/>
<Flex w={'100%'} pl={['90px', '160px']} mt={2}>
{getValues('tags')
.filter(Boolean)
.map((item, i) => (
<Tag mr={2} mb={2} key={i} whiteSpace={'nowrap'}>
{item}
</Tag>
))}
</Flex>
</Flex> </Flex>
{datasetDetail.isOwner && ( {datasetDetail.isOwner && (
<Flex mt={5} alignItems={'center'} w={'100%'} flexWrap={'wrap'}> <Flex mt={5} alignItems={'center'} w={'100%'} flexWrap={'wrap'}>
@ -292,4 +259,4 @@ const Info = (
); );
}; };
export default forwardRef(Info); export default React.memo(Info);

View File

@ -199,12 +199,12 @@ const InputDataModal = ({
<Box p={5} borderRight={theme.borders.base}> <Box p={5} borderRight={theme.borders.base}>
<RawSourceText <RawSourceText
w={'200px'} w={'200px'}
className="" className="textEllipsis3"
whiteSpace={'pre-wrap'} whiteSpace={'pre-wrap'}
sourceName={collection.sourceName} sourceName={collection.sourceName}
sourceId={collection.sourceId} sourceId={collection.sourceId}
mb={6} mb={6}
fontSize={['14px', '16px']} fontSize={'sm'}
/> />
<SideTabs <SideTabs
list={tabList} list={tabList}
@ -220,14 +220,14 @@ const InputDataModal = ({
}} }}
/> />
</Box> </Box>
<Flex flexDirection={'column'} px={5} py={3} flex={1} h={'100%'}> <Flex flexDirection={'column'} py={3} flex={1} h={'100%'}>
<Box fontSize={'lg'} fontWeight={'bold'} mb={4}> <Box fontSize={'lg'} px={5} fontWeight={'bold'} mb={4}>
{currentTab === TabEnum.content && ( {currentTab === TabEnum.content && (
<>{defaultValue.id ? t('dataset.data.Update Data') : t('dataset.data.Input Data')}</> <>{defaultValue.id ? t('dataset.data.Update Data') : t('dataset.data.Input Data')}</>
)} )}
{currentTab === TabEnum.index && <> {t('dataset.data.Index Edit')}</>} {currentTab === TabEnum.index && <> {t('dataset.data.Index Edit')}</>}
</Box> </Box>
<Box flex={1} overflow={'auto'}> <Box flex={1} px={5} overflow={'auto'}>
{currentTab === TabEnum.content && ( {currentTab === TabEnum.content && (
<> <>
<Box> <Box>
@ -358,7 +358,7 @@ const InputDataModal = ({
</Grid> </Grid>
)} )}
</Box> </Box>
<Flex justifyContent={'flex-end'} mt={4}> <Flex justifyContent={'flex-end'} px={5} mt={4}>
<Button variant={'base'} mr={3} isLoading={loading} onClick={onClose}> <Button variant={'base'} mr={3} isLoading={loading} onClick={onClose}>
{t('common.Close')} {t('common.Close')}
</Button> </Button>

View File

@ -187,7 +187,7 @@ const Test = ({ datasetId }: { datasetId: string }) => {
</Box> </Box>
</Box> </Box>
{/* result show */} {/* result show */}
<Box p={4} h={['auto', '100%']} overflow={'overlay'} flex={1}> <Box p={4} h={['auto', '100%']} overflow={'overlay'} flex={'1 0 0'}>
{!datasetTestItem?.results || datasetTestItem.results.length === 0 ? ( {!datasetTestItem?.results || datasetTestItem.results.length === 0 ? (
<Flex <Flex
mt={[10, 0]} mt={[10, 0]}
@ -275,7 +275,7 @@ const Test = ({ datasetId }: { datasetId: string }) => {
<MyIcon name={'kbTest'} w={'14px'} /> <MyIcon name={'kbTest'} w={'14px'} />
<Progress <Progress
mx={2} mx={2}
flex={1} flex={'1 0 0'}
value={item.score * 100} value={item.score * 100}
size="sm" size="sm"
borderRadius={'20px'} borderRadius={'20px'}
@ -283,7 +283,7 @@ const Test = ({ datasetId }: { datasetId: string }) => {
/> />
<Box>{item.score.toFixed(4)}</Box> <Box>{item.score.toFixed(4)}</Box>
</Flex> </Flex>
<Box px={2} fontSize={'xs'} color={'myGray.600'}> <Box px={2} fontSize={'xs'} color={'myGray.600'} wordBreak={'break-word'}>
<Box>{item.q}</Box> <Box>{item.q}</Box>
<Box>{item.a}</Box> <Box>{item.a}</Box>
</Box> </Box>

View File

@ -7,7 +7,6 @@ import { useQuery } from '@tanstack/react-query';
import type { DatasetItemType } from '@fastgpt/global/core/dataset/type.d'; import type { DatasetItemType } from '@fastgpt/global/core/dataset/type.d';
import { getErrText } from '@fastgpt/global/common/error/utils'; import { getErrText } from '@fastgpt/global/common/error/utils';
import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useSystemStore } from '@/web/common/system/useSystemStore';
import { type ComponentRef } from './components/Info';
import Tabs from '@/components/Tabs'; import Tabs from '@/components/Tabs';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import MyIcon from '@/components/Icon'; import MyIcon from '@/components/Icon';
@ -25,6 +24,7 @@ import Script from 'next/script';
import CollectionCard from './components/CollectionCard'; import CollectionCard from './components/CollectionCard';
import { useDatasetStore } from '@/web/core/dataset/store/dataset'; import { useDatasetStore } from '@/web/core/dataset/store/dataset';
import { useUserStore } from '@/web/support/user/useUserStore'; import { useUserStore } from '@/web/support/user/useUserStore';
import { DatasetTypeMap } from '../../../../../../packages/global/core/dataset/constant';
const DataCard = dynamic(() => import('./components/DataCard'), { const DataCard = dynamic(() => import('./components/DataCard'), {
ssr: false ssr: false
@ -41,7 +41,6 @@ export enum TabEnum {
} }
const Detail = ({ datasetId, currentTab }: { datasetId: string; currentTab: `${TabEnum}` }) => { const Detail = ({ datasetId, currentTab }: { datasetId: string; currentTab: `${TabEnum}` }) => {
const InfoRef = useRef<ComponentRef>(null);
const theme = useTheme(); const theme = useTheme();
const { t } = useTranslation(); const { t } = useTranslation();
const { toast } = useToast(); const { toast } = useToast();
@ -77,7 +76,6 @@ const Detail = ({ datasetId, currentTab }: { datasetId: string; currentTab: `${T
useQuery([datasetId], () => loadDatasetDetail(datasetId), { useQuery([datasetId], () => loadDatasetDetail(datasetId), {
onSuccess(res) { onSuccess(res) {
form.reset(res); form.reset(res);
InfoRef.current?.initInput(res.tags?.join(' '));
}, },
onError(err: any) { onError(err: any) {
router.replace(`/dataset/list`); router.replace(`/dataset/list`);
@ -107,14 +105,24 @@ const Detail = ({ datasetId, currentTab }: { datasetId: string; currentTab: `${T
> >
<Flex mb={4} alignItems={'center'}> <Flex mb={4} alignItems={'center'}>
<Avatar src={datasetDetail.avatar} w={'34px'} borderRadius={'lg'} /> <Avatar src={datasetDetail.avatar} w={'34px'} borderRadius={'lg'} />
<Box ml={2} fontWeight={'bold'}> <Box ml={2}>
{datasetDetail.name} <Box fontWeight={'bold'}>{datasetDetail.name}</Box>
</Box> </Box>
</Flex> </Flex>
{DatasetTypeMap[datasetDetail.type] && (
<Flex alignItems={'center'} pl={2}>
<MyIcon
name={DatasetTypeMap[datasetDetail.type]?.icon as any}
mr={1}
w={'16px'}
/>
<Box>{t(DatasetTypeMap[datasetDetail.type]?.label)}</Box>
</Flex>
)}
<SideTabs <SideTabs
flex={1} flex={1}
mx={'auto'} mx={'auto'}
mt={2} mt={3}
w={'100%'} w={'100%'}
list={tabList} list={tabList}
activeId={currentTab} activeId={currentTab}
@ -156,7 +164,7 @@ const Detail = ({ datasetId, currentTab }: { datasetId: string; currentTab: `${T
borderRadius={'50%'} borderRadius={'50%'}
aria-label={''} aria-label={''}
/> />
{t('core.dataset.All Dataset')}
</Flex> </Flex>
</Flex> </Flex>
) : ( ) : (
@ -180,9 +188,7 @@ const Detail = ({ datasetId, currentTab }: { datasetId: string; currentTab: `${T
{currentTab === TabEnum.collectionCard && <CollectionCard />} {currentTab === TabEnum.collectionCard && <CollectionCard />}
{currentTab === TabEnum.dataCard && <DataCard />} {currentTab === TabEnum.dataCard && <DataCard />}
{currentTab === TabEnum.test && <Test datasetId={datasetId} />} {currentTab === TabEnum.test && <Test datasetId={datasetId} />}
{currentTab === TabEnum.info && ( {currentTab === TabEnum.info && <Info datasetId={datasetId} form={form} />}
<Info ref={InfoRef} datasetId={datasetId} form={form} />
)}
</Box> </Box>
)} )}
</Flex> </Flex>

View File

@ -1,14 +1,5 @@
import React, { useCallback, useState, useRef } from 'react'; import React, { useCallback, useState } from 'react';
import { import { Box, Flex, Button, ModalFooter, ModalBody, Input } from '@chakra-ui/react';
Box,
Flex,
Button,
ModalHeader,
ModalFooter,
ModalBody,
Input,
Image
} from '@chakra-ui/react';
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile'; import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { compressImgFileAndUpload } from '@/web/common/file/controller'; import { compressImgFileAndUpload } from '@/web/common/file/controller';
@ -23,10 +14,11 @@ import MyModal from '@/components/MyModal';
import { postCreateDataset } from '@/web/core/dataset/api'; import { postCreateDataset } from '@/web/core/dataset/api';
import type { CreateDatasetParams } from '@/global/core/dataset/api.d'; import type { CreateDatasetParams } from '@/global/core/dataset/api.d';
import MySelect from '@/components/Select'; import MySelect from '@/components/Select';
import { QuestionOutlineIcon } from '@chakra-ui/icons';
import { vectorModelList, qaModelList } from '@/web/common/system/staticData'; import { vectorModelList, qaModelList } from '@/web/common/system/staticData';
import Tag from '@/components/Tag';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import MyRadio from '@/components/common/MyRadio';
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constant';
import { feConfigs } from '@/web/common/system/staticData';
const CreateModal = ({ onClose, parentId }: { onClose: () => void; parentId?: string }) => { const CreateModal = ({ onClose, parentId }: { onClose: () => void; parentId?: string }) => {
const { t } = useTranslation(); const { t } = useTranslation();
@ -36,16 +28,15 @@ const CreateModal = ({ onClose, parentId }: { onClose: () => void; parentId?: st
const { isPc } = useSystemStore(); const { isPc } = useSystemStore();
const { register, setValue, getValues, handleSubmit } = useForm<CreateDatasetParams>({ const { register, setValue, getValues, handleSubmit } = useForm<CreateDatasetParams>({
defaultValues: { defaultValues: {
parentId,
type: DatasetTypeEnum.dataset,
avatar: '/icon/logo.svg', avatar: '/icon/logo.svg',
name: '', name: '',
tags: '', intro: '',
vectorModel: vectorModelList[0].model, vectorModel: vectorModelList[0].model,
agentModel: qaModelList[0].model, agentModel: qaModelList[0].model
type: 'dataset',
parentId
} }
}); });
const InputRef = useRef<HTMLInputElement>(null);
const { File, onOpen: onOpenSelectFile } = useSelectFile({ const { File, onOpen: onOpenSelectFile } = useSelectFile({
fileType: '.jpg,.png', fileType: '.jpg,.png',
@ -59,8 +50,8 @@ const CreateModal = ({ onClose, parentId }: { onClose: () => void; parentId?: st
try { try {
const src = await compressImgFileAndUpload({ const src = await compressImgFileAndUpload({
file, file,
maxW: 100, maxW: 300,
maxH: 100 maxH: 300
}); });
setValue('avatar', src); setValue('avatar', src);
setRefresh((state) => !state); setRefresh((state) => !state);
@ -97,32 +88,67 @@ const CreateModal = ({ onClose, parentId }: { onClose: () => void; parentId?: st
w={'450px'} w={'450px'}
> >
<ModalBody> <ModalBody>
<Box color={'myGray.800'} fontWeight={'bold'}> <>
<Box mb={1} color={'myGray.800'} fontWeight={'bold'}>
</Box> {t('core.dataset.Dataset Type')}
<Flex mt={3} alignItems={'center'}> </Box>
<MyTooltip label={'点击设置头像'}> <MyRadio
<Avatar gridGap={2}
flexShrink={0} gridTemplateColumns={'repeat(1,1fr)'}
src={getValues('avatar')} list={[
w={['28px', '32px']} {
h={['28px', '32px']} title: t('core.dataset.Common Dataset'),
cursor={'pointer'} value: DatasetTypeEnum.dataset,
borderRadius={'md'} icon: 'core/dataset/commonDataset',
onClick={onOpenSelectFile} desc: t('core.dataset.Common Dataset Desc')
/> },
</MyTooltip> ...(feConfigs.isPlus
<Input ? [
ml={3} {
flex={1} title: t('core.dataset.Website Dataset'),
autoFocus value: DatasetTypeEnum.websiteDataset,
bg={'myWhite.600'} icon: 'core/dataset/websiteDataset',
maxLength={30} desc: t('core.dataset.Website Dataset Desc')
{...register('name', { }
required: '知识库名称不能为空~' ]
})} : [])
]}
value={getValues('type')}
onChange={(e) => {
setValue('type', e as `${DatasetTypeEnum}`);
setRefresh(!refresh);
}}
/> />
</Flex> </>
<Box mt={5}>
<Box color={'myGray.800'} fontWeight={'bold'}>
</Box>
<Flex mt={1} alignItems={'center'}>
<MyTooltip label={'点击设置头像'}>
<Avatar
flexShrink={0}
src={getValues('avatar')}
w={['28px', '32px']}
h={['28px', '32px']}
cursor={'pointer'}
borderRadius={'md'}
onClick={onOpenSelectFile}
/>
</MyTooltip>
<Input
ml={3}
flex={1}
autoFocus
bg={'myWhite.600'}
placeholder={t('common.Name')}
maxLength={30}
{...register('name', {
required: '知识库名称不能为空~'
})}
/>
</Flex>
</Box>
<Flex mt={6} alignItems={'center'}> <Flex mt={6} alignItems={'center'}>
<Box flex={'0 0 100px'}></Box> <Box flex={'0 0 100px'}></Box>
<Box flex={1}> <Box flex={1}>
@ -157,42 +183,14 @@ const CreateModal = ({ onClose, parentId }: { onClose: () => void; parentId?: st
/> />
</Box> </Box>
</Flex> </Flex>
<Flex mt={6} alignItems={'center'} w={'100%'}>
<Box flex={'0 0 100px'}>
<MyTooltip label={'用空格隔开多个标签,便于搜索'} forceShow>
<QuestionOutlineIcon ml={1} />
</MyTooltip>
</Box>
<Input
flex={1}
ref={InputRef}
placeholder={'标签,使用空格分割。'}
maxLength={30}
onChange={(e) => {
setValue('tags', e.target.value);
setRefresh(!refresh);
}}
/>
</Flex>
<Flex mt={2} flexWrap={'wrap'}>
{getValues('tags')
.split(' ')
.filter(Boolean)
.map((item, i) => (
<Tag mr={2} mb={2} key={i} whiteSpace={'nowrap'}>
{item}
</Tag>
))}
</Flex>
</ModalBody> </ModalBody>
<ModalFooter> <ModalFooter>
<Button variant={'base'} mr={3} onClick={onClose}> <Button variant={'base'} mr={3} onClick={onClose}>
{t('common.Close')}
</Button> </Button>
<Button isLoading={creating} onClick={handleSubmit((data) => onclickCreate(data))}> <Button isLoading={creating} onClick={handleSubmit((data) => onclickCreate(data))}>
{t('common.Confirm Create')}
</Button> </Button>
</ModalFooter> </ModalFooter>

View File

@ -7,8 +7,7 @@ import {
useDisclosure, useDisclosure,
Card, Card,
MenuButton, MenuButton,
Image, Image
Link
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { useDatasetStore } from '@/web/core/dataset/store/dataset'; import { useDatasetStore } from '@/web/core/dataset/store/dataset';
@ -28,8 +27,11 @@ import Avatar from '@/components/Avatar';
import MyIcon from '@/components/Icon'; import MyIcon from '@/components/Icon';
import { serviceSideProps } from '@/web/common/utils/i18n'; import { serviceSideProps } from '@/web/common/utils/i18n';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import { FolderAvatarSrc, DatasetTypeEnum } from '@fastgpt/global/core/dataset/constant'; import {
import Tag from '@/components/Tag'; FolderAvatarSrc,
DatasetTypeEnum,
DatasetTypeMap
} from '@fastgpt/global/core/dataset/constant';
import MyMenu from '@/components/MyMenu'; import MyMenu from '@/components/MyMenu';
import { useRequest } from '@/web/common/hooks/useRequest'; import { useRequest } from '@/web/common/hooks/useRequest';
import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useSystemStore } from '@/web/common/system/useSystemStore';
@ -55,12 +57,12 @@ const Kb = () => {
const DeleteTipsMap = useRef({ const DeleteTipsMap = useRef({
[DatasetTypeEnum.folder]: t('dataset.deleteFolderTips'), [DatasetTypeEnum.folder]: t('dataset.deleteFolderTips'),
[DatasetTypeEnum.dataset]: t('dataset.deleteDatasetTips') [DatasetTypeEnum.dataset]: t('dataset.deleteDatasetTips'),
[DatasetTypeEnum.websiteDataset]: t('core.dataset.Delete Website Tips')
}); });
const { openConfirm, ConfirmModal } = useConfirm({ const { openConfirm, ConfirmModal } = useConfirm({
title: t('common.Delete Warning'), type: 'delete'
content: ''
}); });
const { myDatasets, loadDatasets, setDatasets, updateDataset } = useDatasetStore(); const { myDatasets, loadDatasets, setDatasets, updateDataset } = useDatasetStore();
const { onOpenModal: onOpenTitleModal, EditModal: EditTitleModal } = useEditTitle({ const { onOpenModal: onOpenTitleModal, EditModal: EditTitleModal } = useEditTitle({
@ -110,14 +112,26 @@ const Kb = () => {
errorToast: t('dataset.Export Dataset Limit Error') errorToast: t('dataset.Export Dataset Limit Error')
}); });
const { data, refetch } = useQuery(['loadDataset', parentId], () => { const { data, refetch, isFetching } = useQuery(['loadDataset', parentId], () => {
return Promise.all([loadDatasets(parentId), getDatasetPaths(parentId)]); return Promise.all([loadDatasets(parentId), getDatasetPaths(parentId)]);
}); });
const paths = data?.[1] || []; const paths = data?.[1] || [];
const formatDatasets = useMemo(
() =>
myDatasets.map((item) => {
return {
...item,
label: DatasetTypeMap[item.type]?.label,
icon: DatasetTypeMap[item.type]?.icon
};
}),
[myDatasets]
);
return ( return (
<PageContainer> <PageContainer isLoading={isFetching}>
<Flex pt={3} px={5} alignItems={'center'}> <Flex pt={3} px={5} alignItems={'center'}>
{/* url path */} {/* url path */}
<ParentPaths <ParentPaths
@ -179,7 +193,7 @@ const Kb = () => {
child: ( child: (
<Flex> <Flex>
<Image src={'/imgs/module/db.png'} alt={''} w={'20px'} mr={1} /> <Image src={'/imgs/module/db.png'} alt={''} w={'20px'} mr={1} />
{t('Dataset')} {t('core.dataset.Dataset')}
</Flex> </Flex>
), ),
onClick: onOpenCreateModal onClick: onOpenCreateModal
@ -194,15 +208,15 @@ const Kb = () => {
gridGap={5} gridGap={5}
userSelect={'none'} userSelect={'none'}
> >
{myDatasets.map((dataset) => ( {formatDatasets.map((dataset) => (
<Card <Card
display={'flex'} display={'flex'}
flexDirection={'column'} flexDirection={'column'}
key={dataset._id} key={dataset._id}
py={4} py={3}
px={5} px={5}
cursor={'pointer'} cursor={'pointer'}
h={'130px'} minH={'130px'}
border={theme.borders.md} border={theme.borders.md}
boxShadow={'none'} boxShadow={'none'}
position={'relative'} position={'relative'}
@ -250,7 +264,7 @@ const Kb = () => {
parentId: dataset._id parentId: dataset._id
} }
}); });
} else if (dataset.type === DatasetTypeEnum.dataset) { } else {
router.push({ router.push({
pathname: '/dataset/detail', pathname: '/dataset/detail',
query: { query: {
@ -388,27 +402,22 @@ const Kb = () => {
{dataset.name} {dataset.name}
</Box> </Box>
</Flex> </Flex>
<Box flex={'1 0 0'} overflow={'hidden'} pt={2}> <Box
<Flex> flex={1}
{dataset.tags.filter(Boolean).map((tag, i) => ( className={'textEllipsis3'}
<Tag key={i} mr={2} mb={2}> py={1}
{tag} wordBreak={'break-all'}
</Tag> fontSize={'sm'}
))} color={'myGray.500'}
</Flex> >
{dataset.intro || t('core.dataset.Intro Placeholder')}
</Box> </Box>
<Flex alignItems={'center'} fontSize={'sm'}> <Flex alignItems={'center'} fontSize={'sm'}>
<Box flex={1}> <Box flex={1}>
<PermissionIconText permission={dataset.permission} color={'myGray.600'} /> <PermissionIconText permission={dataset.permission} color={'myGray.600'} />
</Box> </Box>
{dataset.type === DatasetTypeEnum.folder ? ( <MyIcon mr={1} name={dataset.icon as any} w={'12px'} />
<Box color={'myGray.500'}>{t('Folder')}</Box> <Box color={'myGray.500'}>{t(dataset.label)}</Box>
) : (
<>
<MyIcon mr={1} name="kbTest" w={'12px'} />
<Box color={'myGray.500'}>{dataset.vectorModel.name}</Box>
</>
)}
</Flex> </Flex>
</Card> </Card>
))} ))}
@ -417,7 +426,7 @@ const Kb = () => {
<Flex mt={'35vh'} flexDirection={'column'} alignItems={'center'}> <Flex mt={'35vh'} flexDirection={'column'} alignItems={'center'}>
<MyIcon name="empty" w={'48px'} h={'48px'} color={'transparent'} /> <MyIcon name="empty" w={'48px'} h={'48px'} color={'transparent'} />
<Box mt={2} color={'myGray.500'}> <Box mt={2} color={'myGray.500'}>
{t('core.dataset.Empty Dataset Tips')}
</Box> </Box>
</Flex> </Flex>
)} )}
@ -429,27 +438,19 @@ const Kb = () => {
onClose={() => setEditFolderData(undefined)} onClose={() => setEditFolderData(undefined)}
editCallback={async (name) => { editCallback={async (name) => {
try { try {
if (editFolderData.id) { await postCreateDataset({
await putDatasetById({ parentId,
id: editFolderData.id, name,
name type: DatasetTypeEnum.folder,
}); avatar: FolderAvatarSrc,
} else { intro: ''
await postCreateDataset({ });
parentId,
name,
type: DatasetTypeEnum.folder,
avatar: FolderAvatarSrc,
tags: ''
});
}
refetch(); refetch();
} catch (error) { } catch (error) {
return Promise.reject(error); return Promise.reject(error);
} }
}} }}
isEdit={!!editFolderData.id} isEdit={false}
name={editFolderData.name}
/> />
)} )}
{!!moveDataId && ( {!!moveDataId && (

View File

@ -138,8 +138,8 @@ const CreateModal = ({
try { try {
const src = await compressImgFileAndUpload({ const src = await compressImgFileAndUpload({
file, file,
maxW: 100, maxW: 300,
maxH: 100 maxH: 300
}); });
setValue('avatar', src); setValue('avatar', src);
setRefresh((state) => !state); setRefresh((state) => !state);

View File

@ -96,18 +96,18 @@ export function request(url: string, data: any, config: ConfigType, method: Meth
* @param {Object} config * @param {Object} config
* @returns * @returns
*/ */
export function GET<T>(url: string, params = {}, config: ConfigType = {}): Promise<T> { export function GET<T = undefined>(url: string, params = {}, config: ConfigType = {}): Promise<T> {
return request(url, params, config, 'GET'); return request(url, params, config, 'GET');
} }
export function POST<T>(url: string, data = {}, config: ConfigType = {}): Promise<T> { export function POST<T = undefined>(url: string, data = {}, config: ConfigType = {}): Promise<T> {
return request(url, data, config, 'POST'); return request(url, data, config, 'POST');
} }
export function PUT<T>(url: string, data = {}, config: ConfigType = {}): Promise<T> { export function PUT<T = undefined>(url: string, data = {}, config: ConfigType = {}): Promise<T> {
return request(url, data, config, 'PUT'); return request(url, data, config, 'PUT');
} }
export function DELETE<T>(url: string, data = {}, config: ConfigType = {}): Promise<T> { export function DELETE<T = undefined>(url: string, data = {}, config: ConfigType = {}): Promise<T> {
return request(url, data, config, 'DELETE'); return request(url, data, config, 'DELETE');
} }

View File

@ -20,7 +20,8 @@ export function reRankRecall({ query, inputs }: PostReRankProps) {
Authorization: `Bearer ${model.requestAuth}` Authorization: `Bearer ${model.requestAuth}`
} }
} }
).finally(() => { ).then((data) => {
console.log('rerank time:', Date.now() - start); console.log('rerank time:', Date.now() - start);
return data;
}); });
} }

View File

@ -4,7 +4,8 @@ import {
PatchIndexesProps, PatchIndexesProps,
UpdateDatasetDataProps UpdateDatasetDataProps
} from '@fastgpt/global/core/dataset/controller'; } from '@fastgpt/global/core/dataset/controller';
import { deletePgDataById, insertData2Pg, updatePgDataById } from './pg'; import { deletePgDataById } from '@fastgpt/service/core/dataset/data/pg';
import { insertData2Pg, updatePgDataById } from './pg';
import { Types } from 'mongoose'; import { Types } from 'mongoose';
import { DatasetDataIndexTypeEnum } from '@fastgpt/global/core/dataset/constant'; import { DatasetDataIndexTypeEnum } from '@fastgpt/global/core/dataset/constant';
import { getDefaultIndex } from '@fastgpt/global/core/dataset/utils'; import { getDefaultIndex } from '@fastgpt/global/core/dataset/utils';
@ -213,29 +214,3 @@ export async function updateData2Dataset({
tokenLen tokenLen
}; };
} }
/* delete all data by datasetIds */
export async function delDataByDatasetId({ datasetIds }: { datasetIds: string[] }) {
datasetIds = datasetIds.map((item) => String(item));
// delete pg data
await deletePgDataById(`dataset_id IN ('${datasetIds.join("','")}')`);
// delete dataset.datas
await MongoDatasetData.deleteMany({ datasetId: { $in: datasetIds } });
}
/**
* delete all data by collectionIds
*/
export async function delDataByCollectionId({ collectionIds }: { collectionIds: string[] }) {
const ids = collectionIds.map((item) => String(item));
// delete pg data
await deletePgDataById(`collection_id IN ('${ids.join("','")}')`);
// delete dataset.datas
await MongoDatasetData.deleteMany({ collectionId: { $in: ids } });
}
/**
* delete one data by mongoDataId
*/
export async function deleteDataByDataId(mongoDataId: string) {
await deletePgDataById(['data_id', mongoDataId]);
await MongoDatasetData.findByIdAndDelete(mongoDataId);
}

View File

@ -5,7 +5,7 @@ import type {
} from '@fastgpt/global/core/dataset/type.d'; } from '@fastgpt/global/core/dataset/type.d';
import { PgClient } from '@fastgpt/service/common/pg'; import { PgClient } from '@fastgpt/service/common/pg';
import { getVectorsByText } from '@/service/core/ai/vector'; import { getVectorsByText } from '@/service/core/ai/vector';
import { delay } from '@/utils/tools'; import { delay } from '@fastgpt/global/common/system/utils';
import { PgSearchRawType } from '@fastgpt/global/core/dataset/api'; import { PgSearchRawType } from '@fastgpt/global/core/dataset/api';
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema'; import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema'; import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
@ -103,31 +103,6 @@ export async function updatePgDataById({
return updatePg(); return updatePg();
} }
export async function deletePgDataById(
where: ['id' | 'dataset_id' | 'collection_id' | 'data_id', string] | string
) {
let retry = 2;
async function deleteData(): Promise<any> {
try {
await PgClient.delete(PgDatasetTableName, {
where: [where]
});
} catch (error) {
if (--retry < 0) {
return Promise.reject(error);
}
await delay(500);
return deleteData();
}
}
await deleteData();
return {
tokenLen: 0
};
}
// ------------------ search start ------------------ // ------------------ search start ------------------
type SearchProps = { type SearchProps = {
text: string; text: string;

Some files were not shown because too many files have changed in this diff Show More