Commit eb6a1368 authored by 文乙冲's avatar 文乙冲

init

parent b90f841c
Pipeline #1901 failed with stages
{
}
\ No newline at end of file
//next-i18next.config.js
/**
* @type {import('next-i18next').UserConfig}
*/
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'zh'],
localeDetection: false
},
localePath:
typeof window === 'undefined' ? require('path').resolve('../../packages/web/i18n') : '/i18n',
reloadOnPrerender: process.env.NODE_ENV === 'development'
};
const { i18n } = require('./next-i18next.config');
const path = require('path');
const fs = require('fs');
const isDev = process.env.NODE_ENV === 'development';
/** @type {import('next').NextConfig} */
const nextConfig = {
i18n,
output: 'standalone',
reactStrictMode: isDev ? false : true,
compress: true,
webpack(config, { isServer, nextRuntime }) {
Object.assign(config.resolve.alias, {
'@mongodb-js/zstd': false,
'@aws-sdk/credential-providers': false,
snappy: false,
aws4: false,
'mongodb-client-encryption': false,
kerberos: false,
'supports-color': false,
'bson-ext': false,
'pg-native': false
});
config.module = {
...config.module,
rules: config.module.rules.concat([
{
test: /\.svg$/i,
issuer: /\.[jt]sx?$/,
use: ['@svgr/webpack']
},
{
test: /\.node$/,
use: [{ loader: 'nextjs-node-loader' }]
}
]),
exprContextCritical: false,
unknownContextCritical: false
};
if (!config.externals) {
config.externals = [];
}
if (isServer) {
// config.externals.push('@zilliz/milvus2-sdk-node');
// if (nextRuntime === 'nodejs') {
// const oldEntry = config.entry;
// config = {
// ...config,
// async entry(...args) {
// const entries = await oldEntry(...args);
// return {
// ...entries,
// // ...getWorkerConfig(),
// 'worker/systemPluginRun': path.resolve(
// process.cwd(),
// './src/app/plugins/runtime/worker.ts'
// )
// };
// }
// };
// }
} else {
config.resolve = {
...config.resolve,
fallback: {
...config.resolve.fallback,
fs: false
}
};
}
config.experiments = {
asyncWebAssembly: true,
layers: true
};
return config;
},
transpilePackages: ['ahooks'],
experimental: {
// 优化 Server Components 的构建和运行,避免不必要的客户端打包。
serverComponentsExternalPackages: ['mongoose', 'pg', '@node-rs/jieba', 'duck-duck-scrape'],
outputFileTracingRoot: path.join(__dirname, './output')
}
};
module.exports = nextConfig;
// function getWorkerConfig() {
// const result = fs.readdirSync(path.resolve(__dirname, './packages/service/worker'));
// // 获取所有的目录名
// const folderList = result.filter((item) => {
// return fs
// .statSync(path.resolve(__dirname, '../../packages/service/worker', item))
// .isDirectory();
// });
// /*
// {
// 'worker/htmlStr2Md': path.resolve(
// process.cwd(),
// '../../packages/service/worker/htmlStr2Md/index.ts'
// ),
// 'worker/countGptMessagesTokens': path.resolve(
// process.cwd(),
// '../../packages/service/worker/countGptMessagesTokens/index.ts'
// ),
// 'worker/readFile': path.resolve(
// process.cwd(),
// '../../packages/service/worker/readFile/index.ts'
// )
// }
// */
// const workerConfig = folderList.reduce((acc, item) => {
// acc[`worker/${item}`] = path.resolve(
// process.cwd(),
// `../../packages/service/worker/${item}/index.ts`
// );
// return acc;
// }, {});
// return workerConfig;
// }
This source diff could not be displayed because it is too large. You can view the blob instead.
import { ErrType } from '../errorCode';
import { i18nT } from '../../../../web/i18n/utils';
/* dataset: 502000 */
export enum AppErrEnum {
unExist = 'appUnExist',
unAuthApp = 'unAuthApp',
invalidOwner = 'invalidOwner',
invalidAppType = 'invalidAppType'
}
const appErrList = [
{
statusText: AppErrEnum.unExist,
message: i18nT('common:code_error.app_error.not_exist')
},
{
statusText: AppErrEnum.unAuthApp,
message: i18nT('common:code_error.app_error.un_auth_app')
},
{
statusText: AppErrEnum.invalidOwner,
message: i18nT('common:code_error.app_error.invalid_owner')
},
{
statusText: AppErrEnum.invalidAppType,
message: i18nT('common:code_error.app_error.invalid_app_type')
}
];
export default appErrList.reduce((acc, cur, index) => {
return {
...acc,
[cur.statusText]: {
code: 502000 + index,
statusText: cur.statusText,
message: cur.message,
data: null
}
};
}, {} as ErrType<`${AppErrEnum}`>);
import { ErrType } from '../errorCode';
import { i18nT } from '../../../../web/i18n/utils';
/* dataset: 504000 */
export enum ChatErrEnum {
unAuthChat = 'unAuthChat'
}
const errList = [
{
statusText: ChatErrEnum.unAuthChat,
message: i18nT('common:code_error.chat_error.un_auth')
}
];
export default errList.reduce((acc, cur, index) => {
return {
...acc,
[cur.statusText]: {
code: 504000 + index,
statusText: cur.statusText,
message: cur.message,
data: null
}
};
}, {} as ErrType<`${ChatErrEnum}`>);
import { ErrType } from '../errorCode';
/* dataset: 507000 */
const startCode = 507000;
export enum CommonErrEnum {
fileNotFound = 'fileNotFound',
unAuthFile = 'unAuthFile',
missingParams = 'missingParams',
inheritPermissionError = 'inheritPermissionError'
}
const datasetErr = [
{
statusText: CommonErrEnum.fileNotFound,
message: 'error.fileNotFound'
},
{
statusText: CommonErrEnum.unAuthFile,
message: 'error.unAuthFile'
},
{
statusText: CommonErrEnum.missingParams,
message: 'error.missingParams'
},
{
statusText: CommonErrEnum.inheritPermissionError,
message: 'error.inheritPermissionError'
}
];
export default datasetErr.reduce((acc, cur, index) => {
return {
...acc,
[cur.statusText]: {
code: startCode + index,
statusText: cur.statusText,
message: cur.message,
data: null
}
};
}, {} as ErrType<`${CommonErrEnum}`>);
import { ErrType } from '../errorCode';
/* dataset: 501000 */
export enum DatasetErrEnum {
unExist = 'unExistDataset',
unAuthDataset = 'unAuthDataset',
unCreateCollection = 'unCreateCollection',
unAuthDatasetCollection = 'unAuthDatasetCollection',
unAuthDatasetData = 'unAuthDatasetData',
unAuthDatasetFile = 'unAuthDatasetFile',
unLinkCollection = 'unLinkCollection',
invalidVectorModelOrQAModel = 'invalidVectorModelOrQAModel'
}
const datasetErr = [
{
statusText: DatasetErrEnum.unExist,
message: 'core.dataset.error.unExistDataset'
},
{
statusText: DatasetErrEnum.unAuthDataset,
message: 'core.dataset.error.unAuthDataset'
},
{
statusText: DatasetErrEnum.unAuthDatasetCollection,
message: 'core.dataset.error.unAuthDatasetCollection'
},
{
statusText: DatasetErrEnum.unAuthDatasetData,
message: 'core.dataset.error.unAuthDatasetData'
},
{
statusText: DatasetErrEnum.unAuthDatasetFile,
message: 'core.dataset.error.unAuthDatasetFile'
},
{
statusText: DatasetErrEnum.unCreateCollection,
message: 'core.dataset.error.unCreateCollection'
},
{
statusText: DatasetErrEnum.unLinkCollection,
message: 'core.dataset.error.unLinkCollection'
},
{
statusText: DatasetErrEnum.invalidVectorModelOrQAModel,
message: 'core.dataset.error.invalidVectorModelOrQAModel'
}
];
export default datasetErr.reduce((acc, cur, index) => {
return {
...acc,
[cur.statusText]: {
code: 501000 + index,
statusText: cur.statusText,
message: cur.message,
data: null
}
};
}, {} as ErrType<`${DatasetErrEnum}`>);
import { ErrType } from '../errorCode';
import { i18nT } from '../../../../web/i18n/utils';
/* dataset: 506000 */
export enum OpenApiErrEnum {
unExist = 'openapiUnExist',
unAuth = 'openapiUnAuth',
exceedLimit = 'openapiExceedLimit'
}
const errList = [
{
statusText: OpenApiErrEnum.unExist,
message: i18nT('common:code_error.openapi_error.api_key_not_exist')
},
{
statusText: OpenApiErrEnum.unAuth,
message: i18nT('common:code_error.openapi_error.un_auth')
},
{
statusText: OpenApiErrEnum.exceedLimit,
message: i18nT('common:code_error.openapi_error.exceed_limit')
}
];
export default errList.reduce((acc, cur, index) => {
return {
...acc,
[cur.statusText]: {
code: 506000 + index,
statusText: cur.statusText,
message: cur.message,
data: null
}
};
}, {} as ErrType<`${OpenApiErrEnum}`>);
import { ErrType } from '../errorCode';
import { i18nT } from '../../../../web/i18n/utils';
/* dataset: 505000 */
export enum OutLinkErrEnum {
unExist = 'outlinkUnExist',
unAuthLink = 'unAuthLink',
linkUnInvalid = 'linkUnInvalid',
unAuthUser = 'unAuthUser'
}
const errList = [
{
statusText: OutLinkErrEnum.unExist,
message: i18nT('common:code_error.outlink_error.link_not_exist')
},
{
statusText: OutLinkErrEnum.unAuthLink,
message: i18nT('common:code_error.outlink_error.invalid_link')
},
{
code: 501,
statusText: OutLinkErrEnum.linkUnInvalid,
message: i18nT('common:code_error.outlink_error.invalid_link') // 使用相同的错误消息
},
{
statusText: OutLinkErrEnum.unAuthUser,
message: i18nT('common:code_error.outlink_error.un_auth_user')
}
];
export default errList.reduce((acc, cur, index) => {
return {
...acc,
[cur.statusText]: {
code: cur?.code || 505000 + index,
statusText: cur.statusText,
message: cur.message,
data: null
}
};
}, {} as ErrType<`${OutLinkErrEnum}`>);
import { ErrType } from '../errorCode';
import { i18nT } from '../../../../web/i18n/utils';
/* dataset: 508000 */
export enum PluginErrEnum {
unExist = 'pluginUnExist',
unAuth = 'pluginUnAuth'
}
const errList = [
{
statusText: PluginErrEnum.unExist,
message: i18nT('common:code_error.plugin_error.not_exist')
},
{
statusText: PluginErrEnum.unAuth,
message: i18nT('common:code_error.plugin_error.un_auth')
}
];
export default errList.reduce((acc, cur, index) => {
return {
...acc,
[cur.statusText]: {
code: 508000 + index,
statusText: cur.statusText,
message: cur.message,
data: null
}
};
}, {} as ErrType<`${PluginErrEnum}`>);
import { ErrType } from '../errorCode';
import { i18nT } from '../../../../web/i18n/utils';
/* dataset: 509000 */
export enum SystemErrEnum {
communityVersionNumLimit = 'communityVersionNumLimit'
}
const systemErr = [
{
statusText: SystemErrEnum.communityVersionNumLimit,
message: i18nT('common:code_error.system_error.community_version_num_limit')
}
];
export default systemErr.reduce((acc, cur, index) => {
return {
...acc,
[cur.statusText]: {
code: 509000 + index,
statusText: cur.statusText,
message: cur.message,
data: null
}
};
}, {} as ErrType<`${SystemErrEnum}`>);
import {ErrType} from '../errorCode';
import {i18nT} from '../../../../web/i18n/utils';
/* team: 500000 */
export enum TeamErrEnum {
teamOverSize = 'teamOverSize',
unAuthTeam = 'unAuthTeam',
aiPointsNotEnough = 'aiPointsNotEnough',
datasetSizeNotEnough = 'datasetSizeNotEnough',
datasetAmountNotEnough = 'datasetAmountNotEnough',
appAmountNotEnough = 'appAmountNotEnough',
pluginAmountNotEnough = 'pluginAmountNotEnough',
websiteSyncNotEnough = 'websiteSyncNotEnough',
reRankNotEnough = 'reRankNotEnough'
}
const teamErr = [
{
statusText: TeamErrEnum.teamOverSize,
message: i18nT('common:code_error.team_error.over_size')
},
{
statusText: TeamErrEnum.unAuthTeam,
message: i18nT('common:code_error.team_error.un_auth')
},
{
statusText: TeamErrEnum.aiPointsNotEnough,
message: i18nT('common:code_error.team_error.ai_points_not_enough')
}, // 需要定义或留空
{
statusText: TeamErrEnum.datasetSizeNotEnough,
message: i18nT('common:code_error.team_error.dataset_size_not_enough')
},
{
statusText: TeamErrEnum.datasetAmountNotEnough,
message: i18nT('common:code_error.team_error.dataset_amount_not_enough')
},
{
statusText: TeamErrEnum.appAmountNotEnough,
message: i18nT('common:code_error.team_error.app_amount_not_enough')
},
{
statusText: TeamErrEnum.pluginAmountNotEnough,
message: i18nT('common:code_error.team_error.plugin_amount_not_enough')
},
{
statusText: TeamErrEnum.websiteSyncNotEnough,
message: i18nT('common:code_error.team_error.website_sync_not_enough')
},
{
statusText: TeamErrEnum.reRankNotEnough,
message: i18nT('common:code_error.team_error.re_rank_not_enough')
}
];
export default teamErr.reduce((acc, cur, index) => {
return {
...acc,
[cur.statusText]: {
code: 500000 + index,
statusText: cur.statusText,
message: cur.message,
data: null
}
};
}, {} as ErrType<`${TeamErrEnum}`>);
import { ErrType } from '../errorCode';
import { i18nT } from '../../../../web/i18n/utils';
/* team: 503000 */
export enum UserErrEnum {
unAuthUser = 'unAuthUser',
unAuthRole = 'unAuthRole',
binVisitor = 'binVisitor',
balanceNotEnough = 'balanceNotEnough'
}
const errList = [
{
statusText: UserErrEnum.unAuthUser,
message: i18nT('common:code_error.user_error.un_auth_user')
},
{
statusText: UserErrEnum.binVisitor,
message: i18nT('common:code_error.user_error.bin_visitor')
}, // 身份校验未通过
{
statusText: UserErrEnum.binVisitor,
message: i18nT('common:code_error.user_error.bin_visitor_guest')
}, // 游客身份
{
statusText: UserErrEnum.balanceNotEnough,
message: i18nT('common:code_error.user_error.balance_not_enough')
}
];
export default errList.reduce((acc, cur, index) => {
return {
...acc,
[cur.statusText]: {
code: 503000 + index,
statusText: cur.statusText,
message: cur.message,
data: null
}
};
}, {} as ErrType<`${UserErrEnum}`>);
import appErr from './code/app';
import chatErr from './code/chat';
import datasetErr from './code/dataset';
import openapiErr from './code/openapi';
import pluginErr from './code/plugin';
import outLinkErr from './code/outLink';
import teamErr from './code/team';
import userErr from './code/user';
import commonErr from './code/common';
import SystemErrEnum from './code/system';
import { i18nT } from '../../../web/i18n/utils';
export const ERROR_CODE: { [key: number]: string } = {
400: i18nT('common:code_error.error_code.400'),
401: i18nT('common:code_error.error_code.401'),
403: i18nT('common:code_error.error_code.403'),
404: i18nT('common:code_error.error_code.404'),
405: i18nT('common:code_error.error_code.405'),
406: i18nT('common:code_error.error_code.406'),
410: i18nT('common:code_error.error_code.410'),
422: i18nT('common:code_error.error_code.422'),
500: i18nT('common:code_error.error_code.500'),
502: i18nT('common:code_error.error_code.502'),
503: i18nT('common:code_error.error_code.503'),
504: i18nT('common:code_error.error_code.504')
};
export const TOKEN_ERROR_CODE: Record<number, string> = {
403: i18nT('common:code_error.token_error_code.403')
};
export const proxyError: Record<string, boolean> = {
ECONNABORTED: true,
ECONNRESET: true
};
export enum ERROR_ENUM {
unAuthorization = 'unAuthorization',
insufficientQuota = 'insufficientQuota',
unAuthModel = 'unAuthModel',
unAuthApiKey = 'unAuthApiKey',
unAuthFile = 'unAuthFile'
}
export type ErrType<T> = Record<
string,
{
code: number;
statusText: T;
message: string;
data: null;
}
>;
export const ERROR_RESPONSE: Record<
any,
{
code: number;
statusText: string;
message: string;
data?: any;
}
> = {
[ERROR_ENUM.unAuthorization]: {
code: 403,
statusText: ERROR_ENUM.unAuthorization,
message: i18nT('common:code_error.error_message.403'),
data: null
},
[ERROR_ENUM.insufficientQuota]: {
code: 510,
statusText: ERROR_ENUM.insufficientQuota,
message: i18nT('common:code_error.error_message.510'),
data: null
},
[ERROR_ENUM.unAuthModel]: {
code: 511,
statusText: ERROR_ENUM.unAuthModel,
message: i18nT('common:code_error.error_message.511'),
data: null
},
[ERROR_ENUM.unAuthFile]: {
code: 513,
statusText: ERROR_ENUM.unAuthFile,
message: i18nT('common:code_error.error_message.513'),
data: null
},
[ERROR_ENUM.unAuthApiKey]: {
code: 514,
statusText: ERROR_ENUM.unAuthApiKey,
message: i18nT('common:code_error.error_message.514'),
data: null
},
...appErr,
...chatErr,
...datasetErr,
...openapiErr,
...outLinkErr,
...teamErr,
...userErr,
...pluginErr,
...commonErr,
...SystemErrEnum
};
import { replaceSensitiveText } from '../string/tools';
export const getErrText = (err: any, def = ''): any => {
const msg: string =
typeof err === 'string'
? err
: err?.response?.data?.message || err?.response?.message || err?.message || def;
msg && console.log('error =>', msg);
return replaceSensitiveText(msg);
};
/* mongo fs bucket */
export enum BucketNameEnum {
dataset = 'dataset',
chat = 'chat'
}
export const bucketNameMap = {
[BucketNameEnum.dataset]: {
label: 'file:bucket_file',
previewExpireMinutes: 30 // 30 minutes
},
[BucketNameEnum.chat]: {
label: 'file:bucket_chat',
previewExpireMinutes: 7 * 24 * 60 // 7 days
}
};
export const ReadFileBaseUrl = '/api/common/file/read';
export const documentFileType = '.txt, .docx, .csv, .xlsx, .pdf, .md, .html, .pptx';
import { getErrText } from '../error/utils';
import { replaceRegChars } from './tools';
export const CUSTOM_SPLIT_SIGN = '-----CUSTOM_SPLIT_SIGN-----';
type SplitProps = {
text: string;
chunkLen: number;
overlapRatio?: number;
customReg?: string[];
};
export type TextSplitProps = Omit<SplitProps, 'text' | 'chunkLen'> & {
chunkLen?: number;
};
type SplitResponse = {
chunks: string[];
chars: number;
};
// 判断字符串是否为markdown的表格形式
const strIsMdTable = (str: string) => {
// 检查是否包含表格分隔符 |
if (!str.includes('|')) {
return false;
}
const lines = str.split('\n');
// 检查表格是否至少有两行
if (lines.length < 2) {
return false;
}
// 检查表头行是否包含 |
const headerLine = lines[0].trim();
if (!headerLine.startsWith('|') || !headerLine.endsWith('|')) {
return false;
}
// 检查分隔行是否由 | 和 - 组成
const separatorLine = lines[1].trim();
const separatorRegex = /^(\|[\s:]*-+[\s:]*)+\|$/;
if (!separatorRegex.test(separatorLine)) {
return false;
}
// 检查数据行是否包含 |
for (let i = 2; i < lines.length; i++) {
const dataLine = lines[i].trim();
if (dataLine && (!dataLine.startsWith('|') || !dataLine.endsWith('|'))) {
return false;
}
}
return true;
};
const markdownTableSplit = (props: SplitProps): SplitResponse => {
let { text = '', chunkLen } = props;
const splitText2Lines = text.split('\n');
const header = splitText2Lines[0];
const headerSize = header.split('|').length - 2;
const mdSplitString = `| ${new Array(headerSize > 0 ? headerSize : 1)
.fill(0)
.map(() => '---')
.join(' | ')} |`;
const chunks: string[] = [];
let chunk = `${header}
${mdSplitString}
`;
for (let i = 2; i < splitText2Lines.length; i++) {
if (chunk.length + splitText2Lines[i].length > chunkLen * 1.2) {
chunks.push(chunk);
chunk = `${header}
${mdSplitString}
`;
}
chunk += `${splitText2Lines[i]}\n`;
}
if (chunk) {
chunks.push(chunk);
}
return {
chunks,
chars: chunks.reduce((sum, chunk) => sum + chunk.length, 0)
};
};
const commonSplit = (props: SplitProps): SplitResponse => {
let { text = '', chunkLen, overlapRatio = 0.2, customReg = [] } = props;
const splitMarker = 'SPLIT_HERE_SPLIT_HERE';
const codeBlockMarker = 'CODE_BLOCK_LINE_MARKER';
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);
});
// replace invalid \n
text = text.replace(/(\r?\n|\r){3,}/g, '\n\n\n');
// The larger maxLen is, the next sentence is less likely to trigger splitting
const stepReges: { reg: RegExp; maxLen: number }[] = [
...customReg.map((text) => ({
reg: new RegExp(`(${replaceRegChars(text)})`, 'g'),
maxLen: chunkLen * 1.4
})),
{ reg: /^(#\s[^\n]+)\n/gm, maxLen: chunkLen * 1.2 },
{ reg: /^(##\s[^\n]+)\n/gm, maxLen: chunkLen * 1.2 },
{ reg: /^(###\s[^\n]+)\n/gm, maxLen: chunkLen * 1.2 },
{ reg: /^(####\s[^\n]+)\n/gm, maxLen: chunkLen * 1.2 },
{ reg: /([\n]([`~]))/g, maxLen: chunkLen * 4 }, // code block
{ reg: /([\n](?!\s*[\*\-|>0-9]))/g, maxLen: chunkLen * 2 }, // 增大块,尽可能保证它是一个完整的段落。 (?![\*\-|>`0-9]): markdown special char
{ reg: /([\n])/g, maxLen: chunkLen * 1.2 },
// ------ There's no overlap on the top
{ reg: /([]|([a-zA-Z])\.\s)/g, maxLen: chunkLen * 1.2 },
{ reg: /([]|!\s)/g, maxLen: chunkLen * 1.2 },
{ reg: /([]|\?\s)/g, maxLen: chunkLen * 1.4 },
{ reg: /([]|;\s)/g, maxLen: chunkLen * 1.6 },
{ reg: /([]|,\s)/g, maxLen: chunkLen * 2 }
];
const customRegLen = customReg.length;
const checkIsCustomStep = (step: number) => step < customRegLen;
const checkIsMarkdownSplit = (step: number) => step >= customRegLen && step <= 3 + customRegLen;
const checkIndependentChunk = (step: number) => step >= customRegLen && step <= 4 + customRegLen;
const checkForbidOverlap = (step: number) => step <= 6 + customRegLen;
// if use markdown title split, Separate record title
const getSplitTexts = ({ text, step }: { text: string; step: number }) => {
if (step >= stepReges.length) {
return [
{
text,
title: ''
}
];
}
const isCustomStep = checkIsCustomStep(step);
const isMarkdownSplit = checkIsMarkdownSplit(step);
const independentChunk = checkIndependentChunk(step);
const { reg } = stepReges[step];
const splitTexts = text
.replace(
reg,
(() => {
if (isCustomStep) return splitMarker;
if (independentChunk) return `${splitMarker}$1`;
return `$1${splitMarker}`;
})()
)
.split(`${splitMarker}`)
.filter((part) => part.trim());
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());
};
/* Gets the overlap at the end of a text as the beginning of the next block */
const getOneTextOverlapText = ({ text, step }: { text: string; step: number }): string => {
const forbidOverlap = checkForbidOverlap(step);
const maxOverlapLen = chunkLen * 0.4;
// step >= stepReges.length: Do not overlap incomplete sentences
if (forbidOverlap || overlapLen === 0 || step >= stepReges.length) return '';
const splitTexts = getSplitTexts({ text, step });
let overlayText = '';
for (let i = splitTexts.length - 1; i >= 0; i--) {
const currentText = splitTexts[i].text;
const newText = currentText + overlayText;
const newTextLen = newText.length;
if (newTextLen > overlapLen) {
if (newTextLen > maxOverlapLen) {
const text = getOneTextOverlapText({ text: newText, step: step + 1 });
return text || overlayText;
}
return newText;
}
overlayText = newText;
}
return overlayText;
};
const splitTextRecursively = ({
text = '',
step,
lastText,
mdTitle = ''
}: {
text: string;
step: number;
lastText: string;
mdTitle: string;
}): string[] => {
const independentChunk = checkIndependentChunk(step);
const isCustomStep = checkIsCustomStep(step);
// oversize
if (step >= stepReges.length) {
if (text.length < chunkLen * 3) {
return [text];
}
// use slice-chunkLen to split text
const chunks: string[] = [];
for (let i = 0; i < text.length; i += chunkLen - overlapLen) {
chunks.push(`${mdTitle}${text.slice(i, i + chunkLen)}`);
}
return chunks;
}
// split text by special char
const splitTexts = getSplitTexts({ text, step });
const maxLen = splitTexts.length > 1 ? stepReges[step].maxLen : chunkLen;
const minChunkLen = chunkLen * 0.7;
const miniChunkLen = 30;
// console.log(splitTexts, stepReges[step].reg);
const chunks: string[] = [];
for (let i = 0; i < splitTexts.length; i++) {
const item = splitTexts[i];
const currentTitle = `${mdTitle}${item.title}`;
const currentText = item.text;
const currentTextLen = currentText.length;
const lastTextLen = lastText.length;
const newText = lastText + currentText;
const newTextLen = lastTextLen + currentTextLen;
// newText is too large(now, The lastText must be smaller than chunkLen)
if (newTextLen > maxLen) {
// lastText greater minChunkLen, direct push it to chunks, not add to next chunk. (large lastText)
if (lastTextLen > minChunkLen) {
chunks.push(`${currentTitle}${lastText}`);
lastText = getOneTextOverlapText({ text: lastText, step }); // next chunk will start with overlayText
i--;
continue;
}
// split new Text, split chunks must will greater 1 (small lastText)
const innerChunks = splitTextRecursively({
text: newText,
step: step + 1,
lastText: '',
mdTitle: currentTitle
});
const lastChunk = innerChunks[innerChunks.length - 1];
// last chunk is too small, concat it to lastText(next chunk start)
if (!independentChunk && lastChunk.length < minChunkLen) {
chunks.push(...innerChunks.slice(0, -1));
lastText = lastChunk;
} else {
chunks.push(...innerChunks);
// compute new overlapText
lastText = getOneTextOverlapText({
text: lastChunk,
step
});
}
continue;
}
// size less than chunkLen, push text to last chunk. now, text definitely less than maxLen
lastText = newText;
// markdown paragraph block: Direct addition; If the chunk size reaches, add a chunk
if (
isCustomStep ||
(independentChunk && newTextLen > miniChunkLen) ||
newTextLen >= chunkLen
) {
chunks.push(`${currentTitle}${lastText}`);
lastText = getOneTextOverlapText({ text: lastText, step });
}
}
/* If the last chunk is independent, it needs to be push chunks. */
if (lastText && chunks[chunks.length - 1] && !chunks[chunks.length - 1].endsWith(lastText)) {
if (lastText.length < chunkLen * 0.4) {
chunks[chunks.length - 1] = chunks[chunks.length - 1] + lastText;
} else {
chunks.push(`${mdTitle}${lastText}`);
}
} else if (lastText && chunks.length === 0) {
chunks.push(lastText);
}
return chunks;
};
try {
const chunks = splitTextRecursively({
text,
step: 0,
lastText: '',
mdTitle: ''
}).map((chunk) => chunk?.replaceAll(codeBlockMarker, '\n') || ''); // restore code block
const chars = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
return {
chunks,
chars
};
} catch (err) {
throw new Error(getErrText(err));
}
};
/**
* text split into chunks
* chunkLen - one chunk len. max: 3500
* overlapLen - The size of the before and after Text
* chunkLen > overlapLen
* markdown
*/
export const splitText2Chunks = (props: SplitProps): SplitResponse => {
let { text = '' } = props;
const start = Date.now();
const splitWithCustomSign = text.split(CUSTOM_SPLIT_SIGN);
const splitResult = splitWithCustomSign.map((item) => {
if (strIsMdTable(item)) {
return markdownTableSplit(props);
}
return commonSplit(props);
});
return {
chunks: splitResult.map((item) => item.chunks).flat(),
chars: splitResult.reduce((sum, item) => sum + item.chars, 0)
};
};
import crypto from 'crypto';
import { customAlphabet } from 'nanoid';
import {SECTOR_CODE_ENUM} from "../../support/sector/type";
/* check string is a mongo objectId */
export function strIsMongoId(id?: string): boolean {
if (!id) return false
const mongoIdPattern = /^[0-9a-fA-F]{24}$/;
return mongoIdPattern.test(id);
}
/* check string is a web link */
export function strIsLink(str?: string) {
if (!str) return false;
if (/^((http|https)?:\/\/|www\.|\/)[^\s/$.?#].[^\s]*$/i.test(str)) return true;
return false;
}
/* hash string */
export const hashStr = (str: string) => {
return crypto.createHash('sha256').update(str).digest('hex');
};
/* simple text, remove chinese space and extra \n */
export const simpleText = (text = '') => {
text = text.trim();
text = text.replace(/([\u4e00-\u9fa5])[\s&&[^\n]]+([\u4e00-\u9fa5])/g, '$1$2');
text = text.replace(/\r\n|\r/g, '\n');
text = text.replace(/\n{3,}/g, '\n\n');
text = text.replace(/[\s&&[^\n]]{2,}/g, ' ');
text = text.replace(/[\x00-\x08]/g, ' ');
return text;
};
/*
replace {{variable}} to value
*/
export function replaceVariable(text: any, obj: Record<string, string | number>) {
if (!(typeof text === 'string')) return text;
for (const key in obj) {
const val = obj[key];
if (!['string', 'number'].includes(typeof val)) continue;
text = text.replace(new RegExp(`{{(${key})}}`, 'g'), String(val));
}
return text || '';
}
/* replace sensitive text */
export const replaceSensitiveText = (text: string) => {
// 1. http link
text = text.replace(/(?<=https?:\/\/)[^\s]+/g, 'xxx');
// 2. nx-xxx 全部替换成xxx
text = text.replace(/ns-[\w-]+/g, 'xxx');
return text;
};
/* Make sure the first letter is definitely lowercase */
export const getNanoid = (size = 12) => {
const firstChar = customAlphabet('abcdefghijklmnopqrstuvwxyz', 1)();
if (size === 1) return firstChar;
const randomsStr = customAlphabet(
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890',
size - 1
)();
return `${firstChar}${randomsStr}`;
};
/* Custom text to reg, need to replace special chats */
export const replaceRegChars = (text: string) => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
export const getRegQueryStr = (text: string, flags = 'i') => {
const formatText = replaceRegChars(text);
const chars = formatText.split('');
const regexPattern = chars.join('.*');
return new RegExp(regexPattern, flags);
};
/* slice json str */
export const sliceJsonStr = (str: string) => {
str = str.replace(/(\\n|\\)/g, '').replace(/ /g, '');
const jsonRegex = /{(?:[^{}]|{(?:[^{}]|{[^{}]*})*})*}/g;
const matches = str.match(jsonRegex);
if (!matches) {
return '';
}
// 找到第一个完整的 JSON 字符串
const jsonStr = matches[0];
return jsonStr;
};
export const sliceStrStartEnd = (str: string, start: number, end: number) => {
const overSize = str.length > start + end;
if (!overSize) return str;
const startContent = str.slice(0, start);
const endContent = overSize ? str.slice(-end) : '';
return `${startContent}${overSize ? `\n\n...[hide ${str.length - start - end} chars]...\n\n` : ''}${endContent}`;
};
export const getDevice = (device?: string) => {
if (!device) return 'fbc';
const lowDevice = device.toLowerCase();
if (lowDevice.includes('woa')) {
if (lowDevice.includes('android') || lowDevice.includes('ios')) {
return 'app';
} else {
return 'itw';
}
} else {
return 'fbc';
}
};
export const getSectorName = (code:SECTOR_CODE_ENUM)=>{
const SECTOR_MAP = {
"T": '通威集团',
"G": '通威股份',
"S": '通威农发',
"Y": '通威太阳能',
"X": '通威新能源',
"P": '通威食品',
"Z": '通威组件',
"L": '通威永祥'
};
return SECTOR_MAP[code]
}
\ No newline at end of file
import { i18nT } from '../../../web/i18n/utils';
export enum FlowNodeTemplateTypeEnum {
systemInput = 'systemInput',
ai = 'ai',
function = 'function',
tools = 'tools',
interactive = 'interactive',
search = 'search',
multimodal = 'multimodal',
communication = 'communication',
other = 'other',
teamApp = 'teamApp'
}
export enum WorkflowIOValueTypeEnum {
string = 'string',
number = 'number',
boolean = 'boolean',
object = 'object',
arrayString = 'arrayString',
arrayNumber = 'arrayNumber',
arrayBoolean = 'arrayBoolean',
arrayObject = 'arrayObject',
arrayAny = 'arrayAny',
any = 'any',
chatHistory = 'chatHistory',
datasetQuote = 'datasetQuote',
dynamic = 'dynamic',
// plugin special type
selectApp = 'selectApp',
selectDataset = 'selectDataset'
}
export const toolValueTypeList = [
{
label: WorkflowIOValueTypeEnum.string,
value: WorkflowIOValueTypeEnum.string,
jsonSchema: {
type: 'string'
}
},
{
label: WorkflowIOValueTypeEnum.number,
value: WorkflowIOValueTypeEnum.number,
jsonSchema: {
type: 'number'
}
},
{
label: WorkflowIOValueTypeEnum.boolean,
value: WorkflowIOValueTypeEnum.boolean,
jsonSchema: {
type: 'boolean'
}
},
{
label: 'array<string>',
value: WorkflowIOValueTypeEnum.arrayString,
jsonSchema: {
type: 'array',
items: {
type: 'string'
}
}
},
{
label: 'array<number>',
value: WorkflowIOValueTypeEnum.arrayNumber,
jsonSchema: {
type: 'array',
items: {
type: 'number'
}
}
},
{
label: 'array<boolean>',
value: WorkflowIOValueTypeEnum.arrayBoolean,
jsonSchema: {
type: 'array',
items: {
type: 'boolean'
}
}
}
];
/* reg: modulename key */
export enum NodeInputKeyEnum {
// old
welcomeText = 'welcomeText',
switch = 'switch', // a trigger switch
history = 'history',
answerText = 'text',
// system config
questionGuide = 'questionGuide',
tts = 'tts',
whisper = 'whisper',
variables = 'variables',
scheduleTrigger = 'scheduleTrigger',
chatInputGuide = 'chatInputGuide',
// plugin config
instruction = 'instruction',
// entry
userChatInput = 'userChatInput',
inputFiles = 'inputFiles',
agents = 'agents', // cq agent key
// latest
// common
aiModel = 'model',
aiSystemPrompt = 'systemPrompt',
description = 'description',
anyInput = 'system_anyInput',
textareaInput = 'system_textareaInput',
addInputParam = 'system_addInputParam',
// history
historyMaxAmount = 'maxContext',
// ai chat
aiChatTemperature = 'temperature',
aiChatMaxToken = 'maxToken',
aiChatSettingModal = 'aiSettings',
aiChatIsResponseText = 'isResponseAnswerText',
aiChatQuoteRole = 'aiChatQuoteRole',
aiChatQuoteTemplate = 'quoteTemplate',
aiChatQuotePrompt = 'quotePrompt',
aiChatDatasetQuote = 'quoteQA',
aiChatVision = 'aiChatVision',
stringQuoteText = 'stringQuoteText',
aiChatReasoning = 'aiChatReasoning',
// dataset
datasetSelectList = 'datasets',
datasetSimilarity = 'similarity',
datasetMaxTokens = 'limit',
datasetSearchMode = 'searchMode',
datasetSearchUsingReRank = 'usingReRank',
datasetSearchUsingExtensionQuery = 'datasetSearchUsingExtensionQuery',
datasetSearchExtensionModel = 'datasetSearchExtensionModel',
datasetSearchExtensionBg = 'datasetSearchExtensionBg',
collectionFilterMatch = 'collectionFilterMatch',
// concat dataset
datasetQuoteList = 'system_datasetQuoteList',
// context extract
contextExtractInput = 'content',
extractKeys = 'extractKeys',
// http
httpReqUrl = 'system_httpReqUrl',
httpHeaders = 'system_httpHeader',
httpMethod = 'system_httpMethod',
httpParams = 'system_httpParams',
httpJsonBody = 'system_httpJsonBody',
httpFormBody = 'system_httpFormBody',
httpContentType = 'system_httpContentType',
httpTimeout = 'system_httpTimeout',
abandon_httpUrl = 'url',
// app
runAppSelectApp = 'app',
// plugin
pluginId = 'pluginId',
pluginStart = 'pluginStart',
// if else
condition = 'condition',
ifElseList = 'ifElseList',
// variable update
updateList = 'updateList',
// code
code = 'code',
codeType = 'codeType', // js|py
userInputForms = 'userInputForms',
// read files
fileUrlList = 'fileUrlList',
// user select
userSelectOptions = 'userSelectOptions'
}
export enum NodeOutputKeyEnum {
// common
userChatInput = 'userChatInput',
history = 'history',
answerText = 'answerText', // module answer. the value will be show and save to history
reasoningText = 'reasoningText', // node reasoning. the value will be show but not save to history
success = 'success',
failed = 'failed',
error = 'error',
text = 'system_text',
addOutputParam = 'system_addOutputParam',
rawResponse = 'system_rawResponse',
// start
userFiles = 'userFiles',
// dataset
datasetQuoteQA = 'quoteQA',
// classify
cqResult = 'cqResult',
// context extract
contextExtractFields = 'fields',
// tf switch
resultTrue = 'system_resultTrue',
resultFalse = 'system_resultFalse',
// tools
selectedTools = 'selectedTools',
// http
httpRawResponse = 'httpRawResponse',
// plugin
pluginStart = 'pluginStart',
// if else
ifElseResult = 'ifElseResult',
//user select
selectResult = 'selectResult',
// form input
formInputResult = 'formInputResult'
}
export enum VariableInputEnum {
input = 'input',
textarea = 'textarea',
select = 'select',
custom = 'custom'
}
export const variableMap = {
[VariableInputEnum.input]: {
icon: 'core/app/variable/input',
title: i18nT('common:core.module.variable.input type'),
desc: ''
},
[VariableInputEnum.textarea]: {
icon: 'core/app/variable/textarea',
title: i18nT('common:core.module.variable.textarea type'),
desc: i18nT('app:variable.textarea_type_desc')
},
[VariableInputEnum.select]: {
icon: 'core/app/variable/select',
title: i18nT('common:core.module.variable.select type'),
desc: ''
},
[VariableInputEnum.custom]: {
icon: 'core/app/variable/external',
title: i18nT('common:core.module.variable.Custom type'),
desc: i18nT('app:variable.select type_desc')
}
};
/* run time */
export enum RuntimeEdgeStatusEnum {
'waiting' = 'waiting',
'active' = 'active',
'skipped' = 'skipped'
}
export const VARIABLE_NODE_ID = 'VARIABLE_NODE_ID';
export const DYNAMIC_INPUT_REFERENCE_KEY = 'DYNAMIC_INPUT_REFERENCE_KEY';
// http node body content type
export enum ContentTypes {
none = 'none',
formData = 'form-data',
xWwwFormUrlencoded = 'x-www-form-urlencoded',
json = 'json',
xml = 'xml',
raw = 'raw-text'
}
import { WorkflowIOValueTypeEnum } from '../constants';
import { i18nT } from '../../../../web/i18n/utils';
export enum FlowNodeInputTypeEnum { // render ui
reference = 'reference', // reference to other node output
input = 'input', // one line input
numberInput = 'numberInput',
switch = 'switch', // true/false
select = 'select',
// editor
textarea = 'textarea',
JSONEditor = 'JSONEditor',
addInputParam = 'addInputParam', // params input
// special input
selectApp = 'selectApp',
customVariable = 'customVariable',
// ai model select
selectLLMModel = 'selectLLMModel',
settingLLMModel = 'settingLLMModel',
// dataset special input
selectDataset = 'selectDataset',
selectDatasetParamsModal = 'selectDatasetParamsModal',
settingDatasetQuotePrompt = 'settingDatasetQuotePrompt',
hidden = 'hidden',
custom = 'custom',
fileSelect = 'fileSelect'
}
export const FlowNodeInputMap: Record<
FlowNodeInputTypeEnum,
{
icon: string;
}
> = {
[FlowNodeInputTypeEnum.reference]: {
icon: 'core/workflow/inputType/reference'
},
[FlowNodeInputTypeEnum.input]: {
icon: 'core/workflow/inputType/input'
},
[FlowNodeInputTypeEnum.numberInput]: {
icon: 'core/workflow/inputType/numberInput'
},
[FlowNodeInputTypeEnum.select]: {
icon: 'core/workflow/inputType/option'
},
[FlowNodeInputTypeEnum.switch]: {
icon: 'core/workflow/inputType/switch'
},
[FlowNodeInputTypeEnum.textarea]: {
icon: 'core/workflow/inputType/textarea'
},
[FlowNodeInputTypeEnum.JSONEditor]: {
icon: 'core/workflow/inputType/jsonEditor'
},
[FlowNodeInputTypeEnum.addInputParam]: {
icon: 'core/workflow/inputType/dynamic'
},
[FlowNodeInputTypeEnum.selectApp]: {
icon: 'core/workflow/inputType/selectApp'
},
[FlowNodeInputTypeEnum.selectLLMModel]: {
icon: 'core/workflow/inputType/selectLLM'
},
[FlowNodeInputTypeEnum.settingLLMModel]: {
icon: 'core/workflow/inputType/selectLLM'
},
[FlowNodeInputTypeEnum.selectDataset]: {
icon: 'core/workflow/inputType/selectDataset'
},
[FlowNodeInputTypeEnum.selectDatasetParamsModal]: {
icon: 'core/workflow/inputType/selectDataset'
},
[FlowNodeInputTypeEnum.settingDatasetQuotePrompt]: {
icon: 'core/workflow/inputType/selectDataset'
},
[FlowNodeInputTypeEnum.hidden]: {
icon: 'core/workflow/inputType/select'
},
[FlowNodeInputTypeEnum.customVariable]: {
icon: 'core/workflow/inputType/customVariable'
},
[FlowNodeInputTypeEnum.custom]: {
icon: 'core/workflow/inputType/custom'
},
[FlowNodeInputTypeEnum.fileSelect]: {
icon: 'core/workflow/inputType/file'
}
};
export enum FlowNodeOutputTypeEnum {
hidden = 'hidden',
source = 'source',
static = 'static',
dynamic = 'dynamic'
}
export enum FlowNodeTypeEnum {
emptyNode = 'emptyNode',
systemConfig = 'userGuide',
pluginConfig = 'pluginConfig',
globalVariable = 'globalVariable',
workflowStart = 'workflowStart',
chatNode = 'chatNode',
datasetSearchNode = 'datasetSearchNode',
datasetConcatNode = 'datasetConcatNode',
answerNode = 'answerNode',
classifyQuestion = 'classifyQuestion',
contentExtract = 'contentExtract',
httpRequest468 = 'httpRequest468',
runApp = 'app',
appModule = 'appModule',
pluginModule = 'pluginModule',
pluginInput = 'pluginInput',
pluginOutput = 'pluginOutput',
queryExtension = 'cfr',
tools = 'tools',
stopTool = 'stopTool',
ifElseNode = 'ifElseNode',
variableUpdate = 'variableUpdate',
code = 'code',
textEditor = 'textEditor',
customFeedback = 'customFeedback',
readFiles = 'readFiles',
userSelect = 'userSelect',
formInput = 'formInput'
}
// node IO value type
export const FlowValueTypeMap = {
[WorkflowIOValueTypeEnum.string]: {
label: 'string',
value: WorkflowIOValueTypeEnum.string
},
[WorkflowIOValueTypeEnum.number]: {
label: 'number',
value: WorkflowIOValueTypeEnum.number
},
[WorkflowIOValueTypeEnum.boolean]: {
label: 'boolean',
value: WorkflowIOValueTypeEnum.boolean
},
[WorkflowIOValueTypeEnum.object]: {
label: 'object',
value: WorkflowIOValueTypeEnum.object
},
[WorkflowIOValueTypeEnum.arrayString]: {
label: 'array<string>',
value: WorkflowIOValueTypeEnum.arrayString
},
[WorkflowIOValueTypeEnum.arrayNumber]: {
label: 'array<number>',
value: WorkflowIOValueTypeEnum.arrayNumber
},
[WorkflowIOValueTypeEnum.arrayBoolean]: {
label: 'array<boolean>',
value: WorkflowIOValueTypeEnum.arrayBoolean
},
[WorkflowIOValueTypeEnum.arrayObject]: {
label: 'array<object>',
value: WorkflowIOValueTypeEnum.arrayObject
},
[WorkflowIOValueTypeEnum.arrayAny]: {
label: 'Array',
value: WorkflowIOValueTypeEnum.arrayAny
},
[WorkflowIOValueTypeEnum.any]: {
label: 'any',
value: WorkflowIOValueTypeEnum.any
},
[WorkflowIOValueTypeEnum.chatHistory]: {
label: i18nT('common:core.chat.History'),
value: WorkflowIOValueTypeEnum.chatHistory
},
[WorkflowIOValueTypeEnum.datasetQuote]: {
label: i18nT('common:core.workflow.Dataset quote'),
value: WorkflowIOValueTypeEnum.datasetQuote
},
[WorkflowIOValueTypeEnum.selectApp]: {
label: i18nT('common:plugin.App'),
value: WorkflowIOValueTypeEnum.selectApp
},
[WorkflowIOValueTypeEnum.selectDataset]: {
label: i18nT('common:core.chat.Select dataset'),
value: WorkflowIOValueTypeEnum.selectDataset
},
[WorkflowIOValueTypeEnum.dynamic]: {
label: i18nT('common:core.workflow.dynamic_input'),
value: WorkflowIOValueTypeEnum.dynamic
}
};
export const EDGE_TYPE = 'default';
export const defaultNodeVersion = '481';
export const chatHistoryValueDesc = `{
obj: System | Human | AI;
value: string;
}[]`;
export const datasetQuoteValueDesc = `{
id: string;
datasetId: string;
collectionId: string;
sourceName: string;
sourceId?: string;
q: string;
a: string
}[]`;
import { FlowNodeInputTypeEnum } from '../node/constant';
export enum SseResponseEventEnum {
error = 'error',
answer = 'answer', // animation stream
fastAnswer = 'fastAnswer', // direct answer text, not animation
flowNodeStatus = 'flowNodeStatus', // update node status
toolCall = 'toolCall', // tool start
toolParams = 'toolParams', // tool params return
toolResponse = 'toolResponse', // tool response return
flowResponses = 'flowResponses', // sse response request
updateVariables = 'updateVariables',
interactive = 'interactive' // user select
}
export enum DispatchNodeResponseKeyEnum {
skipHandleId = 'skipHandleId', // skip handle id
nodeResponse = 'responseData', // run node response
nodeDispatchUsages = 'nodeDispatchUsages', // the node bill.
childrenResponses = 'childrenResponses', // Some nodes make recursive calls that need to be returned
toolResponses = 'toolResponses', // The result is passed back to the tool node for use
assistantResponses = 'assistantResponses', // assistant response
rewriteHistories = 'rewriteHistories', // If have the response, workflow histories will be rewrite
interactive = 'INTERACTIVE', // is interactive
runTimes = 'runTimes', // run times
newVariables = 'newVariables' // new variables
}
export const needReplaceReferenceInputTypeList = [
FlowNodeInputTypeEnum.reference,
FlowNodeInputTypeEnum.settingDatasetQuotePrompt,
FlowNodeInputTypeEnum.addInputParam,
FlowNodeInputTypeEnum.custom
] as string[];
export type SECTOR_CODE_ENUM = "T"| "G"| "S"| "Y"|"X"| "P"| "Z"
export interface SectorSchema {
_id: string;
sectorName: string;
sectorCode: SECTOR_CODE_ENUM;
avatar: string;
status: 'active' | 'disable';
createTime: Date;
updateTime: Date;
adminList: { username: string; userId: string; trueName: string }[];
}
import { useMediaQuery } from '@chakra-ui/react';
import { useEffect, useState } from 'react';
export const useSystem = () => {
const [isPc] = useMediaQuery('(min-width: 900px)');
return { isPc };
};
import { useToast as uToast, UseToastOptions } from '@chakra-ui/react';
import { useCallback, useMemo } from 'react';
export const useToast = (props?: UseToastOptions) => {
const toast = uToast({
position: 'top',
duration: 2000,
containerStyle: {
fontSize: 'sm'
},
...props
});
const myToast = useCallback(
(options?: UseToastOptions) => {
if (options?.title || options?.description) {
toast(options);
}
},
[props]
);
return {
toast: myToast
};
};
import Image from "next/image";
export default function Home() {
return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<div className="z-10 w-full max-w-5xl items-center justify-between font-mono text-sm lg:flex">
<p className="fixed left-0 top-0 flex w-full justify-center border-b border-gray-300 bg-gradient-to-b from-zinc-200 pb-6 pt-8 backdrop-blur-2xl dark:border-neutral-800 dark:bg-zinc-800/30 dark:from-inherit lg:static lg:w-auto lg:rounded-xl lg:border lg:bg-gray-200 lg:p-4 lg:dark:bg-zinc-800/30">
Get started by editing&nbsp;
<code className="font-mono font-bold">src/app/page.tsx</code>
</p>
<div className="fixed bottom-0 left-0 flex h-48 w-full items-end justify-center bg-gradient-to-t from-white via-white dark:from-black dark:via-black lg:static lg:size-auto lg:bg-none">
<a
className="pointer-events-none flex place-items-center gap-2 p-8 lg:pointer-events-auto lg:p-0"
href="https://vercel.com?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
By{" "}
<Image
src="/vercel.svg"
alt="Vercel Logo"
className="dark:invert"
width={100}
height={24}
priority
/>
</a>
</div>
</div>
<div className="relative z-[-1] flex place-items-center before:absolute before:h-[300px] before:w-full before:-translate-x-1/2 before:rounded-full before:bg-gradient-radial before:from-white before:to-transparent before:blur-2xl before:content-[''] after:absolute after:-z-20 after:h-[180px] after:w-full after:translate-x-1/3 after:bg-gradient-conic after:from-sky-200 after:via-blue-200 after:blur-2xl after:content-[''] before:dark:bg-gradient-to-br before:dark:from-transparent before:dark:to-blue-700 before:dark:opacity-10 after:dark:from-sky-900 after:dark:via-[#0141ff] after:dark:opacity-40 sm:before:w-[480px] sm:after:w-[240px] before:lg:h-[360px]">
<Image
className="relative dark:drop-shadow-[0_0_0.3rem_#ffffff70] dark:invert"
src="/next.svg"
alt="Next.js Logo"
width={180}
height={37}
priority
/>
</div>
<div className="mb-32 grid text-center lg:mb-0 lg:w-full lg:max-w-5xl lg:grid-cols-4 lg:text-left">
<a
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
target="_blank"
rel="noopener noreferrer"
>
<h2 className="mb-3 text-2xl font-semibold">
Docs{" "}
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
-&gt;
</span>
</h2>
<p className="m-0 max-w-[30ch] text-sm opacity-50">
Find in-depth information about Next.js features and API.
</p>
</a>
<a
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
target="_blank"
rel="noopener noreferrer"
>
<h2 className="mb-3 text-2xl font-semibold">
Learn{" "}
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
-&gt;
</span>
</h2>
<p className="m-0 max-w-[30ch] text-sm opacity-50">
Learn about Next.js in an interactive course with&nbsp;quizzes!
</p>
</a>
<a
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
target="_blank"
rel="noopener noreferrer"
>
<h2 className="mb-3 text-2xl font-semibold">
Templates{" "}
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
-&gt;
</span>
</h2>
<p className="m-0 max-w-[30ch] text-sm opacity-50">
Explore starter templates for Next.js.
</p>
</a>
<a
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
target="_blank"
rel="noopener noreferrer"
>
<h2 className="mb-3 text-2xl font-semibold">
Deploy{" "}
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
-&gt;
</span>
</h2>
<p className="m-0 max-w-[30ch] text-balance text-sm opacity-50">
Instantly deploy your Next.js site to a shareable URL with Vercel.
</p>
</a>
</div>
</main>
);
}
import { PluginTemplateType } from '@eagic/global/core/plugin/type.d';
import { SystemPluginTemplateItemType } from '@eagic/global/core/workflow/type';
export type SystemPluginResponseType = Promise<Record<string, any>>;
declare global {
var systemPlugins: SystemPluginTemplateItemType[];
var systemPluginCb: Record<string, (e: any) => SystemPluginResponseType>;
}
// import { addLog } from '../../common/system/log';
import mongoose, { Model } from 'mongoose';
export default mongoose;
export * from 'mongoose';
export const connectionMongo = (() => {
if (!global.mongodb) {
global.mongodb = mongoose;
}
return global.mongodb;
})();
const addCommonMiddleware = (schema: mongoose.Schema) => {
const operations = [
/^find/,
'save',
'create',
/^update/,
/^delete/,
'aggregate',
'count',
'countDocuments',
'estimatedDocumentCount',
'distinct',
'insertMany'
];
operations.forEach((op: any) => {
schema.pre(op, function (this: any, next) {
this._startTime = Date.now();
this._query = this.getQuery ? this.getQuery() : null;
next();
});
schema.post(op, function (this: any, result: any, next) {
if (this._startTime) {
const duration = Date.now() - this._startTime;
const warnLogData = {
query: this._query,
op,
duration
};
if (duration > 1000) {
// addLog.warn(`Slow operation ${duration}ms`, warnLogData);
}
}
next();
});
});
return schema;
};
export const getMongoModel = <T>(name: string, schema: mongoose.Schema) => {
if (connectionMongo.models[name]) return connectionMongo.models[name] as Model<T>;
console.log('Load model======', name);
addCommonMiddleware(schema);
const model = connectionMongo.model<T>(name, schema);
if (process.env.SYNC_INDEX !== '0') {
try {
model.syncIndexes({ background: true });
} catch (error) {
// addLog.error('Create index error', error);
}
}
return model;
};
export const ReadPreference = connectionMongo.mongo?.ReadPreference;
import { delay } from '@src/utils/utils';
// import { addLog } from '../system/log';
import { connectionMongo } from './index';
import type { Mongoose } from 'mongoose';
const maxConnecting = Math.max(30, Number(process.env.DB_MAX_LINK || 20));
/**
* connect MongoDB and init data
*/
export async function connectMongo(): Promise<Mongoose> {
/* Connecting, connected will return */
if (connectionMongo.connection.readyState !== 0) {
return connectionMongo;
}
console.log('mongo start connect');
try {
connectionMongo.set('strictQuery', true);
connectionMongo.connection.on('error', async (error) => {
console.log('mongo error', error);
await connectionMongo.disconnect();
await delay(1000);
connectMongo();
});
connectionMongo.connection.on('disconnected', () => {
console.log('mongo disconnected');
});
console.log(process.env.MONGODB_URI);
await connectionMongo.connect(process.env.MONGODB_URI as string, {
bufferCommands: true,
maxConnecting: maxConnecting,
maxPoolSize: maxConnecting,
minPoolSize: 20,
connectTimeoutMS: 60000,
waitQueueTimeoutMS: 60000,
socketTimeoutMS: 60000,
maxIdleTimeMS: 300000,
retryWrites: true,
retryReads: true
// readPreference: 'secondaryPreferred',
// readConcern: { level: 'local' },
// writeConcern: { w: 'majority', j: true }
});
console.log('mongo connected');
return connectionMongo;
} catch (error) {
// addLog.error('mongo connect error', error);
await connectionMongo.disconnect();
await delay(1000);
return connectMongo();
}
}
// import { addLog } from '../system/log';
import { connectionMongo, ClientSession } from './index';
const timeout = 60000;
export const mongoSessionRun = async <T = unknown>(fn: (session: ClientSession) => Promise<T>) => {
const session = await connectionMongo.startSession();
try {
session.startTransaction({
maxCommitTimeMS: timeout
});
const result = await fn(session);
await session.commitTransaction();
return result as T;
} catch (error) {
if (!session.transaction.isCommitted) {
await session.abortTransaction();
} else {
// addLog.warn('Un catch mongo session error', { error });
}
return Promise.reject(error);
} finally {
await session.endSession();
}
};
import type { Mongoose } from 'mongoose';
import type { Logger } from 'winston';
declare global {
var mongodb: Mongoose | undefined;
}
import { ReadPreference } from './index';
export const readFromSecondary = {
readPreference: ReadPreference.SECONDARY_PREFERRED, // primary | primaryPreferred | secondary | secondaryPreferred | nearest
readConcern: 'local' as any // local | majority | linearizable | available
};
import type { NextApiResponse } from 'next';
import { SseResponseEventEnum } from '@eagic/global/core/workflow/runtime/constants';
import { proxyError, ERROR_RESPONSE, ERROR_ENUM } from '@eagic/global/common/error/errorCode';
import { addLog } from '../system/log';
import { replaceSensitiveText } from '@eagic/global/common/string/tools';
export interface ResponseType<T = any> {
code: number;
message: string;
data: T;
}
export const jsonRes = <T = any>(
res: NextApiResponse,
props?: {
code?: number;
message?: string;
data?: T;
error?: any;
url?: string;
}
) => {
const { code = 200, message = '', data = null, error, url } = props || {};
const errResponseKey = typeof error === 'string' ? error : error?.message;
// Specified error
if (ERROR_RESPONSE[errResponseKey]) {
// login is expired
if (errResponseKey === ERROR_ENUM.unAuthorization) {
// clearCookie(res);
}
return res.json(ERROR_RESPONSE[errResponseKey]);
}
// another error
let msg = '';
if ((code < 200 || code >= 400) && !message) {
msg = error?.response?.statusText || error?.message || '请求错误';
if (typeof error === 'string') {
msg = error;
} else if (proxyError[error?.code]) {
msg = '网络连接异常';
} else if (error?.response?.data?.error?.message) {
msg = error?.response?.data?.error?.message;
} else if (error?.error?.message) {
msg = error?.error?.message;
}
addLog.error(`Api response error: ${url}, ${msg}`, error);
}
res.status(code).json({
code,
statusText: '',
message: replaceSensitiveText(message || msg),
data: data !== undefined ? data : null
});
};
export const sseErrRes = (res: NextApiResponse, error: any) => {
const errResponseKey = typeof error === 'string' ? error : error?.message;
// Specified error
if (ERROR_RESPONSE[errResponseKey]) {
// login is expired
if (errResponseKey === ERROR_ENUM.unAuthorization) {
// clearCookie(res);
}
return responseWrite({
res,
event: SseResponseEventEnum.error,
data: JSON.stringify(ERROR_RESPONSE[errResponseKey])
});
}
let msg = error?.response?.statusText || error?.message || '请求错误';
if (typeof error === 'string') {
msg = error;
} else if (proxyError[error?.code]) {
msg = '网络连接异常';
} else if (error?.response?.data?.error?.message) {
msg = error?.response?.data?.error?.message;
} else if (error?.error?.message) {
msg = `${error?.error?.code} ${error?.error?.message}`;
}
addLog.error(`sse error: ${msg}`, error);
responseWrite({
res,
event: SseResponseEventEnum.error,
data: JSON.stringify({ message: replaceSensitiveText(msg) })
});
};
export function responseWriteController({
res,
readStream
}: {
res: NextApiResponse;
readStream: any;
}) {
res.on('drain', () => {
readStream?.resume?.();
});
return (text: string | Buffer) => {
const writeResult = res.write(text);
if (!writeResult) {
readStream?.pause?.();
}
};
}
export function responseWrite({
res,
write,
event,
data
}: {
res?: NextApiResponse;
write?: (text: string) => void;
event?: string;
data: string;
}) {
const Write = write || res?.write;
if (!Write) return;
event && Write(`event: ${event}\n`);
Write(`data: ${data}\n\n`);
}
export const responseWriteNodeStatus = ({
res,
status = 'running',
name
}: {
res?: NextApiResponse;
status?: 'running';
name: string;
}) => {
responseWrite({
res,
event: SseResponseEventEnum.flowNodeStatus,
data: JSON.stringify({
status,
name
})
});
};
import dayjs from 'dayjs';
import chalk from 'chalk';
import { LogLevelEnum } from './log/constant';
import { connectionMongo } from '../mongo/index';
import { getMongoLog } from './log/schema';
const logMap = {
[LogLevelEnum.debug]: {
levelLog: chalk.green('[Debug]')
},
[LogLevelEnum.info]: {
levelLog: chalk.blue('[Info]')
},
[LogLevelEnum.warn]: {
levelLog: chalk.yellow('[Warn]')
},
[LogLevelEnum.error]: {
levelLog: chalk.red('[Error]')
}
};
const envLogLevelMap: Record<string, number> = {
debug: LogLevelEnum.debug,
info: LogLevelEnum.info,
warn: LogLevelEnum.warn,
error: LogLevelEnum.error
};
const { LOG_LEVEL, STORE_LOG_LEVEL } = (() => {
const LOG_LEVEL = (process.env.LOG_LEVEL || 'info').toLocaleLowerCase();
const STORE_LOG_LEVEL = (process.env.STORE_LOG_LEVEL || '').toLocaleLowerCase();
return {
LOG_LEVEL: envLogLevelMap[LOG_LEVEL] ?? LogLevelEnum.info,
STORE_LOG_LEVEL: envLogLevelMap[STORE_LOG_LEVEL] ?? 99
};
})();
/* add logger */
export const addLog = {
log(level: LogLevelEnum, msg: string, obj: Record<string, any> = {}) {
if (level < LOG_LEVEL) return;
const stringifyObj = JSON.stringify(obj);
const isEmpty = Object.keys(obj).length === 0;
console.log(
`${logMap[level].levelLog} ${dayjs().format('YYYY-MM-DD HH:mm:ss')} ${msg} ${
level !== LogLevelEnum.error && !isEmpty ? stringifyObj : ''
}`
);
level === LogLevelEnum.error && console.error(obj);
// store
if (level >= STORE_LOG_LEVEL && connectionMongo.connection.readyState === 1) {
// store log
getMongoLog().create({
text: msg,
level,
metadata: obj
});
}
},
debug(msg: string, obj?: Record<string, any>) {
this.log(LogLevelEnum.debug, msg, obj);
},
info(msg: string, obj?: Record<string, any>) {
this.log(LogLevelEnum.info, msg, obj);
},
warn(msg: string, obj?: Record<string, any>) {
this.log(LogLevelEnum.warn, msg, obj);
},
error(msg: string, error?: any) {
this.log(LogLevelEnum.error, msg, {
message: error?.message || error,
stack: error?.stack,
...(error?.config && {
config: {
headers: error.config.headers,
url: error.config.url,
data: error.config.data
}
}),
...(error?.response && {
response: {
status: error.response.status,
statusText: error.response.statusText
}
})
});
}
};
export enum LogLevelEnum {
debug = 0,
info = 1,
warn = 2,
error = 3
}
export enum LogSignEnum {
slowOperation = 'slowOperation'
}
import { getMongoModel, Schema } from '../../../common/mongo';
import { SystemLogType } from './type';
import { LogLevelEnum } from './constant';
export const LogCollectionName = 'system_logs';
export const getMongoLog = () => {
const SystemLogSchema = new Schema({
text: {
type: String,
required: true
},
level: {
type: String,
required: true,
enum: Object.values(LogLevelEnum)
},
time: {
type: Date,
default: () => new Date()
},
metadata: Object
});
SystemLogSchema.index({ time: 1 }, { expires: '15d' });
SystemLogSchema.index({ level: 1 });
return getMongoModel<SystemLogType>(LogCollectionName, SystemLogSchema);
};
import { LogLevelEnum, LogSignEnum } from './constant';
export type SystemLogType = {
_id: string;
text: string;
level: LogLevelEnum;
time: Date;
metadata?: Record<string, any>;
};
// import type { UserModelSchema } from '@eagic/global/support/user/type';
import OpenAI from 'openai';
export const openaiBaseUrl = process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1';
import type { Agent } from 'http';
export const getAIApi = () => {
const baseUrl = process.env.ONEAPI_URL || openaiBaseUrl;
const apiKey = process.env.CHAT_API_KEY || '';
console.log(baseUrl);
console.log(apiKey);
return new OpenAI({
baseURL: baseUrl,
apiKey,
httpAgent: global.httpsAgent,
timeout:30000 ,
maxRetries: 2
});
};
import type { Agent } from 'http';
declare global {
var httpsAgent: Agent;
}
import axios, {
Method,
InternalAxiosRequestConfig,
AxiosResponse,
AxiosProgressEvent
} from 'axios';
interface ConfigType {
headers?: { [key: string]: string };
timeout?: number;
onUploadProgress?: (progressEvent: AxiosProgressEvent) => void;
cancelToken?: AbortController;
maxQuantity?: number; // The maximum number of simultaneous requests, usually used to cancel old requests
withCredentials?: boolean;
}
interface ResponseDataType {
code: number;
message: string;
data: any;
}
const maxQuantityMap: Record<
string,
{
amount: number;
sign: AbortController;
}
> = {};
function checkMaxQuantity({ url, maxQuantity }: { url: string; maxQuantity?: number }) {
if (maxQuantity) {
const item = maxQuantityMap[url];
const controller = new AbortController();
if (item) {
if (item.amount >= maxQuantity) {
item.sign?.abort?.();
maxQuantityMap[url] = {
amount: 1,
sign: controller
};
} else {
item.amount++;
}
} else {
maxQuantityMap[url] = {
amount: 1,
sign: controller
};
}
return controller;
}
}
function requestFinish({ url }: { url: string }) {
const item = maxQuantityMap[url];
if (item) {
item.amount--;
if (item.amount <= 0) {
delete maxQuantityMap[url];
}
}
}
/**
* 请求开始
*/
function startInterceptors(config: InternalAxiosRequestConfig): InternalAxiosRequestConfig {
if (config.headers) {
}
return config;
}
/**
* 请求成功,检查请求头
*/
function responseSuccess(response: AxiosResponse<ResponseDataType>) {
return response;
}
/**
* 响应数据检查
*/
function checkRes(data: ResponseDataType) {
if (data === undefined) {
console.log('error->', data, 'data is empty');
return Promise.reject('服务器异常');
} else if (data.code < 200 || data.code >= 400) {
return Promise.reject(data);
}
return data.data;
}
/**
* 响应错误
*/
function responseError(err: any) {
console.log('error->', '请求错误', err);
if (!err) {
return Promise.reject({ message: '未知错误' });
}
if (typeof err === 'string') {
return Promise.reject({ message: err });
}
if (err?.response?.data) {
return Promise.reject(err?.response?.data);
}
return Promise.reject(err);
}
/* 创建请求实例 */
const instance = axios.create({
timeout: 60000, // 超时时间
headers: {
'content-type': 'application/json'
}
});
/* 请求拦截 */
instance.interceptors.request.use(startInterceptors, (err) => Promise.reject(err));
/* 响应拦截 */
instance.interceptors.response.use(responseSuccess, (err) => Promise.reject(err));
function request(
url: string,
data: any,
{ cancelToken, maxQuantity, withCredentials, ...config }: ConfigType,
method: Method
): any {
/* 去空 */
for (const key in data) {
if (data[key] === undefined) {
delete data[key];
}
}
const controller = checkMaxQuantity({ url, maxQuantity });
return instance
.request({
baseURL: '/api',
url,
method,
data: ['POST', 'PUT'].includes(method) ? data : null,
params: !['POST', 'PUT'].includes(method) ? data : null,
signal: cancelToken?.signal ?? controller?.signal,
withCredentials,
...config // 用户自定义配置,可以覆盖前面的配置
})
.then((res) => checkRes(res.data))
.catch((err) => responseError(err))
.finally(() => requestFinish({ url }));
}
/**
* api请求方式
* @param {String} url
* @param {Any} params
* @param {Object} config
* @returns
*/
export function GET<T = undefined>(url: string, params = {}, config: ConfigType = {}): Promise<T> {
return request(url, params, config, 'GET');
}
export function POST<T = undefined>(url: string, data = {}, config: ConfigType = {}): Promise<T> {
return request(url, data, config, 'POST');
}
export function PUT<T = undefined>(url: string, data = {}, config: ConfigType = {}): Promise<T> {
return request(url, data, config, 'PUT');
}
export function DELETE<T = undefined>(url: string, data = {}, config: ConfigType = {}): Promise<T> {
return request(url, data, config, 'DELETE');
}
import React, { useRef, useCallback } from 'react';
import { Box } from '@chakra-ui/react';
import { useToast } from '@/web/common/hooks/useToast';
export const useSelectFile = (props?: {
fileType?: string;
multiple?: boolean;
maxCount?: number;
}) => {
const { fileType = '*', multiple = false, maxCount = 10 } = props || {};
const { toast } = useToast();
const SelectFileDom = useRef<HTMLInputElement>(null);
const openSign = useRef<any>();
const File = useCallback(
({ onSelect }: { onSelect: (e: File[], sign?: any) => void }) => (
<Box position={'absolute'} w={0} h={0} overflow={'hidden'}>
<input
ref={SelectFileDom}
type="file"
accept={fileType}
multiple={multiple}
onChange={(e) => {
const files = e.target.files;
if (!files || files?.length === 0) return;
let fileList = Array.from(files);
if (fileList.length > maxCount) {
toast({
status: 'warning',
title:'select_file_amount_limit'
});
fileList = fileList.slice(0, maxCount);
}
onSelect(fileList, openSign.current);
e.target.value = '';
}}
/>
</Box>
),
[fileType, maxCount, multiple, toast]
);
const onOpen = useCallback((sign?: any) => {
openSign.current = sign;
SelectFileDom.current && SelectFileDom.current.click();
}, []);
return {
File,
onOpen
};
};
import { useToast as uToast, UseToastOptions } from '@chakra-ui/react';
import { useCallback, useMemo } from 'react';
export const useToast = (props?: UseToastOptions) => {
const toast = uToast({
position: 'top',
duration: 2000,
containerStyle: {
fontSize: 'sm'
},
...props
});
const myToast = useCallback(
(options?: UseToastOptions) => {
if (options?.title || options?.description) {
toast(options);
}
},
[props]
);
return {
toast: myToast
};
};
import { diffDetailType } from '@src/pages/api/contractCompare/diffHandler';
// 转义正则表达式的关键字
export const escapeRegExp = (input: string) => {
// 列出正则表达式的关键字
const regexKeywords = /[-/\\^$*+?.()|[\]{}]/g;
// 使用replace方法来转义关键字
return input.replace(regexKeywords, '\\$&');
};
// 分割字符串,每n个一组
export const splitString = (inputString: string, chunkSize: number) => {
const result = [];
for (let i = 0; i < inputString.length; i += chunkSize) {
result.push(inputString.slice(i, i + chunkSize));
}
return result;
};
interface returnObj {
contractHtml: string;
diffCount: number;
}
interface DiffObj {
added?: boolean;
removed?: boolean;
count: number;
value: string;
lastIndex: number;
strLastIndexList: number[];
}
interface FragmentObj {
first: string;
table: string;
last: string;
}
// 拼接多页内容,用--PAGEEND--作为分割符
const concatPage = (list: HTMLElement[]) => {
const strList = list.map((item) => item.innerHTML);
return strList.join('--PAGEEND--');
};
// 比较新老内容,处理差异
export const diffDocument = (newDocxNode: Element, diffDetail: diffDetailType): returnObj => {
let addNum = 0;
let delNum = 0;
let cycleInedx = 1;
const handleDiff = (newHtml: string, diffList: any) => {
// 区分新增和删除,并获取每个字的位置
let reg = new RegExp('', 'g');
let lastIndexOld = 0;
// 记录未改变的字符数/新增字符数/删除字符数
// let newHtml = newNodeStr;
const regex = /[^ \t\r\n\v\f]/;
for (const item of diffList) {
const strArr = item.value.split(''); // 拆分成单个字符
const strLastIndexList: number[] = []; // 每个字符的lastIndex
if (item.removed) {
if (!regex.test(item.value)) {
continue;
}
newHtml =
newHtml.slice(0, lastIndexOld) +
`<s id='diff-${cycleInedx}' style="background-color:#fac5cd;">${item.value}</s>` +
newHtml.slice(lastIndexOld);
//标签字符串长度 + 移除项的长度 + 循环索引长度
lastIndexOld += 52 + item.value.length + cycleInedx.toString().length;
delNum += item.value.length;
cycleInedx += 1;
continue;
}
// 获取每个字的位置
strArr.forEach((s: string) => {
reg = new RegExp(`(?<!<[^>]*)(${escapeRegExp(s)})(?![^<]*>)`, 'gi');
reg.lastIndex = lastIndexOld; // 在上次正则位置基础上,继续匹配
reg.exec(newHtml);
if (reg.lastIndex != 0) {
lastIndexOld = reg.lastIndex; // 记录本次位置
strLastIndexList.push(reg.lastIndex - 1);
}
});
if (item.added && regex.test(item.value)) {
const len = strLastIndexList.length;
newHtml =
newHtml.slice(0, strLastIndexList[0]) +
`<span id='diff-${cycleInedx}' style="background-color:#c7f0d2;">${item.value}</span>` +
newHtml.slice(strLastIndexList[strLastIndexList.length - 1] + 1);
//首个字符索引位置 - 1 + 新增字符串长度 + 循环索引长度 + 标签字符串长度
lastIndexOld = strLastIndexList[0] - 1 + len + cycleInedx.toString().length + 52;
cycleInedx += 1;
addNum += 1;
}
}
return newHtml;
};
(newDocxNode.querySelector('.vue-office-docx') as HTMLElement).style.height = 'auto';
const docxWrapper = newDocxNode.querySelector('.docx-wrapper') as HTMLElement;
docxWrapper.style.backgroundColor = '#fff';
docxWrapper.style.padding = '0';
// 使用 forEach 遍历并设置样式
docxWrapper.querySelectorAll('.docx').forEach((docx) => {
(docx as HTMLElement).style.width = '100%';
(docx as HTMLElement).style.marginBottom = '0';
(docx as HTMLElement).style.position = 'static';
});
// 使用 forEach 遍历并设置样式
docxWrapper.querySelectorAll('footer svg').forEach((svg) => {
(svg as HTMLElement).style.height = '1in';
});
const newNodeList: HTMLElement[] = Array.from(
newDocxNode.querySelectorAll('.docx-wrapper > .docx > article')
);
if (newNodeList.length === 0) {
return { contractHtml: '', diffCount: 0 };
}
let newNodeStr = concatPage(newNodeList);
const reg1 = /<span\s+style="([^"]+)">/g;
// 去除文档原本的字体颜色和背景色
const deleteColor = (match: any, styleContent: string) => {
// 分割 style 属性中的各个 CSS 规则
const styles = styleContent.split(';').map((style) => style.trim());
// 过滤掉 background-color 和 color 属性
const filteredStyles = styles.filter(
(style) => !style.startsWith('background-color:') && !style.startsWith('color:')
);
// 将过滤后的样式重新组合成一个字符串
const newStyleContent = filteredStyles.join('; ');
// 如果没有剩余样式,返回没有 style 属性的 <span> 标签
if (newStyleContent) {
return `<span style="${newStyleContent}">`;
} else {
return '<span>';
}
};
newNodeStr = newNodeStr.replace(reg1, deleteColor);
const newFragments: FragmentObj = {
first: '',
table: '',
last: ''
};
const tableReg = /<table[^>]*>.*?<\/table>/g;
tableReg.lastIndex = 0;
const matchNews = newNodeStr.match(tableReg);
if (matchNews) {
let matchNew = matchNews.filter(
(match) => match.includes('产品名称') && match.includes('金额')
)[0];
const tableIndex = newNodeStr.indexOf(matchNew);
newFragments.first = newNodeStr.slice(0, tableIndex);
newFragments.table = matchNew;
newFragments.last = newNodeStr.slice(tableIndex + matchNew.length);
}
newFragments.first = handleDiff(newFragments.first, diffDetail.fristFragmentDiff);
newFragments.table = handleDiff(newFragments.table, diffDetail.tableDiff);
newFragments.last = handleDiff(newFragments.last, diffDetail.lastFragmentDiff);
if (diffDetail.outherDiff.deliveryTimeDiff) {
let diffArr: number[] = [];
// console.log("diffArr====================", diffArr);
// console.log(' reg.exec(handledStr) ====================', handledStr);
// let reg = new RegExp(/于(<[^>]*>)*[0-9]{4}.*?由/);
let reg = new RegExp(/不(<[^>]*>)*晚(<[^>]*>)*于.*?由/);
reg.lastIndex = 0;
reg.exec(newFragments.last);
// let low = 0;
// let high = diffArr.length - 1;
// let result = -1; // 如果没有找到,返回-1
// // 找到应该插入的位置
// while (low <= high) {
// const mid = low + Math.floor((high - low) / 2);
// if (diffArr[mid] > reg.lastIndex) {
// result = mid;
// high = mid - 1; // 继续在左半部分查找,看是否有更小的数
// } else {
// low = mid + 1; // 继续在右半部分查找
// }
// }
// let diffTemp = result
// while (diffTemp < diffArr.length - 1) {
// // 其他差异后移
// reg = new RegExp(`diff-${diffTemp}`);
// handledStr.replace(reg, `diff-${diffTemp + 1}`)
// }
newFragments.last = newFragments.last.replace(reg, (word) => {
word = word.replace(/<[^>]*>/g, '');
return `不晚于<span id='diff-${cycleInedx}' style="background-color:yellow;">${word.substring(
3,
word.length - 1
)}</span>由`;
});
}
let handledStr = Object.values(newFragments).join('');
// 根据分割符重新分割每页
const newHtmlList = handledStr.split('--PAGEEND--');
newNodeList.forEach((node, i) => {
node.innerHTML = newHtmlList[i];
});
return {
contractHtml: newHtmlList.join(''),
diffCount: cycleInedx - 1
};
};
export const modifyDateStyle = (setAgain: Function) => {
const newDocx = document.querySelector('.new-docx');
if (newDocx) {
setAgain(true);
const newNodeList: HTMLElement[] = Array.from(
newDocx.shadowRoot?.querySelectorAll('.docx-wrapper > .docx > article') || []
);
if (newNodeList.length === 0) {
return 1;
}
// 拼接多页内容,用--PAGEEND--作为分割符
let newNodeStr = concatPage(newNodeList);
// let newContentStr = newNodeStr.replace(/<[^>]*>/g, "")
// let reg = new RegExp(/预计发货时间(<[^>]*>)*.*?金额/);
// newNodeStr = newNodeStr.replace(reg, (word) => {
// word = word.replace(/<[^>]*>/g, "");
// return `于<span style="background-color:yellow;">${word.substring(1, word.length - 1)}</span>由`
// })
let reg = new RegExp(/于(<[^>]*>)*[0-9]{4}.*?由/);
newNodeStr = newNodeStr.replace(reg, (word) => {
word = word.replace(/<[^>]*>/g, '');
return `于<span style="background-color:yellow;">${word.substring(
1,
word.length - 1
)}</span>由`;
});
const newHtmlList = newNodeStr.split('--PAGEEND--');
newNodeList.forEach((node, i) => {
node.innerHTML = newHtmlList[i];
});
}
};
export const editFocusDiffStyle = (pre: number, next: number) => {
const newDocx = document.querySelector('.new-docx');
if (newDocx) {
let doc = newDocx.shadowRoot?.getElementById(`diff-${next}`);
doc && doc.classList.add('diff-focus');
if (pre !== 0) {
let preDoc = newDocx.shadowRoot?.getElementById(`diff-${pre}`);
preDoc && preDoc.classList.remove('diff-focus');
}
doc && doc.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
};
// const getTable = async (content: string) => {
// return
// }
import React from 'react';
import { Box, Flex, Image } from '@chakra-ui/react';
import type { ImageProps } from '@chakra-ui/react';
// import { LOGO_ICON } from '@eagic/global/common/system/constants';
import MyIcon from '../Icon';
import { iconPaths } from '../Icon/constants';
const LOGO_ICON = `/icon/logo2.png`;
const Avatar = ({ w = '30px', src, ...props }: ImageProps) => {
// @ts-ignore
const isIcon = !!iconPaths[src as any];
return isIcon ? (
<Box display={'inline-flex'} {...props}>
<MyIcon name={src as any} w={w} borderRadius={props.borderRadius} />
</Box>
) : (
<Image
fallbackSrc={LOGO_ICON}
fallbackStrategy={'onError'}
objectFit={'contain'}
alt=""
w={w}
h={w}
src={src || LOGO_ICON}
{...props}
/>
);
};
export default Avatar;
// @ts-nocheck
export const iconPaths = {
addSvg: () => import('./icons/core/chat/add.svg'),
deleteSvg: () => import('./icons/core/chat/delete.svg')
};
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" rx="20" fill="#D9F7D0"/>
<path d="M20 31.0944C20.373 31.0944 20.7306 30.9462 20.9944 30.6825C21.2581 30.4188 21.4062 30.0611 21.4062 29.6881V10.3119C21.4062 9.93893 21.2581 9.58124 20.9944 9.31752C20.7306 9.0538 20.373 8.90564 20 8.90564C19.627 8.90564 19.2694 9.0538 19.0056 9.31752C18.7419 9.58124 18.5938 9.93893 18.5938 10.3119V29.6881C18.5938 30.0611 18.7419 30.4188 19.0056 30.6825C19.2694 30.9462 19.627 31.0944 20 31.0944Z" fill="#6ADE46"/>
<path d="M10.312 21.4062H29.6882C30.0612 21.4062 30.4189 21.2581 30.6826 20.9944C30.9463 20.7306 31.0945 20.373 31.0945 20C31.0945 19.627 30.9463 19.2694 30.6826 19.0056C30.4189 18.7419 30.0612 18.5938 29.6882 18.5938H10.312C9.93902 18.5938 9.58134 18.7419 9.31761 19.0056C9.05389 19.2694 8.90574 19.627 8.90574 20C8.90574 20.373 9.05389 20.7306 9.31761 20.9944C9.58134 21.2581 9.93902 21.4062 10.312 21.4062Z" fill="#6ADE46"/>
</svg>
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" rx="20" fill="#DE4E46" fill-opacity="0.35"/>
<path d="M27.5789 14.6667C27.6906 14.6667 27.7977 14.7135 27.8767 14.7968C27.9556 14.8802 28 14.9932 28 15.1111V16C28 16.1179 27.9556 16.2309 27.8767 16.3143C27.7977 16.3976 27.6906 16.4444 27.5789 16.4444H25.8947V26.2222C25.8947 27.2124 25.0863 27.9538 24.1318 27.9978L24.0421 28H15.9579C14.9954 28 14.1575 27.2889 14.1078 26.3142L14.1053 26.2222V16.4444H12.4211C12.3094 16.4444 12.2023 16.3976 12.1233 16.3143C12.0444 16.2309 12 16.1179 12 16V15.1111C12 14.9932 12.0444 14.8802 12.1233 14.7968C12.2023 14.7135 12.3094 14.6667 12.4211 14.6667H27.5789ZM24.2105 16.4444H15.7895V26.1729L15.8063 26.1853C15.8395 26.204 15.8758 26.2156 15.9133 26.2196L15.9579 26.2222H24.0421C24.0948 26.2236 24.1469 26.2109 24.1937 26.1853L24.2105 26.1724V16.4444ZM18.7368 18.6667C18.8485 18.6667 18.9556 18.7135 19.0346 18.7968C19.1135 18.8802 19.1579 18.9932 19.1579 19.1111V23.5556C19.1579 23.6734 19.1135 23.7865 19.0346 23.8698C18.9556 23.9532 18.8485 24 18.7368 24H17.8947C17.7831 24 17.676 23.9532 17.597 23.8698C17.518 23.7865 17.4737 23.6734 17.4737 23.5556V19.1111C17.4737 18.9932 17.518 18.8802 17.597 18.7968C17.676 18.7135 17.7831 18.6667 17.8947 18.6667H18.7368ZM22.1053 18.6667C22.2169 18.6667 22.324 18.7135 22.403 18.7968C22.482 18.8802 22.5263 18.9932 22.5263 19.1111V23.5556C22.5263 23.6734 22.482 23.7865 22.403 23.8698C22.324 23.9532 22.2169 24 22.1053 24H21.2632C21.1515 24 21.0444 23.9532 20.9654 23.8698C20.8865 23.7865 20.8421 23.6734 20.8421 23.5556V19.1111C20.8421 18.9932 20.8865 18.8802 20.9654 18.7968C21.0444 18.7135 21.1515 18.6667 21.2632 18.6667H22.1053ZM22.1053 12C22.1606 12 22.2153 12.0115 22.2664 12.0338C22.3175 12.0562 22.3639 12.0889 22.403 12.1302C22.4421 12.1714 22.4731 12.2204 22.4943 12.2744C22.5154 12.3283 22.5263 12.3861 22.5263 12.4444V13.3333C22.5263 13.4512 22.482 13.5643 22.403 13.6476C22.324 13.731 22.2169 13.7778 22.1053 13.7778H17.8947C17.7831 13.7778 17.676 13.731 17.597 13.6476C17.518 13.5643 17.4737 13.4512 17.4737 13.3333V12.4444C17.4737 12.3861 17.4846 12.3283 17.5057 12.2744C17.5269 12.2204 17.5579 12.1714 17.597 12.1302C17.6361 12.0889 17.6825 12.0562 17.7336 12.0338C17.7847 12.0115 17.8394 12 17.8947 12H22.1053Z" fill="#F22828"/>
</svg>
import React, { useEffect, useState } from 'react';
import type { IconProps } from '@chakra-ui/react';
import { Box, Icon } from '@chakra-ui/react';
import { iconPaths } from './constants';
import type { IconNameType } from './type.d';
const MyIcon = ({ name, w = 'auto', h = 'auto', ...props }: { name: IconNameType } & IconProps) => {
const [IconComponent, setIconComponent] = useState<any>(null);
useEffect(() => {
console.log(name);
iconPaths[name]?.()
.then((icon) => {
setIconComponent({ as: icon.default });
})
.catch((error) => console.log(error));
}, [name]);
return !!IconComponent ? (
<Icon
{...IconComponent}
w={w}
h={h}
boxSizing={'content-box'}
verticalAlign={'top'}
fill={'currentcolor'}
{...props}
/>
) : (
<Box w={w} h={'1px'}></Box>
);
};
export default React.memo(MyIcon);
import { iconPaths } from './constants';
export type IconNameType = keyof typeof iconPaths;
import React from 'react';
import { Box, BoxProps } from '@chakra-ui/react';
const FormLabel = ({
children,
required,
...props
}: BoxProps & {
required?: boolean;
children: React.ReactNode;
}) => {
return (
<Box color={'myGray.900'} fontSize={'sm'} position={'relative'} {...props}>
{required && (
<Box color={'red.600'} position={'absolute'} top={'-4px'} left={'-6px'}>
*
</Box>
)}
{children}
</Box>
);
};
export default FormLabel;
import React, { forwardRef } from 'react';
import { Box, BoxProps, SpinnerProps } from '@chakra-ui/react';
import Loading from '../MyLoading';
type Props = BoxProps & {
isLoading?: boolean;
text?: string;
size?: SpinnerProps['size'];
};
const MyBox = ({ text, isLoading, children, size, ...props }: Props, ref: any) => {
return (
<Box ref={ref} position={isLoading ? 'relative' : 'unset'} {...props}>
{isLoading && <Loading fixed={false} text={text} size={size} />}
{children}
</Box>
);
};
export default forwardRef(MyBox);
import React from 'react';
import { Spinner, Flex, Box, SpinnerProps } from '@chakra-ui/react';
const Loading = ({
fixed = true,
text = '',
bg = 'rgba(255,255,255,0.5)',
zIndex = 1000,
size = 'lg'
}: {
fixed?: boolean;
text?: string;
bg?: string;
zIndex?: number;
size?: SpinnerProps['size'];
}) => {
return (
<Flex
position={fixed ? 'fixed' : 'absolute'}
zIndex={fixed ? zIndex : 10}
bg={bg}
borderRadius={'md'}
top={0}
left={0}
right={0}
bottom={0}
alignItems={'center'}
justifyContent={'center'}
flexDirection={'column'}
>
<Spinner
thickness="4px"
speed="0.65s"
emptyColor="myGray.100"
color="primary.500"
size={size}
/>
{text && (
<Box mt={2} color="primary.600" fontWeight={'bold'}>
{text}
</Box>
)}
</Flex>
);
};
export default Loading;
import React, { useMemo } from 'react';
import { ModalFooter, ModalBody, Input, Button, Box, Textarea } from '@chakra-ui/react';
import MyModal from './index';
import { useTranslation } from 'next-i18next';
import { useRequest2 } from '@eagic/web/hooks/useRequest';
import FormLabel from '../MyBox/FormLabel';
import { useForm } from 'react-hook-form';
export type EditFolderFormType = {
id?: string;
name?: string;
intro?: string;
};
type CommitType = {
name: string;
intro?: string;
};
const EditFolderModal = ({
onClose,
onCreate,
onEdit,
id,
name,
intro
}: EditFolderFormType & {
onClose: () => void;
onCreate: (data: CommitType) => any;
onEdit: (data: CommitType & { id: string }) => any;
}) => {
const { t } = useTranslation();
const isEdit = !!id;
const { register, handleSubmit } = useForm<EditFolderFormType>({
defaultValues: {
name,
intro
}
});
const typeMap = useMemo(
() =>
isEdit
? {
title: t('common:dataset.Edit Folder')
}
: {
title: t('common:dataset.Create Folder')
},
[isEdit, t]
);
const { run: onSave, loading } = useRequest2(
({ name = '', intro }: EditFolderFormType) => {
if (!name) return;
if (isEdit) return onEdit({ id, name, intro });
return onCreate({ name, intro });
},
{
onSuccess: (res) => {
onClose();
}
}
);
return (
<MyModal isOpen onClose={onClose} iconSrc="common/folderFill" title={typeMap.title}>
<ModalBody>
<Box>
<FormLabel mb={1}>{t('common:common.Input name')}</FormLabel>
<Input
{...register('name', { required: true })}
bg={'myGray.50'}
autoFocus
maxLength={20}
/>
</Box>
<Box mt={4}>
<FormLabel mb={1}>{t('common:common.Input folder description')}</FormLabel>
<Textarea {...register('intro')} bg={'myGray.50'} maxLength={200} />
</Box>
</ModalBody>
<ModalFooter>
<Button isLoading={loading} onClick={handleSubmit(onSave)} px={6}>
{t('common:common.Confirm')}
</Button>
</ModalFooter>
</MyModal>
);
};
export default EditFolderModal;
import React from 'react';
import {
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalCloseButton,
ModalContentProps,
Box
} from '@chakra-ui/react';
import MyBox from '../MyBox';
import { useSystem } from '../../../../hooks/useSystem';
import Avatar from '../Avatar';
export interface MyModalProps extends ModalContentProps {
iconSrc?: string;
iconColor?: string;
title?: any;
isCentered?: boolean;
isLoading?: boolean;
isOpen?: boolean;
onClose?: () => void;
closeOnOverlayClick?: boolean;
size?: 'md' | 'lg';
}
const MyModal = ({
isOpen = true,
onClose,
iconSrc,
title,
children,
isCentered,
isLoading,
w = 'auto',
maxW = ['90vw', '600px'],
closeOnOverlayClick = true,
iconColor,
size = 'md',
...props
}: MyModalProps) => {
const { isPc } = useSystem();
return (
<Modal
isOpen={isOpen}
onClose={() => onClose && onClose()}
size={size}
autoFocus={false}
isCentered={isPc ? isCentered : true}
blockScrollOnMount={false}
closeOnOverlayClick={closeOnOverlayClick}
>
<ModalOverlay />
<ModalContent
w={w}
minW={['90vw', '400px']}
maxW={maxW}
position={'relative'}
maxH={'85vh'}
boxShadow={'7'}
{...props}
>
{!title && onClose && <ModalCloseButton zIndex={1} />}
{!!title && (
<ModalHeader
display={'flex'}
alignItems={'center'}
background={'#FBFBFC'}
borderBottom={'1px solid #F4F6F8'}
roundedTop={'lg'}
py={'10px'}
fontSize={'md'}
fontWeight={'bold'}
>
{iconSrc && (
<>
<Avatar
color={iconColor}
objectFit={'contain'}
alt=""
src={iconSrc}
w={'1.5rem'}
borderRadius={'md'}
/>
</>
)}
<Box ml={3} color={'myGray.900'} fontWeight={'500'}>
{title}
</Box>
<Box flex={1} />
{onClose && (
<ModalCloseButton position={'relative'} fontSize={'xs'} top={0} right={0} />
)}
</ModalHeader>
)}
<MyBox
isLoading={isLoading}
overflow={props.overflow || 'overlay'}
h={'100%'}
display={'flex'}
flexDirection={'column'}
>
{children}
</MyBox>
</ModalContent>
</Modal>
);
};
export default React.memo(MyModal);
import { ChakraProvider, ColorModeScript } from '@chakra-ui/react';
import { theme } from '../styles/theme';
import { Router } from 'next/router';
import { ReactNode } from 'react';
import NProgress from 'nprogress'; //nprogress module
import 'nprogress/nprogress.css';
Router.events.on('routeChangeStart', () => NProgress.start());
Router.events.on('routeChangeComplete', () => NProgress.done());
Router.events.on('routeChangeError', () => NProgress.done());
export const ChakraUIContext = ({ children }: { children: ReactNode }) => {
return (
<ChakraProvider theme={theme}>
<ColorModeScript initialColorMode={theme.config.initialColorMode} />
{children}
</ChakraProvider>
);
};
export default ChakraUIContext;
import { createContext, useContextSelector } from 'use-context-selector';
import { useTranslation } from 'next-i18next';
import { TFunction } from 'i18next';
type I18nContextType = {
commonT: TFunction<['common'], undefined>;
appT: TFunction<['app'], undefined>;
datasetT: TFunction<['dataset'], undefined>;
fileT: TFunction<['file'], undefined>;
publishT: TFunction<['publish'], undefined>;
workflowT: TFunction<['workflow'], undefined>;
userT: TFunction<['user'], undefined>;
chatT: TFunction<['chat'], undefined>;
};
export const I18nContext = createContext<I18nContextType>({
// @ts-ignore
commonT: undefined
});
const I18nContextProvider = ({ children }: { children: React.ReactNode }) => {
const { t: commonT } = useTranslation('common');
const { t: appT } = useTranslation('app');
const { t: datasetT } = useTranslation('dataset');
const { t: fileT } = useTranslation('file');
const { t: publishT } = useTranslation('publish');
const { t: workflowT } = useTranslation('workflow');
const { t: userT } = useTranslation('user');
const { t: chatT } = useTranslation('chat');
return (
<I18nContext.Provider
value={{
commonT,
appT,
datasetT,
fileT,
publishT,
workflowT,
userT,
chatT
}}
>
{children}
</I18nContext.Provider>
);
};
export default I18nContextProvider;
export const useI18n = () => {
return useContextSelector(I18nContext, (ctx) => ctx);
};
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactNode } from 'react';
// Create a client
const queryClient = new QueryClient({
defaultOptions: {
queries: {
keepPreviousData: true,
refetchOnWindowFocus: false,
retry: false,
cacheTime: 10,
networkMode: 'always'
}
}
});
const QueryClientContext = ({ children }: { children: ReactNode }) => {
return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>;
};
export default QueryClientContext;
import { useToast } from './useToast';
import { useMutation } from '@tanstack/react-query';
import type { UseMutationOptions } from '@tanstack/react-query';
import { getErrText } from '@eagic/global/common/error/utils';
import { useTranslation } from 'next-i18next';
import { useRequest as ahooksUseRequest } from 'ahooks';
interface Props extends UseMutationOptions<any, any, any, any> {
successToast?: string | null;
errorToast?: string | null;
}
export const useRequest = ({ successToast, errorToast, onSuccess, onError, ...props }: Props) => {
const { toast } = useToast();
const { t } = useTranslation();
const mutation = useMutation<unknown, unknown, any, unknown>({
...props,
onSuccess(res, variables: void, context: unknown) {
onSuccess?.(res, variables, context);
successToast &&
toast({
title: successToast,
status: 'success'
});
},
onError(err: any, variables: void, context: unknown) {
onError?.(err, variables, context);
if (errorToast !== undefined) {
const errText = t(getErrText(err, errorToast || '') as any);
if (errText) {
toast({
title: errText,
status: 'error'
});
}
}
}
});
return mutation;
};
type UseRequestFunProps<TData, TParams extends any[]> = Parameters<
typeof ahooksUseRequest<TData, TParams>
>;
export const useRequest2 = <TData, TParams extends any[]>(
server: UseRequestFunProps<TData, TParams>[0],
options: UseRequestFunProps<TData, TParams>[1] & {
errorToast?: string;
successToast?: string;
} = {},
plugin?: UseRequestFunProps<TData, TParams>[2]
) => {
const { t } = useTranslation();
const { errorToast = 'Error', successToast, ...rest } = options || {};
const { toast } = useToast();
const res = ahooksUseRequest<TData, TParams>(
server,
{
manual: true,
...rest,
onError: (err, params) => {
rest?.onError?.(err, params);
if (errorToast !== undefined) {
const errText = t(getErrText(err, errorToast || '') as any);
if (errText) {
toast({
title: errText,
status: 'error'
});
}
}
},
onSuccess: (res, params) => {
rest?.onSuccess?.(res, params);
if (successToast) {
toast({
title: successToast,
status: 'success'
});
}
}
},
plugin
);
return res;
};
import { useToast as uToast, UseToastOptions } from '@chakra-ui/react';
import { useCallback, useMemo } from 'react';
export const useToast = (props?: UseToastOptions) => {
const toast = uToast({
position: 'top',
duration: 2000,
containerStyle: {
fontSize: 'sm'
},
...props
});
const myToast = useCallback(
(options?: UseToastOptions) => {
if (options?.title || options?.description) {
toast(options);
}
},
[props]
);
return {
toast: myToast
};
};
{
"Run": "Execute",
"ai_settings": "AI Configuration",
"all_apps": "All Applications",
"app": {
"Version name": "Version Name",
"modules": {
"click to update": "Click to Refresh",
"has new version": "New Version Available"
},
"version_back": "Revert to Original State",
"version_copy": "Duplicate",
"version_current": "Current Version",
"version_initial": "Initial Version",
"version_initial_copy": "Duplicate - Original State",
"version_name_tips": "Version name cannot be empty",
"version_past": "Previously Published",
"version_publish_tips": "This version will be saved to the team cloud, synchronized with the entire team, and update the app version on all release channels."
},
"app_detail": "Application Details",
"chat_debug": "Chat Preview",
"chat_logs": "Conversation Logs",
"chat_logs_tips": "Logs will record the online, shared, and API (requires chatId) conversation records of this app.",
"config_file_upload": "Click to Configure File Upload Rules",
"confirm_copy_app_tip": "The system will create an app with the same configuration for you. Please confirm!",
"confirm_del_app_tip": "Confirm to delete this app and all its conversation records?",
"confirm_delete_folder_tip": "Confirm to delete this folder? All apps and corresponding conversation records under it will be deleted. Please confirm!",
"copy_one_app": "Create Duplicate",
"create_copy_success": "Duplicate Created Successfully",
"create_empty_app": "Create Default App",
"create_empty_plugin": "Create Default Plugin",
"create_empty_workflow": "Create Default Workflow",
"cron": {
"every_day": "Run Daily",
"every_month": "Run Monthly",
"every_week": "Run Weekly",
"interval": "Run at Intervals"
},
"current_settings": "Current Configuration",
"day": "Day",
"document_quote": "Document Reference",
"document_quote_tip": "Usually used to accept user-uploaded document content (requires document parsing), and can also be used to reference other string data.",
"document_upload": "Document Upload",
"edit_app": "Edit Application",
"edit_info": "Edit Information",
"execute_time": "Execution Time",
"export_config_successful": "Configuration copied, some sensitive information automatically filtered. Please check for any remaining sensitive data.",
"export_configs": "Export Configurations",
"feedback_count": "User Feedback",
"file_recover": "File will overwrite current content",
"file_upload": "File Upload",
"file_upload_tip": "Once enabled, documents/images can be uploaded. Documents are retained for 7 days, images for 15 days. Using this feature may incur additional costs. To ensure a good experience, please choose an AI model with a larger context length when using this feature.",
"go_to_chat": "Go to Conversation",
"go_to_run": "Go to Execution",
"image_upload": "Image Upload",
"image_upload_tip": "Please ensure to select a vision model that can process images.",
"import_configs": "Import Configurations",
"import_configs_failed": "Import configuration failed, please ensure the configuration is correct!",
"import_configs_success": "Import Successful",
"interval": {
"12_hours": "Every 12 Hours",
"2_hours": "Every 2 Hours",
"3_hours": "Every 3 Hours",
"4_hours": "Every 4 Hours",
"6_hours": "Every 6 Hours",
"per_hour": "Every Hour"
},
"intro": "A comprehensive model application orchestration system that offers out-of-the-box data processing and model invocation capabilities. It allows for rapid Dataset construction and workflow orchestration through Flow visualization, enabling complex Dataset scenarios!",
"llm_not_support_vision": "This model does not support image recognition",
"llm_use_vision": "Enable Image Recognition",
"llm_use_vision_tip": "Once image recognition is enabled, this model will automatically receive images uploaded from the 'dialog box' and image links in 'user questions'.",
"logs_empty": "No logs yet~",
"logs_message_total": "Total Messages",
"logs_title": "Title",
"mark_count": "Number of Marked Answers",
"module": {
"Confirm Sync": "Will update to the latest template configuration. Fields not in the template will be deleted (including all custom fields). It is recommended to copy a node first, then update the original node version.",
"Custom Title Tip": "This title will be displayed during the conversation.",
"No Modules": "No Plugins Found",
"type": "\"{{type}}\" type\n{{description}}"
},
"modules": {
"Title is required": "Module name cannot be empty"
},
"month": {
"unit": "Day"
},
"move_app": "Move Application",
"not_json_file": "Please select a JSON file",
"or_drag_JSON": "or drag in JSON file",
"paste_config": "Paste Configuration",
"permission": {
"des": {
"manage": "Based on write permissions, you can configure publishing channels, view conversation logs, and assign permissions to the application.",
"read": "Use the app to have conversations",
"write": "Can view and edit apps"
}
},
"plugin_cost_per_times": "{{cost}}/time",
"plugin_dispatch": "Plugin Invocation",
"plugin_dispatch_tip": "Adds extra capabilities to the model. The specific plugins to be invoked will be autonomously decided by the model.\nIf a plugin is selected, the Dataset invocation will automatically be treated as a special plugin.",
"publish_channel": "Publish Channel",
"publish_success": "Publish Successful",
"saved_success": "Save Successful",
"search_app": "Search Application",
"setting_app": "Application Settings",
"setting_plugin": "Plugin Settings",
"template": {
"simple_robot": "Simple Robot"
},
"template.hard_strict": "Strict Q&A template",
"template.hard_strict_des": "Based on the question and answer template, stricter requirements are imposed on the model's answers.",
"template.qa_template": "Q&A template",
"template.qa_template_des": "A knowledge base suitable for QA question and answer structure, which allows AI to answer strictly according to preset content",
"template.simple_robot": "Simple robot",
"template.standard_strict": "Standard strict template",
"template.standard_strict_des": "Based on the standard template, stricter requirements are imposed on the model's answers.",
"template.standard_template": "Standard template",
"template.standard_template_des": "Standard prompt words for knowledge bases with unfixed structures.",
"templateMarket": {
"Search_template": "Search Template",
"Template_market": "Template Market",
"Use": "Use",
"no_intro": "No introduction yet~",
"templateTags": {
"Image_generation": "Image Generation",
"Office_services": "Office Services",
"Recommendation": "Recommendation",
"Roleplay": "Roleplay",
"Web_search": "Web Search",
"Writing": "Writing"
}
},
"template_market": "Template Market",
"template_market_description": "Explore more features in the template market, with configuration tutorials and usage guides to help you understand and get started with various applications.",
"template_market_empty_data": "No suitable templates found",
"time_zone": "Time Zone",
"tool_input_param_tip": "This plugin requires configuration of related information to run properly.",
"transition_to_workflow": "Convert to Workflow",
"transition_to_workflow_create_new_placeholder": "Create a new app instead of modifying the current app",
"transition_to_workflow_create_new_tip": "Once converted to a workflow, it cannot be reverted to simple mode. Please confirm!",
"type": {
"All": "All",
"Create http plugin tip": "Batch create plugins through OpenAPI Schema, compatible with GPTs format.",
"Create one plugin tip": "Customizable input and output workflows, usually used to encapsulate reusable workflows.",
"Create plugin bot": "Create Plugin",
"Create simple bot": "Create Simple App",
"Create simple bot tip": "Create a simple AI app by filling out a form, suitable for beginners.",
"Create workflow bot": "Create Workflow",
"Create workflow tip": "Build complex multi-turn dialogue AI applications through low-code methods, recommended for advanced users.",
"Http plugin": "HTTP Plugin",
"Plugin": "Plugin",
"Simple bot": "Simple App",
"Workflow bot": "Workflow"
},
"upload_file_max_amount": "Maximum File Quantity",
"upload_file_max_amount_tip": "1. The maximum number of files that can be uploaded at one time.\n2. The maximum number of files remembered by the chat window: each round of dialogue will automatically retrieve files from history, files beyond the range will be forgotten.",
"variable": {
"select type_desc": "A global variable that does not require user input can be defined.\nThe value of this variable can come from an API interface, a query in a shared link, or be assigned through the [Variable Update] module.",
"textarea_type_desc": "Allows users to input up to 4000 characters in the dialogue box."
},
"version": {
"Revert success": "Revert Successful"
},
"vision_model_title": "Enable Image Recognition",
"week": {
"Friday": "Friday",
"Monday": "Monday",
"Saturday": "Saturday",
"Sunday": "Sunday",
"Thursday": "Thursday",
"Tuesday": "Tuesday",
"Wednesday": "Wednesday"
},
"workflow": {
"Input guide": "Input Guide",
"file_url": "Document Link",
"read_files": "Document Parsing",
"read_files_result": "Document Parsing Result",
"read_files_result_desc": "Original document text, consisting of file names and document content, separated by hyphens between multiple files.",
"read_files_tip": "Parse all uploaded documents in the dialogue and return the corresponding document content.",
"select_description": "Description Text",
"select_description_placeholder": "For example: \nAre there tomatoes in the fridge?",
"select_description_tip": "You can add a description text to explain the meaning of each option to the user.",
"select_result": "Selected Result",
"template": {
"communication": "Communication"
},
"user_file_input": "File Link",
"user_file_input_desc": "Links to documents and images uploaded by users.",
"user_select": "User Selection",
"user_select_tip": "This module can configure multiple options for selection during the dialogue. Different options can lead to different workflow branches."
}
}
{
"Delete_all": "Clear All Lexicon",
"ai_reasoning": "Thinking process",
"chat_history": "Conversation History",
"chat_input_guide_lexicon_is_empty": "Lexicon not configured yet",
"citations": "{{num}} References",
"click_contextual_preview": "Click to see contextual preview",
"config_input_guide": "Set Up Input Guide",
"config_input_guide_lexicon": "Set Up Lexicon",
"config_input_guide_lexicon_title": "Set Up Lexicon",
"content_empty": "No Content",
"contextual": "{{num}} Contexts",
"contextual_preview": "Contextual Preview {{num}} Items",
"csv_input_lexicon_tip": "Only CSV batch import is supported, click to download the template",
"custom_input_guide_url": "Custom Lexicon URL",
"delete_all_input_guide_confirm": "Are you sure you want to clear the input guide lexicon?",
"empty_directory": "This directory is empty~",
"file_amount_over": "Exceeded maximum file quantity {{max}}",
"in_progress": "In Progress",
"input_guide": "Input Guide",
"input_guide_lexicon": "Lexicon",
"input_guide_tip": "You can set up some preset questions. When the user inputs a question, related questions from these presets will be suggested.",
"insert_input_guide,_some_data_already_exists": "Duplicate data detected, automatically filtered, {{len}} items inserted",
"is_chatting": "Chatting in progress... please wait until it finishes",
"items": "Items",
"module_runtime_and": "Total Module Runtime",
"multiple_AI_conversations": "Multiple AI Conversations",
"new_input_guide_lexicon": "New Lexicon",
"no_workflow_response": "No workflow data",
"plugins_output": "Plugin Output",
"question_tip": "From top to bottom, the response order of each module",
"response": {
"node_inputs": "Node Inputs"
},
"select_file": "Select File",
"select_img": "Select Image",
"stream_output": "Stream Output",
"view_citations": "View References",
"web_site_sync": "Web Site Sync"
}
\ No newline at end of file
{
"App": "Application",
"Export": "Export",
"FAQ": {
"ai_point_a": "Each time you use the AI model, a certain amount of AI points will be deducted. For detailed calculation standards, please refer to the 'AI Points Calculation Standards' above.\nToken calculation uses the same formula as GPT-3.5, where 1 Token ≈ 0.7 Chinese characters ≈ 0.9 English words. Consecutive characters may be considered as 1 Token.",
"ai_point_expire_a": "Yes, they will expire. After the current package expires, the AI points will be reset to the new package's AI points. Annual package AI points are valid for one year, not monthly.",
"ai_point_expire_q": "Do AI points expire?",
"ai_point_q": "What are AI points?",
"check_subscription_a": "Go to Account - Personal Information - Package Details - Usage. You can view the effective and expiration dates of your subscribed packages. After the paid package expires, it will automatically switch to the free version.",
"check_subscription_q": "Where can I view my subscribed packages?",
"dataset_compute_a": "1 Dataset storage equals 1 Dataset index. A piece of Dataset data can contain one or more Dataset indexes. In enhanced training, 1 piece of data generates 5 indexes.",
"dataset_compute_q": "How is Dataset storage calculated?",
"dataset_index_a": "No, but if the Dataset index exceeds the limit, you cannot insert or update Dataset content.",
"dataset_index_q": "Will the Dataset index be deleted if it exceeds the limit?",
"free_user_clean_a": "If a free team (free version and has not purchased additional packages) does not log in to the system for 30 consecutive days, the system will automatically clear all Dataset content under that team.",
"free_user_clean_q": "Will the data of the free version be cleared?",
"package_overlay_a": "Yes, each purchased resource pack is independent and will be used in an overlapping manner within its validity period. AI points will be deducted from the resource pack that expires first.",
"package_overlay_q": "Can additional resource packs be stacked?",
"switch_package_a": "The package usage rule is to prioritize the use of higher-level packages. Therefore, if the newly purchased package is higher than the current package, the new package will take effect immediately; otherwise, the current package will continue to be used.",
"switch_package_q": "Will the subscription package be switched?"
},
"Folder": "Folder",
"Login": "Login",
"Move": "Move",
"Name": "Name",
"Rename": "Rename",
"Resume": "Resume",
"Running": "Running",
"UnKnow": "Unknown",
"Warning": "Warning",
"add_new": "Add New",
"back": "Back",
"chose_condition": "Choose Condition",
"chosen": "Chosen",
"classification": "Classification",
"click_to_resume": "Click to Resume",
"code_editor": "Code Editor",
"code_error": {
"app_error": {
"invalid_app_type": "Invalid Application Type",
"invalid_owner": "Unauthorized Application Owner",
"not_exist": "Application Does Not Exist",
"un_auth_app": "Unauthorized to Operate This Application"
},
"chat_error": {
"un_auth": "Unauthorized to Operate This Chat Record"
},
"error_code": {
"400": "Request Failed",
"401": "No Access Permission",
"403": "Access Forbidden",
"404": "Request Not Found",
"405": "Request Method Error",
"406": "Request Format Error",
"410": "Resource Deleted",
"422": "Validation Error",
"500": "Server Error",
"502": "Gateway Error",
"503": "Server Overloaded or Under Maintenance",
"504": "Gateway Timeout"
},
"error_message": {
"403": "Credential Error",
"510": "Insufficient Account Balance",
"511": "Unauthorized to Operate This Model",
"513": "Unauthorized to Read This File",
"514": "Invalid API Key"
},
"openapi_error": {
"api_key_not_exist": "API Key Does Not Exist",
"exceed_limit": "Up to 10 API Keys",
"un_auth": "Unauthorized to Operate This API Key"
},
"outlink_error": {
"invalid_link": "Invalid Share Link",
"link_not_exist": "Share Link Does Not Exist",
"un_auth_user": "Identity Verification Failed"
},
"plugin_error": {
"not_exist": "Plugin Does Not Exist",
"un_auth": "Unauthorized to Operate This Plugin"
},
"system_error": {
"community_version_num_limit": "Exceeded Open Source Version Limit"
},
"team_error": {
"ai_points_not_enough": "Insufficient AI Points",
"app_amount_not_enough": "Application Limit Reached",
"dataset_amount_not_enough": "Dataset Limit Reached",
"dataset_size_not_enough": "Insufficient Dataset Capacity, Please Expand",
"over_size": "error.team.overSize",
"plugin_amount_not_enough": "Plugin Limit Reached",
"re_rank_not_enough": "Unauthorized to Use Re-Rank",
"un_auth": "Unauthorized to Operate This Team",
"website_sync_not_enough": "Unauthorized to Use Website Sync"
},
"token_error_code": {
"403": "Invalid Login Status, Please Re-login"
},
"user_error": {
"balance_not_enough": "Insufficient Account Balance",
"bin_visitor": "Identity Verification Failed",
"bin_visitor_guest": "You Are Currently a Guest, Unauthorized to Operate",
"un_auth_user": "User Not Found"
}
},
"common": {
"Action": "Action",
"Add": "Add",
"Add New": "Add New",
"Add Success": "Added Successfully",
"All": "All",
"Cancel": "Cancel",
"Choose": "Choose",
"Close": "Close",
"Config": "Configuration",
"Confirm": "Confirm",
"Confirm Create": "Confirm Creation",
"Confirm Import": "Confirm Import",
"Confirm Move": "Move Here",
"Confirm Update": "Confirm Update",
"Confirm to leave the page": "Confirm to Leave This Page?",
"Copy": "Copy",
"Copy Successful": "Copied Successfully",
"Copy_failed": "Copy Failed, Please Copy Manually",
"Create Failed": "Creation Failed",
"Create New": "Create New",
"Create Success": "Created Successfully",
"Create Time": "Creation Time",
"Creating": "Creating",
"Custom Title": "Custom Title",
"Delete": "Delete",
"Delete Failed": "Deletion Failed",
"Delete Success": "Deleted Successfully",
"Delete Warning": "Deletion Warning",
"Delete folder": "Delete Folder",
"Detail": "Detail",
"Documents": "Documents",
"Done": "Done",
"Edit": "Edit",
"Exit": "Exit",
"Exit Directly": "Exit Directly",
"Expired Time": "Expiration Time",
"File": "File",
"Finish": "Finish",
"Import": "Import",
"Import failed": "Import Failed",
"Import success": "Imported Successfully",
"Input": "Input",
"Input folder description": "Folder Description",
"Input name": "Enter a Name",
"Intro": "Introduction",
"Last Step": "Previous Step",
"Last use time": "Last Use Time",
"Load Failed": "Load Failed",
"Loading": "Loading...",
"More": "More",
"Move": "Move",
"MultipleRowSelect": {
"No data": "No Data Available"
},
"Name": "Name",
"Next Step": "Next Step",
"No more data": "No More Data",
"Not open": "Not Open",
"OK": "OK",
"Open": "Open",
"Operation": "Operation",
"Other": "Other",
"Output": "Output",
"Params": "Parameters",
"Password inconsistency": "Passwords Do Not Match",
"Permission": "Permission",
"Please Input Name": "Please Enter a Name",
"Read document": "Read Document",
"Read intro": "Read Introduction",
"Remove": "Remove",
"Rename": "Rename",
"Request Error": "Request Error",
"Require Input": "Required",
"Restart": "Restart",
"Role": "Permission",
"Root folder": "Root Folder",
"Run": "Run",
"Save": "Save",
"Save Failed": "Save Failed",
"Save Success": "Saved Successfully",
"Save_and_exit": "Save and Exit",
"Search": "Search",
"Select File Failed": "File Selection Failed",
"Select template": "Select Template",
"Set Avatar": "Click to Set Avatar",
"Set Name": "Enter a Name",
"Setting": "Setting",
"Status": "Status",
"Submit failed": "Submission Failed",
"Success": "Success",
"Sync success": "Synced Successfully",
"Team": "Team",
"Team Tags Set": "Tags",
"Un used": "Unused",
"UnKnow": "Unknown",
"UnKnow Source": "Unknown Source",
"Unlimited": "Unlimited",
"Update": "Update",
"Update Failed": "Update Failed",
"Update Success": "Updated Successfully",
"Update Successful": "Updated Successfully",
"Username": "Username",
"Waiting": "Waiting",
"Warning": "Warning",
"Website": "Website",
"all_result": "Full Results",
"avatar": {
"Select Avatar": "Click to Select Avatar",
"Select Failed": "Avatar Selection Failed"
},
"base_config": "Basic Configuration",
"choosable": "Choosable",
"confirm": {
"Common Tip": "Confirm"
},
"copy_to_clipboard": "Copy to Clipboard",
"course": {
"Read Course": "Read Course"
},
"empty": {
"Common Tip": "No Data Available"
},
"error": {
"Select avatar failed": "Avatar Selection Failed",
"unKnow": "An Unexpected Error Occurred"
},
"export_to_json": "Export to JSON",
"failed": "Failed",
"folder": {
"Drag Tip": "Click to Drag",
"Move Success": "Moved Successfully",
"Move to": "Move to",
"No Folder": "No Subdirectories, Place Here",
"Open folder": "Open Folder",
"Root Path": "Root Directory",
"empty": "No More Items in This Directory",
"open_dataset": "Open Dataset"
},
"have_done": "Completed",
"input": {
"Repeat Value": "Duplicate Value"
},
"is_requesting": "Requesting...",
"jsonEditor": {
"Parse error": "Possible JSON Error, Please Check Carefully"
},
"json_config": "JSON Configuration",
"link": {
"UnValid": "Invalid Link"
},
"month": "Month",
"name_is_empty": "Name Cannot Be Empty",
"no_intro": "No Introduction Available",
"not_support": "Not Supported",
"page_center": "Page Center",
"redo_tip": "Redo ctrl shift z",
"redo_tip_mac": "Redo ⌘ shift z",
"request_end": "All Loaded",
"request_more": "Click to Load More",
"speech": {
"error tip": "Speech to Text Failed",
"not support": "Your Browser Does Not Support Speech Input"
},
"submit_success": "Submitted Successfully",
"submitted": "Submitted",
"support": "Support",
"system": {
"Commercial version function": "Please Upgrade to the Commercial Version to Use This Feature",
"Help Chatbot": "Help Chatbot",
"Use Helper": "Use Helper"
},
"ui": {
"textarea": {
"Magnifying": "Magnifying"
}
},
"undo_tip": "Undo ctrl z",
"undo_tip_mac": "Undo ⌘ z ",
"upload_file": "Upload File",
"zoomin_tip": "Zoom Out ctrl -",
"zoomin_tip_mac": "Zoom Out ⌘ -",
"zoomout_tip": "Zoom In ctrl +",
"zoomout_tip_mac": "Zoom In ⌘ +"
},
"comon": {
"Continue_Adding": "Continue Adding"
},
"confirm_choice": "Confirm Choice",
"contribute_app_template": "Contribute Template",
"core": {
"Chat": "Chat",
"Max Token": "Max Token",
"ai": {
"AI settings": "AI Settings",
"Ai point price": "AI Points Consumption",
"Max context": "Max Context",
"Model": "AI Model",
"Not deploy rerank model": "Re-rank Model Not Deployed",
"Prompt": "Prompt",
"Support tool": "Function Call",
"model": {
"Dataset Agent Model": "File Processing Model",
"Vector Model": "Index Model",
"doc_index_and_dialog": "Document Index & Dialog Index"
}
},
"app": {
"Ai response": "AI Response",
"reasoning_response": "Output thinking",
"Api request": "API Request",
"Api request desc": "Integrate into existing systems through API, or WeChat Work, Feishu, etc.",
"App intro": "App Introduction",
"Chat Variable": "Chat Variable",
"Config schedule plan": "Configure Scheduled Execution",
"Config whisper": "Configure Voice Input",
"Interval timer config": "Scheduled Execution Configuration",
"Interval timer run": "Scheduled Execution",
"Interval timer tip": "Can Execute App on Schedule",
"Make a brief introduction of your app": "Give Your AI App an Introduction",
"Max histories": "Number of Chat Histories",
"Max tokens": "Response Limit",
"Name and avatar": "Avatar & Name",
"Publish": "Publish",
"Publish Confirm": "Confirm to Publish App? This Will Immediately Update the App Status on All Publishing Channels.",
"Publish app tip": "After Publishing the App, All Publishing Channels Will Immediately Use This Version",
"Question Guide": "Guess What You Want to Ask",
"Question Guide Tip": "After the conversation ends, 3 guiding questions will be generated.",
"Quote prompt": "Quote Template Prompt",
"Quote templates": "Quote Content Templates",
"Random": "Divergent",
"Search team tags": "Search Tags",
"Select TTS": "Select Voice Playback Mode",
"Select app from template": "Template",
"Select quote template": "Select Quote Prompt Template",
"Set a name for your app": "Set a Name for Your App",
"Setting ai property": "Click to Configure AI Model Properties",
"Share link": "Login-Free Window",
"Share link desc": "Share the link with other users, they can use it directly without logging in",
"Share link desc detail": "You can directly share this model with other users for conversation, they can use it directly without logging in. Note, this feature will consume your account balance, please keep the link safe!",
"TTS": "Voice Playback",
"TTS Tip": "After enabling, you can use the voice playback function after each conversation. Using this feature may incur additional costs.",
"TTS start": "Read Content",
"Team tags": "Team Tags",
"Temperature": "Temperature",
"Tool call": "Tool Call",
"ToolCall": {
"No plugin": "No Available Plugins",
"Parameter setting": "Input Parameters",
"System": "System",
"Team": "Team"
},
"Welcome Text": "Conversation Opening",
"Whisper": "Voice Input",
"Whisper config": "Voice Input Configuration",
"deterministic": "Deterministic",
"edit": {
"Prompt Editor": "Prompt Editor",
"Query extension background prompt": "Conversation Background Description",
"Query extension background tip": "Describe the scope of the current conversation to help the AI complete and extend the current question. The content you fill in is usually for this assistant."
},
"edit_content": "Edit App Information",
"error": {
"App name can not be empty": "App Name Cannot Be Empty",
"Get app failed": "Failed to Retrieve App"
},
"feedback": {
"Custom feedback": "Custom Feedback",
"close custom feedback": "Close Feedback"
},
"have_saved": "Saved",
"logs": {
"Source And Time": "Source & Time"
},
"more": "View More",
"no_app": "No Apps Yet, Create One Now!",
"not_saved": "Not Saved",
"outLink": {
"Can Drag": "Icon Can Be Dragged",
"Default open": "Default Open",
"Iframe block title": "Copy the iframe below to add to your website",
"Link block title": "Copy the link below to open in the browser",
"Script Close Icon": "Close Icon",
"Script Open Icon": "Open Icon",
"Script block title": "Add the code below to your website",
"Select Mode": "Start Using",
"Select Using Way": "Select Usage Method",
"Show History": "Show Chat History"
},
"publish": {
"Fei shu bot": "Feishu",
"Fei shu bot publish": "Publish to Feishu Bot"
},
"schedule": {
"Default prompt": "Default Question",
"Default prompt placeholder": "Default question when executing the app",
"Every day": "Every day at {{hour}}:00",
"Every month": "Every month on the {{day}} at {{hour}}:00",
"Every week": "Every week on {{day}} at {{hour}}:00",
"Interval": "Every {{interval}} hours",
"Open schedule": "Scheduled Execution"
},
"setting": "App Information Settings",
"share": {
"Amount limit tip": "Up to 10 groups",
"Create link": "Create New Link",
"Create link tip": "Creation successful. The share address has been copied and can be shared directly.",
"Ip limit title": "IP Rate Limit (people/minute)",
"Is response quote": "Return Quote",
"Not share link": "No Share Link Created",
"Role check": "Identity Verification"
},
"tip": {
"Add a intro to app": "Give the app an introduction",
"chatNodeSystemPromptTip": "Fixed guide words for the model. By adjusting this content, you can guide the model's chat direction. This content will be fixed at the beginning of the context. You can use / to insert variables.\nIf a Dataset is associated, you can also guide the model when to call the Dataset search by appropriate description. For example:\nYou are an assistant for the movie 'Interstellar'. When users ask about content related to 'Interstellar', please search the Dataset and answer based on the search results.",
"variableTip": "Before the conversation starts, you can ask the user to fill in some content as specific variables for this round of conversation. This module is located after the opening guide.\nVariables can be injected into other modules' string type inputs in the form of {{variable key}}, such as prompts, delimiters, etc.",
"welcomeTextTip": "Before each conversation starts, send an initial content. Supports standard Markdown syntax. Additional tags that can be used:\n[Quick Key]: Users can directly send the question by clicking"
},
"tool_label": {
"doc": "Documentation",
"github": "GitHub Address",
"price": "Pricing",
"view_doc": "View Documentation"
},
"tts": {
"Close": "Do Not Use",
"Speech model": "Speech Model",
"Speech speed": "Speech Speed",
"Test Listen": "Test Listen",
"Test Listen Text": "Hello, this is a voice test. If you can hear this sentence, the voice playback function is normal.",
"Web": "Browser Built-in (Free)"
},
"whisper": {
"Auto send": "Auto Send",
"Auto send tip": "Automatically send after voice input is completed, no need to click the send button manually",
"Auto tts response": "Auto Voice Response",
"Auto tts response tip": "The question sent by voice input will be directly responded to in voice form. Please ensure that the voice playback function is enabled.",
"Close": "Close",
"Not tts tip": "You have not enabled voice playback, this feature cannot be used",
"Open": "Open",
"Switch": "Enable Voice Input"
}
},
"chat": {
"Admin Mark Content": "Corrected Reply",
"Audio Not Support": "Device Does Not Support Voice Playback",
"Audio Speech Error": "Voice Playback Error",
"Cancel Speak": "Cancel Voice Input",
"Confirm to clear history": "Confirm to Clear Online Chat History for This App? Share and API Call Records Will Not Be Cleared.",
"Confirm to clear share chat history": "Confirm to Delete All Chat Records?",
"Converting to text": "Converting to Text...",
"Custom History Title": "Custom History Title",
"Custom History Title Description": "If set to empty, it will automatically follow the chat record.",
"Exit Chat": "Exit Chat",
"Failed to initialize chat": "Failed to Initialize Chat",
"Feedback Failed": "Feedback Submission Failed",
"Feedback Modal": "Result Feedback",
"Feedback Modal Tip": "Enter the part you are not satisfied with the answer",
"Feedback Submit": "Submit Feedback",
"Feedback Success": "Feedback Successful!",
"Finish Speak": "Voice Input Completed",
"History": "History",
"History Amount": "{{amount}} Records",
"Mark": "Mark Expected Answer",
"Mark Description": "The current marking function is in beta.\n\nAfter clicking to add a mark, you need to select a Dataset to store the marked data. You can quickly mark questions and expected answers through this function to guide the model's next answer.\n\nCurrently, the marking function is the same as other data in the Dataset and is affected by the model, which does not mean that it will 100% meet expectations after marking.\n\nMarking data is only synchronized with the Dataset in one direction. If the Dataset modifies the marked data, the marked data displayed in the log cannot be synchronized.",
"Mark Description Title": "Marking Function Introduction",
"New Chat": "New Chat",
"Pin": "Pin",
"Question Guide": "Guess What You Want to Ask",
"Quote": "Quote",
"Quote Amount": "Dataset Quotes ({{amount}} Records)",
"Read Mark Description": "View Marking Function Introduction",
"Recent use": "Recently Used",
"Record": "Voice Input",
"Restart": "Restart Chat",
"Run test": "Run Preview",
"Select dataset": "Select Dataset",
"Select dataset Desc": "Select a Dataset to store the expected answer",
"Send Message": "Send",
"Speaking": "I'm Listening, Please Speak...",
"Start Chat": "Start Chat",
"Type a message": "Enter a Question, Press [Enter] to Send / Press [Ctrl(Alt/Shift) + Enter] for New Line",
"Unpin": "Unpin",
"You need to a chat app": "You Do Not Have an Available App",
"error": {
"Chat error": "Chat Error",
"Messages empty": "API Content is Empty, Possibly Due to Text Being Too Long",
"Select dataset empty": "You Have Not Selected a Dataset",
"User input empty": "User Question Input is Empty",
"data_error": "Data Retrieval Error"
},
"feedback": {
"Close User Like": "User Agrees\nClick to Close This Mark",
"Feedback Close": "Close Feedback",
"No Content": "User Did Not Provide Specific Feedback Content",
"Read User dislike": "User Disagrees\nClick to View Content"
},
"logs": {
"api": "API Call",
"feishu": "Feishu",
"free_login": "No login link",
"official_account": "Official Account",
"online": "Online Use",
"share": "External Link Call",
"team": "Team Space Chat",
"test": "Test",
"wecom": "WeChat Work"
},
"markdown": {
"Edit Question": "Edit Question",
"Quick Question": "Click to Ask Immediately",
"Send Question": "Send Question"
},
"quote": {
"Quote Tip": "Only the actual quoted content is displayed here. If the data is updated, it will not be updated in real-time here.",
"Read Quote": "View Quote"
},
"response": {
"Complete Response": "Complete Response",
"Extension model": "Question Optimization Model",
"Read complete response": "View Details",
"Read complete response tips": "Click to View Detailed Process",
"Tool call tokens": "Tool Call Tokens Consumption",
"context total length": "Total Context Length",
"module cq": "Question Classification List",
"module cq result": "Classification Result",
"module extract description": "Extract Background Description",
"module extract result": "Extraction Result",
"module historyPreview": "History Preview (Only Partial Content Displayed)",
"module http result": "Response Body",
"module if else Result": "Condition Result",
"module limit": "Single Search Limit",
"module maxToken": "Max Response Tokens",
"module model": "Model",
"module name": "Model Name",
"module query": "Question/Search Term",
"module quoteList": "Quote Content",
"module similarity": "Similarity",
"module temperature": "Temperature",
"module time": "Run Time",
"module tokens": "Total Tokens",
"plugin output": "Plugin Output Value",
"search using reRank": "Result Re-Rank",
"text output": "Text Output",
"update_var_result": "Variable Update Result (Displays Multiple Variable Update Results in Order)",
"user_select_result": "User Selection Result"
},
"retry": "Regenerate",
"tts": {
"Stop Speech": "Stop"
}
},
"common": {
"tip": {
"leave page": "Content has been modified, confirm to leave the page?"
}
},
"dataset": {
"Choose Dataset": "Associate Dataset",
"Collection": "Dataset",
"Create dataset": "Create a {{name}}",
"Dataset": "Dataset",
"Dataset ID": "Dataset ID",
"Delete Confirm": "Confirm to Delete This Dataset? Data Cannot Be Recovered After Deletion, Please Confirm!",
"Empty Dataset": "Empty Dataset",
"Empty Dataset Tips": "No Dataset Yet, Create One Now!",
"Folder placeholder": "This is a Directory",
"Go Dataset": "Go to Dataset",
"Intro Placeholder": "This Dataset Has No Introduction Yet",
"Manual collection": "Manual Dataset",
"My Dataset": "My Dataset",
"Query extension intro": "Enabling the question optimization function can improve the accuracy of Dataset searches during continuous conversations. After enabling this function, when performing Dataset searches, the AI will complete the missing information of the question based on the conversation history.",
"Quote Length": "Quote Content Length",
"Read Dataset": "View Dataset Details",
"Set Website Config": "Start Configuring Website Information",
"Start export": "Export Started",
"Table collection": "Table Dataset",
"Text collection": "Text Dataset",
"collection": {
"Click top config website": "Click to Configure Website",
"Collection name": "Dataset Name",
"Collection raw text": "Dataset Content",
"Empty Tip": "The Dataset is Empty",
"QA Prompt": "QA Split Prompt",
"Start Sync Tip": "Confirm to Start Syncing Data? Old Data Will Be Deleted and Re-fetched, Please Confirm!",
"Sync": "Sync Data",
"Sync Collection": "Data Sync",
"Website Empty Tip": "No Website Associated Yet",
"Website Link": "Website Address",
"id": "Collection ID",
"metadata": {
"Chunk Size": "Chunk Size",
"Createtime": "Creation Time",
"Raw text length": "Raw Text Length",
"Training Type": "Training Mode",
"Updatetime": "Update Time",
"Web page selector": "Web Page Selector",
"metadata": "Metadata",
"read source": "View Original Content",
"source": "Data Source",
"source name": "Source Name",
"source size": "Source Size"
},
"status": {
"active": "Ready"
},
"sync": {
"result": {
"sameRaw": "Content Unchanged, No Update Needed",
"success": "Sync Started"
}
},
"training": {}
},
"data": {
"Auxiliary Data": "Auxiliary Data",
"Auxiliary Data Placeholder": "This part is optional and is usually used to construct structured prompts in conjunction with the 'Data Content' above for special scenarios, up to {{maxToken}} characters.",
"Auxiliary Data Tip": "This part is optional\nThis content is usually used to construct structured prompts in conjunction with the data content above for special scenarios",
"Data Content": "Related Data Content",
"Data Content Placeholder": "This input box is required. This content is usually a description of the knowledge point or a user's question, up to {{maxToken}} characters.",
"Data Content Tip": "This input box is required\nThis content is usually a description of the knowledge point or a user's question.",
"Default Index Tip": "Cannot be edited. The default index will use the text of 'Related Data Content' and 'Auxiliary Data' to generate the index directly.",
"Edit": "Edit Data",
"Empty Tip": "This collection has no data yet",
"Main Content": "Main Content",
"Search data placeholder": "Search Related Data",
"Too Long": "Total Length Exceeded",
"Total Amount": "{{total}} Groups",
"group": "Group",
"unit": "Items"
},
"embedding model tip": "The index model can convert natural language into vectors for semantic search.\nNote that different index models cannot be used together. Once an index model is selected, it cannot be changed.",
"error": {
"Data not found": "Data Not Found or Deleted",
"Start Sync Failed": "Failed to Start Sync",
"invalidVectorModelOrQAModel": "Invalid Vector Model or QA Model",
"unAuthDataset": "Unauthorized to Operate This Dataset",
"unAuthDatasetCollection": "Unauthorized to Operate This Dataset",
"unAuthDatasetData": "Unauthorized to Operate This Data",
"unAuthDatasetFile": "Unauthorized to Operate This File",
"unCreateCollection": "Unauthorized to Operate This Data",
"unLinkCollection": "Not a Web Link Collection"
},
"externalFile": "External File Library",
"file": "File",
"folder": "Directory",
"import": {
"Auto mode Estimated Price Tips": "Requires calling the file processing model, which consumes a lot of tokens: {{price}} points/1K tokens",
"Auto process": "Automatic",
"Auto process desc": "Automatically set segmentation and preprocessing rules",
"Chunk Range": "Range: {{min}}~{{max}}",
"Chunk Split": "Direct Segmentation",
"Chunk Split Tip": "Segment the text according to certain rules and convert it into a format that can be semantically searched. Suitable for most scenarios. No additional model processing is required, and the cost is low.",
"Custom process": "Custom Rules",
"Custom process desc": "Customize segmentation and preprocessing rules",
"Custom prompt": "Custom Prompt",
"Custom split char": "Custom Separator",
"Custom split char Tips": "Allows you to segment based on custom separators. Usually used for pre-processed data, using specific separators for precise segmentation.",
"Custom text": "Custom Text",
"Custom text desc": "Manually enter a piece of text as a dataset",
"Data Preprocessing": "Data Processing",
"Data process params": "Data Processing Parameters",
"Down load csv template": "Click to Download CSV Template",
"Embedding Estimated Price Tips": "Only use the index model, consuming a small amount of AI points: {{price}} points/1K tokens",
"Ideal chunk length": "Ideal Chunk Length",
"Ideal chunk length Tips": "Segment according to ending symbols and combine multiple segments into one chunk. This value determines the estimated size of the chunk.",
"Import success": "Import Successful, Please Wait for Training",
"Link name": "Web Link",
"Link name placeholder": "Only supports static links. If the data is empty after uploading, the link may not be readable\nEach line one, up to 10 links at a time",
"Local file": "Local File",
"Local file desc": "Upload files in PDF, TXT, DOCX, etc. formats",
"Preview chunks": "Preview Segments (up to 5 segments)",
"Preview raw text": "Preview Raw Text (up to 3000 characters)",
"Process way": "Processing Method",
"QA Estimated Price Tips": "Requires calling the file processing model, which consumes a lot of AI points: {{price}} points/1K tokens",
"QA Import": "QA Split",
"QA Import Tip": "According to certain rules, split the text into larger paragraphs and call AI to generate Q&A pairs for the paragraph. It has very high retrieval accuracy but may lose a lot of content details.",
"Select file": "Select File",
"Select source": "Select Source",
"Source name": "Source Name",
"Sources list": "Source List",
"Start upload": "Start Upload",
"Total files": "Total {{total}} Files",
"Training mode": "Training Mode",
"Upload data": "Upload Data",
"Upload file progress": "File Upload Progress",
"Upload status": "Status",
"Web link": "Web Link",
"Web link desc": "Read static web page content as a dataset"
},
"link": "Link",
"search": {
"Dataset Search Params": "Dataset Search Configuration",
"Empty result response": "Empty Search Response",
"Filter": "Search Filter",
"Max Tokens": "Quote Limit",
"Max Tokens Tips": "The maximum number of tokens for a single search. About 1 Chinese character = 1.7 tokens, 1 English word = 1 token",
"Min Similarity": "Minimum Similarity",
"Min Similarity Tips": "The similarity of different index models varies. Please choose an appropriate value through search testing. When using Re-rank, the similarity may be very low.",
"No support similarity": "Only supported when using result re-rank or semantic search",
"Nonsupport": "Not Supported",
"Params Setting": "Search Parameter Settings",
"Quote index": "Quote Index",
"ReRank": "Result Re-rank",
"ReRank desc": "Use the re-rank model for secondary sorting to enhance the comprehensive ranking.",
"Source id": "Source ID",
"Source name": "Quote Source Name",
"Using query extension": "Use Question Optimization",
"mode": {
"embedding": "Semantic Search",
"embedding desc": "Use vectors for text relevance queries",
"fullTextRecall": "Full Text Search",
"fullTextRecall desc": "Use traditional full-text search, suitable for finding some keywords and subject-predicate special data",
"mixedRecall": "Mixed Search",
"mixedRecall desc": "Use a combination of vector search and full-text search results, sorted using the RRF algorithm."
},
"score": {
"embedding": "Semantic Search",
"embedding desc": "Get scores by calculating the distance between vectors, ranging from 0 to 1.",
"fullText": "Full Text Search",
"fullText desc": "Calculate the score of the same keywords, ranging from 0 to infinity.",
"reRank": "Result Re-rank",
"reRank desc": "Calculate the relevance between sentences using the re-rank model, ranging from 0 to 1.",
"rrf": "Comprehensive Ranking",
"rrf desc": "Merge multiple search results using the reciprocal rank fusion method."
},
"search mode": "Search Mode"
},
"status": {
"active": "Ready",
"syncing": "Syncing"
},
"test": {
"Batch test": "Batch Test",
"Batch test Placeholder": "Select a CSV File",
"Search Test": "Search Test",
"Test": "Test",
"Test Result": "Test Result",
"Test Text": "Single Text Test",
"Test Text Placeholder": "Enter the text to be tested",
"Test params": "Test Parameters",
"delete test history": "Delete This Test Result",
"test history": "Test History",
"test result placeholder": "Test results will be displayed here",
"test result tip": "Sort based on the similarity between the Dataset content and the test text. You can adjust the corresponding text based on the test results.\nNote: The data in the test records may have been modified. Clicking on a test data will display the latest data."
},
"training": {
"Agent queue": "QA Training Queue",
"Auto mode": "Enhanced Processing (Experimental)",
"Auto mode Tip": "Increase the semantic richness of data blocks by generating related questions and summaries through sub-indexes and calling models, making it more conducive to retrieval. Requires more storage space and increases AI call times.",
"Chunk mode": "Direct Segmentation",
"Full": "Estimated Over 5 Minutes",
"Leisure": "Idle",
"QA mode": "QA Split",
"Vector queue": "Index Queue",
"Waiting": "Estimated 5 Minutes",
"Website Sync": "Website Sync",
"tag": "Queue Status"
},
"website": {
"Base Url": "Base URL",
"Config": "Website Configuration",
"Config Description": "The website sync function allows you to fill in the root address of a website. The system will automatically crawl related web pages for Dataset training. Only static websites will be crawled, mainly project documentation and blogs.",
"Confirm Create Tips": "Confirm to sync this site. The sync task will start shortly. Please confirm!",
"Confirm Update Tips": "Confirm to update the site configuration? The sync will start immediately according to the new configuration. Please confirm!",
"Selector": "Selector",
"Selector Course": "Usage Tutorial",
"Start Sync": "Start Sync",
"UnValid Website Tip": "Your site may not be a static site and cannot be synced"
}
},
"module": {
"Add question type": "Add Question Type",
"Add_option": "Add Option",
"Can not connect self": "Cannot Connect to Itself",
"Data Type": "Data Type",
"Dataset quote": {
"label": "Dataset Quote",
"select": "Select Dataset Quote"
},
"Default Value": "Default Value",
"Default value": "Default Value",
"Default value placeholder": "Leave blank to return an empty string by default",
"Diagram": "Diagram",
"Edit intro": "Edit Description",
"Field Description": "Field Description",
"Field Name": "Field Name",
"Http request props": "Request Parameters",
"Http request settings": "Request Configuration",
"Http timeout": "Timeout Duration",
"Input Type": "Input Type",
"Laf sync params": "Sync Parameters",
"Max Length": "Max Length",
"Max Length placeholder": "Maximum length of input text",
"Max Value": "Max Value",
"Min Value": "Min Value",
"QueryExtension": {
"placeholder": "For example:\nQuestions about the introduction and use of Python.\nThe current conversation is related to the game 'GTA5'."
},
"Quote prompt setting": "Quote Prompt Configuration",
"Select app": "Select App",
"Setting quote prompt": "Configure Quote Prompt",
"Variable": "Global Variable",
"Variable Setting": "Variable Setting",
"edit": {
"Field Name Cannot Be Empty": "Field Name Cannot Be Empty"
},
"extract": {
"Add field": "Add Field",
"Enum Description": "List the possible values of this field, one per line",
"Enum Value": "Enum Value",
"Field Description Placeholder": "Name/Age/SQL Statement...",
"Field Setting Title": "Extract Field Configuration",
"Required": "Must Return",
"Required Description": "Even if the field cannot be extracted, it will be returned using the default value",
"Target field": "Target Field"
},
"http": {
"Add props": "Add Parameter",
"AppId": "App ID",
"ChatId": "Current Chat ID",
"Current time": "Current Time",
"Histories": "History Records",
"Key already exists": "Key Already Exists",
"Key cannot be empty": "Parameter Name Cannot Be Empty",
"Props name": "Parameter Name",
"Props tip": "You can set related parameters for the HTTP request\nYou can call global variables or external parameter inputs through {{key}}, currently available variables:\n{{variable}}",
"Props value": "Parameter Value",
"ResponseChatItemId": "AI Response ID",
"Url and params have been split": "Path parameters have been automatically added to Params",
"curl import": "cURL Import",
"curl import placeholder": "Please enter the cURL format content, the request information of the first interface will be extracted."
},
"input": {
"Add Branch": "Add Branch",
"add": "Add Condition",
"description": {
"Background": "You can add some specific content introductions to better identify the type of user questions. This content is usually to introduce something the model does not know.",
"HTTP Dynamic Input": "Receive the output value of the previous node as a variable, which can be used by the HTTP request parameters.",
"Http Request Header": "Custom request headers, please strictly fill in the JSON string.\n1. Ensure that the last attribute has no comma\n2. Ensure that the key contains double quotes\nFor example: {\"Authorization\":\"Bearer xxx\"}",
"Http Request Url": "New HTTP request address. If there are two 'request addresses', you can delete this module and re-add it to pull the latest module configuration.",
"Response content": "You can use \\n to achieve continuous line breaks.\nYou can achieve replies through external module input, and the content filled in here will be overwritten by external module input.\nIf non-string type data is passed in, it will be automatically converted to a string"
},
"label": {
"Background": "Background Knowledge",
"Http Request Url": "Request Address",
"Response content": "Response Content",
"Select dataset": "Select Dataset",
"aiModel": "AI Model",
"chat history": "Chat History",
"user question": "User Question"
},
"placeholder": {
"Classify background": "For example:\n1. AIGC (Artificial Intelligence Generated Content) refers to the use of artificial intelligence technology to automatically or semi-automatically generate digital content, such as text, images, music, videos, etc.\n2. AIGC technology includes but is not limited to natural language processing, computer vision, machine learning, and deep learning. These technologies can create new content or modify existing content to meet specific creative, educational, entertainment, or informational needs."
}
},
"laf": {
"Select laf function": "Select LAF Function"
},
"output": {
"description": {
"Ai response content": "Will be triggered after the stream reply is completed",
"New context": "Splice the current reply content with the history records and return it as the new context",
"query extension result": "Output as a string array, which can be directly connected to the 'User Question' of 'Dataset Search'. It is recommended not to connect to the 'User Question' of 'AI Chat'"
},
"label": {
"Ai response content": "AI Response Content",
"New context": "New Context",
"query extension result": "Optimization Result"
}
},
"template": {
"AI function": "AI Capability",
"AI response switch tip": "If you want the current node not to output content, you can turn off this switch. The content output by AI will not be displayed to the user, and you can manually use 'AI Response Content' for special processing.",
"AI support tool tip": "Models that support function calls can better use tool calls.",
"Basic Node": "Basic Function",
"Query extension": "Question Optimization",
"System Plugin": "System Plugin",
"System input module": "System Input",
"Team app": "Team App",
"Tool module": "Tool",
"UnKnow Module": "Unknown Module",
"ai_chat": "AI conversation",
"ai_chat_intro": "AI large model dialogue",
"config_params": "Can configure application system parameters",
"empty_plugin": "Blank plugin",
"empty_workflow": "Blank workflow",
"http body placeholder": "Same syntax as Apifox",
"self_input": "Custom plug-in input",
"self_output": "Custom plug-in output",
"system_config": "System configuration",
"system_config_info": "Can configure application system parameters",
"work_start": "Process starts"
},
"templates": {
"Load plugin error": "Failed to Load Plugin"
},
"variable": {
"Custom type": "Custom Variable",
"add option": "Add Option",
"input type": "Text",
"key": "Variable Key",
"key already exists": "Key Already Exists",
"key is required": "Variable Key is Required",
"select type": "Dropdown Single Select",
"text max length": "Max Length",
"textarea type": "Paragraph",
"variable name": "Variable Name",
"variable name is required": "Variable Name Cannot Be Empty",
"variable option is required": "Options Cannot Be All Empty",
"variable option is value is required": "Option Content Cannot Be Empty",
"variable options": "Options"
},
"variable add option": "Add Option"
},
"plugin": {
"Custom headers": "Custom Request Headers",
"Free": "This plugin does not consume points",
"Get Plugin Module Detail Failed": "Failed to Retrieve Plugin Information",
"Http plugin intro placeholder": "For display only, no actual effect",
"cost": "Points Consumption:"
},
"view_chat_detail": "View Chat Details",
"workflow": {
"Can not delete node": "This Node Cannot Be Deleted",
"Change input type tip": "Changing the input type will clear the filled values, please confirm!",
"Check Failed": "Workflow Validation Failed, Please Check If the Nodes Are Correctly Filled and the Connections Are Normal",
"Confirm stop debug": "Confirm to Stop Debugging? Debug Information Will Not Be Retained.",
"Copy node": "Node Copied",
"Custom inputs": "Custom Inputs",
"Custom outputs": "Custom Outputs",
"Dataset quote": "Dataset Quote",
"Debug": "Debug",
"Debug Node": "Debug Mode",
"Failed": "Run Failed",
"Not intro": "This Node Has No Introduction",
"Run": "Run",
"Running": "Running",
"Save and publish": "Save and Publish",
"Save to cloud": "Save Only",
"Skipped": "Skipped",
"Stop debug": "Stop Debugging",
"Success": "Run Successful",
"Value type": "Data Type",
"Variable": {
"Variable type": "Variable Type"
},
"debug": {
"Done": "Debugging Completed",
"Hide result": "Hide Result",
"Not result": "No Run Result",
"Run result": "Run Result",
"Show result": "Show Result"
},
"dynamic_input": "dynamic input",
"inputType": {
"JSON Editor": "JSON Input Box",
"Manual input": "Manual Input",
"Manual select": "Manual Select",
"Reference": "Variable Reference",
"custom": "Custom Variable",
"dynamicTargetInput": "Dynamic External Data",
"input": "Single Line Input Box",
"number input": "Number Input Box",
"select": "Single Select Box",
"selectApp": "App Select",
"selectDataset": "Dataset Select",
"selectLLMModel": "Chat Model Select",
"switch": "Switch",
"textarea": "Multi-line Input Box"
},
"publish": {
"OnRevert version": "Click to Revert to This Version",
"OnRevert version confirm": "Confirm to Revert to This Version? The configuration of the editing version will be saved, and a new release version will be created for the reverted version.",
"histories": "Release Records"
},
"template": {
"Interactive": "Interactive",
"Multimodal": "Multimodal",
"Search": "Search"
},
"tool": {
"Handle": "Tool Connector",
"Select Tool": "Select Tool"
},
"value": "Value",
"variable": "Variable"
}
},
"create": "Create",
"cron_job_run_app": "Scheduled Task",
"dataset": {
"Confirm move the folder": "Confirm to Move to This Directory",
"Confirm to delete the data": "Confirm to Delete This Data?",
"Confirm to delete the file": "Confirm to Delete This File and All Its Data?",
"Create Folder": "Create Folder",
"Create manual collection": "Create Manual Dataset",
"Delete Dataset Error": "Delete Dataset Error",
"Edit Folder": "Edit Folder",
"Edit Info": "Edit Information",
"Export": "Export",
"Export Dataset Limit Error": "Export Data Failed",
"Folder Name": "Enter Folder Name",
"Insert Data": "Insert",
"Manual collection Tip": "Manual datasets allow you to create an empty container to hold data",
"Move Failed": "Move Error",
"Select Dataset": "Select This Dataset",
"Select Dataset Tips": "Only Datasets with the same index model can be selected",
"Select Folder": "Enter Folder",
"Training Name": "Data Training",
"collections": {
"Collection Embedding": "{{total}} Indexes",
"Confirm to delete the folder": "Confirm to Delete This Folder and All Its Contents?",
"Create And Import": "Create/Import",
"Data Amount": "Total Data",
"Select Collection": "Select File",
"Select One Collection To Store": "Select a File to Store"
},
"data": {
"Can not edit": "No Edit Permission",
"Custom Index Number": "Custom Index {{number}}",
"Default Index": "Default Index",
"Delete Tip": "Confirm to Delete This Data?",
"Index Placeholder": "Enter Index Text Content",
"Input Success Tip": "Data Imported Successfully",
"Update Success Tip": "Data Updated Successfully",
"edit": {
"Index": "Data Index ({{amount}})",
"divide_content": "Segment Content"
},
"input is empty": "Data Content Cannot Be Empty"
},
"dataset_name": "Dataset Name",
"deleteFolderTips": "Confirm to Delete This Folder and All Its Contained Datasets? Data Cannot Be Recovered After Deletion, Please Confirm!",
"test": {
"noResult": "No Search Results"
}
},
"error": {
"Create failed": "Create failed",
"fileNotFound": "File not found~",
"inheritPermissionError": "Inherit permission Error",
"missingParams": "Insufficient parameters",
"upload_file_error_filename": "{{name}} Upload Failed",
"username_empty": "Account cannot be empty"
},
"error.code_error": "Verification code error",
"extraction_results": "Extraction Results",
"field_name": "Field Name",
"free": "Free",
"get_QR_failed": "Failed to Get QR Code",
"get_app_failed": "Failed to Retrieve App",
"get_laf_failed": "Failed to Retrieve Laf Function List",
"has_verification": "Verified, Click to Unbind",
"info": {
"buy_extra": "Buy Extra Package",
"csv_download": "Click to Download Batch Test Template",
"csv_message": "Read the first column of the CSV file for batch testing, supporting up to 100 groups of data at a time.",
"felid_message": "Field key must be pure English letters or numbers and cannot start with a number.",
"free_plan": "If a free team does not log in to the system for 30 consecutive days, the system will automatically clear the account's Dataset.",
"include": "Includes Standard Package and Extra Resource Pack",
"node_info": "Adjusting this module will affect the timing of tool calls.\nYou can guide the model to call tools by accurately describing the function of this module.",
"old_version_attention": "Detected that your advanced orchestration is an old version. The system will automatically format it into the new workflow version.\n\nDue to significant version differences, some workflows may not be arranged correctly. Please manually reconnect the workflow. If it is still abnormal, try deleting the corresponding node and re-adding it.\n\nYou can directly click debug to test the workflow. After debugging, click publish. The new workflow will only be saved and take effect after you click publish.\n\nBefore you publish the new workflow, auto-save will not take effect.",
"open_api_notice": "You can fill in the relevant keys of OpenAI/OneAPI. If you fill in this content, the 'AI Chat', 'Question Classification', and 'Content Extraction' on the online platform will use the key you filled in and will not be charged. Please check if your key has access to the corresponding model. GPT models can choose FastAI.",
"open_api_placeholder": "Request address, default is the official OpenAI. You can fill in the transit address, 'v1' will not be automatically completed",
"resource": "Resource Usage"
},
"invalid_variable": "Invalid Variable",
"is_open": "Is Open",
"is_using": "In Use",
"item_description": "Field Description",
"item_name": "Field Name",
"key_repetition": "Key Repetition",
"navbar": {
"Account": "Account",
"Chat": "Chat",
"Datasets": "Datasets",
"Studio": "Studio",
"Tools": "Tools",
"Store": "Store",
"Admin": "Admin"
},
"new_create": "Create New",
"no": "No",
"no_laf_env": "System Not Configured with Laf Environment",
"not_yet_introduced": "No Introduction Yet",
"option": "Option",
"pay": {
"amount": "Amount",
"package_tip": {
"buy": "The package you purchased is lower than the current package. This package will take effect after the current package expires.\nYou can view the package usage in Account - Personal Information - Package Details.",
"renewal": "You are renewing the package. You can view the package usage in Account - Personal Information - Package Details.",
"upgrade": "The package you purchased is higher than the current package. This package will take effect immediately, and the current package will take effect later. You can view the package usage in Account - Personal Information - Package Details."
},
"wechat": "Please Scan the QR Code with WeChat to Pay: {{price}} Yuan\nPlease Do Not Close the Page",
"yuan": "{{amount}} Yuan"
},
"permission": {
"Collaborator": "Collaborator",
"Default permission": "Default Permission",
"Manage": "Manage",
"No InheritPermission": "Permission Inheritance Restricted",
"Not collaborator": "No Collaborator",
"Owner": "Owner",
"Permission": "Permission",
"Permission config": "Permission Configuration",
"Private": "Private",
"Private Tip": "Only Available to Yourself",
"Public": "Team",
"Public Tip": "Available to All Team Members",
"Remove InheritPermission Confirm": "This operation will invalidate permission inheritance. Proceed?",
"Resume InheritPermission Confirm": "Resume inheriting permissions from the parent folder?",
"Resume InheritPermission Failed": "Resume Failed",
"Resume InheritPermission Success": "Resume Successful",
"change_owner": "Transfer Ownership",
"change_owner_failed": "Transfer Ownership Failed",
"change_owner_placeholder": "Enter Username to Search Account",
"change_owner_success": "Ownership Transferred Successfully",
"change_owner_tip": "Your permissions will not be retained after the transfer",
"change_owner_to": "Transfer to",
"manager": "administrator",
"read": "Read permission",
"write": "write permission"
},
"plugin": {
"App": "Select App",
"Currentapp": "Current App",
"Description": "Description",
"Edit Http Plugin": "Edit HTTP Plugin",
"Enter PAT": "Enter Personal Access Token (PAT)",
"Get Plugin Module Detail Failed": "Failed to Retrieve Plugin Information",
"Import Plugin": "Import HTTP Plugin",
"Import from URL": "Import from URL. https://xxxx",
"Intro": "Plugin Introduction",
"Invalid Env": "Invalid Laf Environment",
"Invalid Schema": "Invalid Schema",
"Invalid URL": "Invalid URL",
"Method": "Method",
"Path": "Path",
"Please bind laf accout first": "Please Bind Laf Account First",
"Plugin List": "Plugin List",
"Search plugin": "Search Plugin",
"Search_app": "Search App",
"Set Name": "Name the Plugin",
"contribute": "Contribute Plugin",
"go to laf": "Go to Write",
"path": "Path"
},
"required": "Required",
"resume_failed": "Resume Failed",
"select_reference_variable": "Select Reference Variable",
"share_link": "Share Link",
"support": {
"account": {
"Individuation": "Personalization"
},
"inform": {
"Read": "Read"
},
"openapi": {
"Api baseurl": "API Base URL",
"Api manager": "API Key Management",
"Copy success": "API Address Copied",
"New api key": "New API Key",
"New api key tip": "Please keep your key safe, it will not be displayed again"
},
"outlink": {
"Delete link tip": "Confirm to Delete This Login-Free Link? The link will become invalid immediately after deletion, but the chat logs will be retained. Please confirm!",
"Max usage points": "Points Limit",
"Max usage points tip": "The maximum number of points allowed for this link. It cannot be used after exceeding the limit. -1 means unlimited.",
"Usage points": "Points Consumption",
"share": {
"Response Quote": "Return Quote",
"Response Quote tips": "Return quoted content in the share link, but do not allow users to download the original document"
}
},
"permission": {
"Permission": "Permission"
},
"standard": {
"AI Bonus Points": "AI Points",
"due_date": "Due Date",
"storage": "Storage",
"type": "Type"
},
"team": {
"limit": {
"No permission rerank": "No Permission to Use Result Re-rank, Please Upgrade Your Package"
}
},
"user": {
"Avatar": "Avatar",
"Go laf env": "Click to Go to {{env}} to Get PAT Token.",
"Laf account course": "View the Tutorial for Binding Laf Account.",
"Laf account intro": "After binding your Laf account, you can use the Laf module in the workflow to write code online.",
"Need to login": "Please Log In First",
"Price": "Pricing",
"User self info": "Profile",
"auth": {
"Sending Code": "Sending Code"
},
"captcha_placeholder": "Please enter the verification code",
"inform": {
"System message": "System Message"
},
"login": {
"Email": "Email",
"Github": "GitHub Login",
"Google": "Google Login",
"Password": "Password",
"Password login": "Password Login",
"Phone": "Phone Login",
"Phone number": "Phone Number",
"Provider error": "Login Error, Please Try Again",
"Username": "Username",
"Wechat": "WeChat Login",
"can_not_login": "Cannot Log In, Click to Contact",
"error": "Login Error",
"security_failed": "Security Verification Failed",
"wx_qr_login": "WeChat QR Code Login"
},
"logout": {
"confirm": "Confirm to Log Out?"
},
"team": {
"Dataset usage": "Dataset Capacity",
"Team Tags Async Success": "Sync Completed",
"member": "Member"
}
},
"wallet": {
"Ai point every thousand tokens": "{{points}} Points/1K Tokens",
"Amount": "Amount",
"Buy": "Buy",
"Not sufficient": "Insufficient AI Points, Please Upgrade Your Package or Purchase Additional AI Points to Continue Using.",
"Plan expired time": "Package Expiration Time",
"Standard Plan Detail": "Package Details",
"To read plan": "View Package",
"amount_0": "Purchase Quantity Cannot Be 0",
"apply_invoice": "Apply for Invoice",
"bill": {
"Number": "Order Number",
"Status": "Status",
"Type": "Order Type",
"payWay": {
"Way": "Payment Method",
"balance": "Balance Payment",
"wx": "WeChat Payment"
},
"status": {
"closed": "Closed",
"notpay": "Unpaid",
"refund": "Refunded",
"success": "Payment Successful"
}
},
"bill_detail": "Bill Details",
"bill_tag": {
"bill": "Bill Records",
"default_header": "Default Header",
"invoice": "Invoice Records"
},
"billable_invoice": "Billable Invoice",
"buy_resource": "Buy Resource Pack",
"has_invoice": "Invoiced",
"invoice_amount": "Invoice Amount",
"invoice_data": {
"bank": "Bank",
"bank_account": "Bank Account",
"company_address": "Company Address",
"company_phone": "Company Phone",
"email": "Email Address",
"need_special_invoice": "Need Special Invoice",
"organization_name": "Organization Name",
"unit_code": "Unified Credit Code"
},
"invoice_detail": "Invoice Details",
"invoice_info": "The invoice will be sent to the email within 3-7 working days, please wait patiently",
"invoicing": "Invoicing",
"moduleName": {
"index": "Index Generation",
"qa": "QA Split"
},
"noBill": "No Bill Records",
"no_invoice": "No Invoice Records",
"subscription": {
"AI points": "AI Points",
"AI points click to read tip": "Each time the AI model is called, a certain amount of AI points (similar to tokens) will be consumed. Click to view detailed calculation rules.",
"AI points usage": "AI Points Usage",
"AI points usage tip": "Each time the AI model is called, a certain amount of AI points will be consumed. For specific calculation standards, please refer to the 'Pricing' above.",
"Ai points": "AI Points Calculation Standards",
"Current plan": "Current Package",
"Extra ai points": "Extra AI Points",
"Extra dataset size": "Extra Dataset Capacity",
"Extra plan": "Extra Resource Pack",
"Extra plan tip": "When the standard package is not enough, you can purchase extra resource packs to continue using",
"FAQ": "FAQ",
"Month amount": "Months",
"Next plan": "Future Package",
"Stand plan level": "Subscription Package",
"Sub plan": "Subscription Package",
"Sub plan tip": "Free to use {{title}} or upgrade to a higher package",
"Team plan and usage": "Package and Usage",
"Training weight": "Training Priority: {{weight}}",
"Update extra ai points": "Extra AI Points",
"Update extra dataset size": "Extra Storage",
"Upgrade plan": "Upgrade Package",
"ai_model": "AI Language Model",
"function": {
"History store": "{{amount}} Days of Chat History Retention",
"Max app": "{{amount}} Apps & Plugins",
"Max dataset": "{{amount}} Datasets",
"Max dataset size": "{{amount}} Dataset Indexes",
"Max members": "{{amount}} Team Members",
"Points": "{{amount}} AI Points"
},
"mode": {
"Month": "Monthly",
"Period": "Subscription Period",
"Year": "Yearly",
"Year sale": "Two Months Free"
},
"point": "Points",
"rerank": "Result Re-rank",
"standardSubLevel": {
"custom": "Custom Version",
"enterprise": "Enterprise Version",
"enterprise_desc": "Suitable for small and medium-sized enterprises to build Dataset applications in production environments",
"experience": "Experience Version",
"experience_desc": "Unlock the full functionality of Eaigc",
"free": "Free Version",
"free desc": "Basic functions can be used for free every month. If the system is not logged in for 30 consecutive days, the Dataset will be automatically cleared.",
"team": "Team Version",
"team_desc": "Suitable for small teams to build Dataset applications and provide external services"
},
"status": {
"active": "Active",
"expired": "Expired",
"inactive": "Inactive"
},
"token_compute": "Click to View Online Tokens Calculator",
"type": {
"balance": "Balance Recharge",
"extraDatasetSize": "Dataset Expansion",
"extraPoints": "AI Points Package",
"standard": "Package Subscription"
},
"web_site_sync": "Website Sync"
},
"usage": {
"Ai model": "AI Model",
"App name": "App Name",
"Audio Speech": "Voice Playback",
"Bill Module": "Billing Module",
"Duration": "Duration (seconds)",
"Extension result": "Question Optimization Result",
"Module name": "Module Name",
"Source": "Source",
"Text Length": "Text Length",
"Time": "Generation Time",
"Token Length": "Token Length",
"Total": "Total Amount",
"Total points": "AI Points Consumption",
"Usage Detail": "Usage Details",
"Whisper": "Voice Input"
}
}
},
"sync_link": "Sync Link",
"system": {
"Concat us": "Contact Us",
"Help Document": "Help Document"
},
"tag_list": "Tag List",
"team_tag": "Team Tag",
"template": {
"Quote Content Tip": "You can customize the structure of the quoted content to better adapt to different scenarios. You can use some variables to configure the template:\n{{q}} - Search content, {{a}} - Expected content, {{source}} - Source, {{sourceId}} - Source file name, {{index}} - The nth quote, they are all optional. Below is the default value:\n{{default}}",
"Quote Prompt Tip": "You can use {{quote}} to insert the quote content template, and use {{question}} to insert the question. Below is the default value:\n{{default}}"
},
"textarea_variable_picker_tip": "Enter \"/\" to select a variable",
"tool_field": "Tool Field Parameter Configuration",
"undefined_var": "Referenced an undefined variable, add it automatically?",
"unit": {
"character": "Character",
"minute": "Minute"
},
"unusable_variable": "No Usable Variables",
"upload_file_error": "File Upload Failed",
"user": {
"Account": "Account",
"Amount of earnings": "Earnings (¥)",
"Amount of inviter": "Total Number of Invites",
"Application Name": "Project Name",
"Avatar": "Avatar",
"Change": "Change",
"Copy invite url": "Copy Invite Link",
"Edit name": "Click to Edit Nickname",
"Invite Url": "Invite Link",
"Invite url tip": "Friends registered through this link will be permanently bound to you, and you will receive a balance reward when they recharge.\nAdditionally, you will immediately receive a 5 yuan reward when friends register with their phone number.\nThe reward will be sent to your default team.",
"Laf Account Setting": "Laf Account Configuration",
"Language": "Language",
"Member Name": "Nickname",
"Notification Receive": "Notification Receive",
"Notification Receive Bind": "Please bind the notification receive method first",
"Old password is error": "Old Password is Incorrect",
"OpenAI Account Setting": "OpenAI Account Configuration",
"Password": "Password",
"Pay": "Recharge",
"Promotion": "Promotion",
"Promotion Rate": "Cashback Rate",
"Promotion rate tip": "You will receive a balance reward when friends recharge",
"Replace": "Replace",
"Set OpenAI Account Failed": "Failed to Set OpenAI Account",
"Team": "Team",
"Time": "Time",
"Timezone": "Timezone",
"Update Password": "Update Password",
"Update password failed": "Failed to Update Password",
"Update password successful": "Password Updated Successfully",
"apikey": {
"key": "API Key"
},
"confirm_password": "Confirm Password",
"new_password": "New Password",
"no_invite_records": "No Invite Records",
"no_notice": "No Notices",
"no_usage_records": "No Usage Records",
"old_password": "Old Password",
"password_message": "Password must be at least 4 characters and at most 60 characters",
"team": {
"Balance": "Team Balance",
"Check Team": "Switch",
"Confirm Invite": "Confirm Invite",
"Create Team": "Create New Team",
"Invite Member": "Invite Member",
"Invite Member Failed Tip": "Failed to Invite Member",
"Invite Member Result Tip": "Invite Result Tip",
"Invite Member Success Tip": "Member Invitation Completed\nSuccess: {{success}} people\nInvalid Username: {{inValid}}\nAlready in Team: {{inTeam}}",
"Invite Member Tips": "The other party can view or use other resources within the team",
"Leave Team": "Leave Team",
"Leave Team Failed": "Failed to Leave Team",
"Member": "Member",
"Member Name": "Member Name",
"Over Max Member Tip": "The team can have up to {{max}} people",
"Personal Team": "Personal Team",
"Processing invitations": "Processing Invitations",
"Processing invitations Tips": "You have {{amount}} team invitations to process",
"Remove Member Confirm Tip": "Confirm to remove {{username}} from the team?",
"Select Team": "Select Team",
"Set Name": "Name the Team",
"Switch Team Failed": "Failed to Switch Team",
"Tags Async": "Save",
"Team Name": "Team Name",
"Team Tags Async": "Tag Sync",
"Team Tags Async Success": "Link Error Successful, Tag Information Updated",
"Update Team": "Update Team Information",
"invite": {
"Accept Confirm": "Confirm to join this team?",
"Accepted": "Joined Team",
"Deal Width Footer Tip": "It will automatically close after processing",
"Reject": "Invitation Rejected",
"Reject Confirm": "Confirm to reject this invitation?",
"accept": "Accept",
"reject": "Reject"
},
"member": {
"Confirm Leave": "Confirm to leave this team?",
"active": "Joined",
"reject": "Rejected",
"waiting": "Pending Acceptance"
},
"role": {
"Admin": "Admin",
"Owner": "Owner"
}
},
"type": "Type"
},
"verification": "Verification",
"xx_search_result": "{{key}} Search Results",
"yes": "Yes"
}
{
"Enable": "Enable",
"collection": {
"Create update time": "Creation/Update Time",
"Training type": "Training Mode"
},
"collection_tags": "Collection Tags",
"common_dataset": "General Dataset",
"common_dataset_desc": "Build a Dataset by importing files, web links, or manual input.",
"confirm_to_rebuild_embedding_tip": "Are you sure you want to switch the index for the Dataset?\nSwitching the index is a significant operation that requires re-indexing all data in your Dataset, which may take a long time. Please ensure your account has sufficient remaining points.\n\nAdditionally, you need to update the applications that use this Dataset to avoid conflicts with other indexed model Datasets.",
"dataset": {
"no_collections": "No datasets available",
"no_tags": "No tags available"
},
"external_file": "External File Library",
"external_file_dataset_desc": "Import files from an external file library to build a Dataset. The files will not be stored again.",
"external_id": "File Reading ID",
"external_read_url": "External Preview URL",
"external_read_url_tip": "Configure the reading URL of your file library for user authentication. Use the {{fileId}} variable to refer to the external file ID.",
"external_url": "File Access URL",
"file_model_function_tip": "Enhances indexing and QA generation",
"filename": "Filename",
"folder_dataset": "Folder",
"permission": {
"des": {
"manage": "Can manage the entire knowledge base data and information",
"read": "View knowledge base content",
"write": "Ability to add and change knowledge base content"
}
},
"rebuild_embedding_start_tip": "Index model switching task has started",
"rebuilding_index_count": "Number of indexes being rebuilt: {{count}}",
"tag": {
"Add New": "Add New",
"Add_new_tag": "Add New Tag",
"Edit_tag": "Edit Tag",
"add": "Create",
"cancel": "Cancel",
"delete_tag_confirm": "Confirm to delete the tag?",
"manage": "Tagging",
"searchOrAddTag": "Search or Add Tag",
"tags": "Tags",
"total_tags": "Total {{total}} tags"
},
"the_knowledge_base_has_indexes_that_are_being_trained_or_being_rebuilt": "The Dataset has indexes that are being trained or rebuilt",
"website_dataset": "Website Sync",
"website_dataset_desc": "Website sync allows you to build a Dataset directly using a web link."
}
\ No newline at end of file
{
"bucket_chat": "Conversation Files",
"bucket_file": "Dataset Documents",
"click_to_view_raw_source": "Click to View Original Source",
"file_name": "Filename",
"file_size": "Filesize",
"release_the_mouse_to_upload_the_file": "Release Mouse to Upload File",
"select_and_drag_file_tip": "Click or Drag Files Here to Upload",
"select_file_amount_limit": "You can select up to {{max}} files",
"some_file_count_exceeds_limit": "Exceeded {{maxCount}} files, automatically truncated",
"some_file_size_exceeds_limit": "Some files exceed {{maxSize}}, filtered out",
"support_file_type": "Supports {{fileType}} file types",
"support_max_count": "Supports up to {{maxCount}} files",
"support_max_size": "Maximum file size is {{maxSize}}",
"upload_failed": "Upload Failed",
"reached_max_file_count": "Maximum file count reached",
"upload_error_description": "Only multiple files or a single folder can be uploaded at a time"
}
\ No newline at end of file
{
"Login": "Login",
"forget_password": "Find password",
"login_failed": "Login failed",
"login_success": "Login successful",
"password_condition": "Password maximum 60 characters",
"policy_tip": "By useing, you agree to our",
"privacy": "Privacy policy",
"register": "Register",
"root_password_placeholder": "The root user password is the value of the environment variable DEFAULT_ROOT_PSW",
"terms": "Terms",
"use_root_login": "Log in as root user"
}
\ No newline at end of file
{
"app_key_tips": "These keys are already linked to the current application. Check the documentation for detailed usage.",
"basic_info": "Basic Info",
"copy_link_hint": "Copy the link below to the specified location",
"create_api_key": "Create New Key",
"create_link": "Create Link",
"edit_api_key": "Edit Key Details",
"edit_feishu_bot": "Edit Feishu Bot",
"edit_link": "Edit",
"feishu_api": "Feishu API",
"feishu_bot": "Feishu Bot",
"feishu_bot_desc": "Connect to Feishu Bot directly via API",
"key_alias": "Key alias, for display only",
"key_tips": "You can use the API key to access specific interfaces (cannot access the application, use the in-app API key for that)",
"link_name": "Share Link Name",
"new_feishu_bot": "Add New Feishu Bot",
"official_account": {
"create_modal_title": "Create WeChat Official Account Integration",
"desc": "Connect to WeChat Official Account directly via API",
"edit_modal_title": "Edit WeChat Official Account Integration",
"name": "WeChat Official Account Integration",
"params": "WeChat Official Account Parameters"
},
"publish_name": "Name",
"qpm_is_empty": "QPM cannot be empty",
"qpm_tips": "Maximum number of queries per minute per IP",
"request_address": "Request URL",
"show_share_link_modal_title": "Get Started",
"token_auth": "Token Authentication",
"token_auth_tips": "Token authentication server URL. If provided, a request will be sent to the specified server for authentication before each conversation.",
"token_auth_use_cases": "View Token Authentication Guide",
"wecom": {
"api": "WeCom API",
"bot": "WeCom Bot",
"bot_desc": "Connect to WeCom Bot directly via API",
"create_modal_title": "Create WeCom Bot",
"edit_modal_title": "Edit WeCom Bot",
"title": "Publish to WeCom Bot"
}
}
\ No newline at end of file
{
"bill": {
"balance": "Balance",
"buy_plan": "Purchase Plan",
"contact_customer_service": "Contact Support",
"conversion": "Conversion",
"convert_error": "Conversion Failed",
"convert_success": "Conversion Successful",
"current_token_price": "Current Token Price",
"not_need_invoice": "Balance payment, invoice not available",
"price": "Price",
"renew_plan": "Renew Plan",
"standard_valid_tip": "Plan Usage Rules: Higher-level plans will be used first. Unused plans will be activated later.",
"token_expire_1year": "Tokens are valid for one year",
"tokens": "Tokens",
"use_balance": "Use Balance",
"use_balance_hint": "Due to system upgrade, the 'Auto-renewal from balance' mode is canceled, and the balance recharge option is closed. Your balance can be used to purchase tokens.",
"valid_time": "Effective Time",
"you_can_convert": "You can convert",
"yuan": "Yuan"
},
"bill_and_invoices": "Bill & Invoice",
"bind_inform_account_error": "Failed to Bind Notification Account",
"bind_inform_account_success": "Notification Account Bound Successfully",
"delete": {
"admin_failed": "Failed to Delete Admin",
"admin_success": "Admin Deleted Successfully"
},
"has_chosen": "Selected",
"individuation": "Individuation",
"login": {
"error": "Login Error",
"password_condition": "Password can be up to 60 characters",
"success": "Login Successful"
},
"name": "Name",
"notice": "Notice",
"notification": {
"Bind Notification Pipe Hint": "Please bind a notification receiving account to ensure you receive notifications such as plan expiration reminders, ensuring your service runs smoothly.",
"remind_owner_bind": "Please remind the creator to bind a notification account"
},
"operations": "Actions",
"password": {
"code_required": "Verification Code Required",
"code_send_error": "Failed to Send Verification Code",
"code_sended": "Verification Code Sent",
"confirm": "Confirm Password",
"email_phone_error": "Invalid Email/Phone Number Format",
"email_phone_void": "Email/Phone Number Cannot Be Empty",
"get_code": "Get Verification Code",
"get_code_again": "Get Again in s",
"new_password": "New Password (4-20 characters)",
"not_match": "Passwords Do Not Match",
"password_condition": "Password must be between 4 and 20 characters",
"password_required": "Password Cannot Be Empty",
"retrieve": "Retrieve Password",
"retrieved": "Password Retrieved",
"retrieved_account": "Retrieve {{account}} Account",
"to_login": "Go to Login",
"verification_code": "Verification Code"
},
"permission": {
"Manage": "Admin",
"Manage tip": "Team admin with full permissions",
"Read": "Read Only",
"Read desc": "Members can only read related resources, cannot create new resources",
"Write": "Write",
"Write tip": "In addition to read access, can create new resources",
"only_collaborators": "Collaborators Only",
"team_read": "Team Read Access",
"team_write": "Team Write Access"
},
"permission_des": {
"manage": "Can create resources, invite, and delete members",
"read": "Members can only read related resources and cannot create new resources.",
"write": "In addition to readable resources, you can also create new resources"
},
"permissions": "Permissions",
"personal_information": "Me",
"personalization": "Personalization",
"promotion_records": "Promotion",
"register": {
"confirm": "Confirm Registration",
"register_account": "Register {{account}} Account",
"success": "Registration Successful",
"to_login": "Already have an account? Login"
},
"search_user": "Search Username",
"sign_out": "Sign out",
"synchronization": {
"button": "Sync Now",
"placeholder": "Enter Sync Tag",
"title": "Enter the sync tag link and click the sync button to synchronize"
},
"team": {
"Add manager": "Add Admin",
"add_collaborator": "Add Collaborator",
"manage_collaborators": "Manage Collaborators",
"no_collaborators": "No Collaborators"
},
"usage": {
"feishu": "Feishu",
"official_account": "Official Account",
"share": "Share Link",
"wecom": "WeCom"
},
"usage_record": "Usages"
}
\ No newline at end of file
{
"Code": "Code",
"about_xxx_question": "Question regarding xxx",
"add_new_input": "Add New Input",
"append_application_reply_to_history_as_new_context": "Append the application's reply to the history as new context",
"application_call": "Application Call",
"assigned_reply": "Assigned Reply",
"choose_another_application_to_call": "Select another application to call",
"classification_result": "Classification Result",
"code": {
"Reset template": "Reset Template",
"Reset template confirm": "Confirm reset code template? This will reset all inputs and outputs to template values. Please save your current code."
},
"code_execution": "Code Execution",
"collection_metadata_filter": "Collection Metadata Filter",
"complete_extraction_result": "Complete Extraction Result",
"complete_extraction_result_description": "A JSON string, e.g., {\"name\":\"YY\",\"Time\":\"2023/7/2 18:00\"}",
"concatenation_result": "Concatenation Result",
"concatenation_text": "Concatenation Text",
"condition_checker": "Condition Checker",
"confirm_delete_field_tip": "Confirm delete this field?",
"contains": "Contains",
"content_to_retrieve": "Content to Retrieve",
"content_to_search": "Content to Search",
"create_link_error": "Error creating link",
"custom_feedback": "Custom Feedback",
"custom_input": "Custom Input",
"custom_plugin_output": "Custom Plugin Output",
"delete_api": "Confirm delete this API key? The key will be invalid immediately after deletion, but the corresponding conversation logs will not be deleted. Please confirm!",
"dynamic_input_description": "Receive the output value of the previous node as a variable, which can be used by Laf request parameters.",
"dynamic_input_description_concat": "You can reference the output of other nodes as variables for text concatenation. Type / to invoke the variable list.",
"edit_input": "Edit Input",
"end_with": "Ends With",
"error_info_returns_empty_on_success": "Error information of code execution, returns empty on success",
"execute_a_simple_script_code_usually_for_complex_data_processing": "Execute a simple script code, usually for complex data processing.",
"execute_different_branches_based_on_conditions": "Execute different branches based on conditions.",
"execution_error": "Execution Error",
"extraction_requirements_description": "Extraction Requirements Description",
"extraction_requirements_description_detail": "Provide AI with some background knowledge or requirements to guide it in completing the task better.\\nThis input box can use global variables.",
"extraction_requirements_placeholder": "For example: \\n1. The current time is: {{cTime}}. You are a lab reservation assistant, and your task is to help users reserve a lab by extracting the corresponding reservation information from the text.\\n2. You are a Google search assistant, and you need to extract suitable search terms from the text.",
"feedback_text": "Feedback Text",
"field_description": "Field Description",
"field_description_placeholder": "Describe the function of this input field. If it is a tool call parameter, this description will affect the quality of the model generation.",
"field_name_already_exists": "Field name already exists",
"field_required": "Required",
"field_used_as_tool_input": "Used as Tool Call Parameter",
"filter_description": "Currently supports filtering by tags and creation time. Fill in the format as follows:\n{\n \"tags\": {\n \"$and\": [\"Tag 1\",\"Tag 2\"],\n \"$or\": [\"When there are $and tags, and is effective, or is not effective\"]\n },\n \"createTime\": {\n \"$gte\": \"YYYY-MM-DD HH:mm format, collection creation time greater than this time\",\n \"$lte\": \"YYYY-MM-DD HH:mm format, collection creation time less than this time, can be used with $gte\"\n }\n}",
"full_field_extraction": "Full Field Extraction",
"full_field_extraction_description": "Returns true when all fields are fully extracted (success includes model extraction or using default values)",
"full_response_data": "Full Response Data",
"greater_than": "Greater Than",
"greater_than_or_equal_to": "Greater Than or Equal To",
"greeting": "Greeting",
"http_raw_response_description": "Raw HTTP response. Only accepts string or JSON type response data.",
"http_request": "HTTP Request",
"http_request_error_info": "HTTP request error information, returns empty on success",
"ifelse": {
"Input value": "Input Value",
"Select value": "Select Value"
},
"input_description": "Field Description",
"input_variable_list": "Type / to invoke variable list",
"intro_assigned_reply": "This module can directly reply with a specified content. Commonly used for guidance or prompts. Non-string content will be converted to string for output.",
"intro_custom_feedback": "When this module is triggered, a feedback will be added to the current conversation record. It can be used to automatically record conversation effects, etc.",
"intro_custom_plugin_output": "Custom configuration of external output. When using plugins, only the custom configured output is exposed.",
"intro_http_request": "Can send an HTTP request to perform more complex operations (network search, database query, etc.)",
"intro_knowledge_base_search_merge": "Can merge multiple Dataset search results for output. Uses RRF merging method for final sorting output.",
"intro_laf_function_call": "Can call cloud functions under the Laf account.",
"intro_plugin_input": "Can configure what inputs the plugin needs and use these inputs to run the plugin.",
"intro_question_classification": "Determine the type of question based on the user's history and current question. Multiple question types can be added. Below is a template example:\nType 1: Greeting\nType 2: Questions about product 'usage'\nType 3: Questions about product 'purchase'\nType 4: Other questions",
"intro_question_optimization": "Using question optimization can improve the accuracy of Dataset searches during continuous conversations. After using this function, AI will first construct one or more new search terms based on the context, which are more conducive to Dataset searches. This module is already built into the Dataset search module. If you only perform a single Dataset search, you can directly use the built-in completion function of the Dataset.",
"intro_text_concatenation": "Can process and output fixed or incoming text. Non-string type data will be converted to string type.",
"intro_text_content_extraction": "Can extract specified data from text, such as SQL statements, search keywords, code, etc.",
"intro_tool_call_termination": "This module needs to be configured for tool calls. When this module is executed, the current tool call will be forcibly terminated, and AI will no longer answer questions based on the tool call results.",
"is_empty": "Is Empty",
"is_equal_to": "Is Equal To",
"is_not_empty": "Is Not Empty",
"is_not_equal": "Is Not Equal",
"judgment_result": "Judgment Result",
"knowledge_base_reference": "Dataset Reference",
"knowledge_base_search_merge": "Dataset Search Merge",
"laf_function_call_test": "Laf Function Call (Test)",
"length_equal_to": "Length Equal To",
"length_greater_than": "Length Greater Than",
"length_greater_than_or_equal_to": "Length Greater Than or Equal To",
"length_less_than": "Length Less Than",
"length_less_than_or_equal_to": "Length Less Than or Equal To",
"length_not_equal_to": "Length Not Equal To",
"less_than": "Less Than",
"less_than_or_equal_to": "Less Than or Equal To",
"max_dialog_rounds": "Maximum Number of Dialog Rounds",
"max_tokens": "Maximum Tokens",
"mouse_priority": "Mouse first",
"new_context": "New Context",
"not_contains": "Does Not Contain",
"only_the_reference_type_is_supported": "Only reference type is supported",
"optional_value_type": "Optional Value Type",
"optional_value_type_tip": "You can specify one or more data types. When dynamically adding fields, users can only select the configured types.",
"other_questions": "Other Questions",
"pan_priority": "Touchpad first",
"pass_returned_object_as_output_to_next_nodes": "Pass the object returned in the code as output to the next nodes. The variable name needs to correspond to the return key.",
"plugin": {
"Instruction_Tip": "You can configure an instruction to explain the purpose of the plugin. This instruction will be displayed each time the plugin is used. Supports standard Markdown syntax.",
"Instructions": "Instructions"
},
"plugin_input": "Plugin Input",
"question_classification": "Question Classification",
"question_optimization": "Question Optimization",
"quote_num": "Quote {{num}}",
"raw_response": "Raw Response",
"regex": "Regex",
"reply_text": "Reply Text",
"request_error": "Request Error",
"response": {
"Code log": "Code Log",
"Custom inputs": "Custom Inputs",
"Custom outputs": "Custom Outputs",
"Error": "Error",
"Read file result": "Read File Result",
"read files": "Read Files"
},
"select_an_application": "Select an Application",
"select_another_application_to_call": "You can choose another application to call",
"special_array_format": "Special array format, returns an empty array when the search result is empty.",
"start_with": "Starts With",
"target_fields_description": "A target field consists of 'description' and 'key'. Multiple target fields can be extracted.",
"template": {
"ai_chat": "AI Chat",
"ai_chat_intro": "AI Large Model Chat",
"dataset_search": "Dataset Search",
"dataset_search_intro": "Use 'semantic search' and 'full-text search' capabilities to find potentially relevant reference content from the 'Dataset'.",
"system_config": "System Configuration",
"tool_call": "Tool Call",
"tool_call_intro": "Automatically select one or more functional blocks for calling through the AI model, or call plugins.",
"workflow_start": "Workflow Start"
},
"text_concatenation": "Text Concatenation",
"text_content_extraction": "Text Content Extraction",
"text_to_extract": "Text to Extract",
"these_variables_will_be_input_parameters_for_code_execution": "These variables will be input parameters for code execution",
"tool_call_termination": "Tool Call Termination",
"tool_input": "Tool Input",
"trigger_after_application_completion": "Will be triggered after the application is fully completed",
"update_link_error": "Error updating link",
"update_specified_node_output_or_global_variable": "Can update the output value of a specified node or update global variables",
"use_user_id": "User ID",
"user_question": "User Question",
"variable_picker_tips": "Type node name or variable name to search",
"variable_update": "Variable Update",
"workflow": {
"My edit": "My Edit",
"Switch_success": "Switch Successful",
"Team cloud": "Team Cloud",
"exit_tips": "Your changes have not been saved. 'Exit directly' will not save your edits."
}
}
import { I18nKeyFunction } from '../types/i18next';
export const i18nT: I18nKeyFunction = (key) => key;
{
"Run": "运行",
"ai_settings": "AI 配置",
"all_apps": "全部应用",
"app": {
"Version name": "版本名称",
"modules": {
"click to update": "点击更新",
"has new version": "有新版本"
},
"version_back": "回到初始状态",
"version_copy": "副本",
"version_current": "当前版本",
"version_initial": "初始版本",
"version_initial_copy": "副本-初始状态",
"version_name_tips": "版本名称不能为空",
"version_past": "发布过",
"version_publish_tips": "该版本将被保存至团队云端,同步给整个团队,同时更新所有发布渠道的应用版本"
},
"app_detail": "应用详情",
"chat_debug": "调试预览",
"chat_logs": "对话日志",
"chat_logs_tips": "日志会记录该应用的在线、分享和 API(需填写 chatId)对话记录",
"config_file_upload": "点击配置文件上传规则",
"confirm_copy_app_tip": "系统将为您创建一个相同配置应用,请确认!",
"confirm_del_app_tip": "确认删除该应用及其所有聊天记录?",
"confirm_delete_folder_tip": "确认删除该文件夹?将会删除它下面所有应用及对应的聊天记录,请确认!",
"copy_one_app": "创建副本",
"create_copy_success": "创建副本成功",
"create_empty_app": "创建空白应用",
"create_empty_plugin": "创建空白插件",
"create_empty_workflow": "创建空白工作流",
"cron": {
"every_day": "每天执行",
"every_month": "每月执行",
"every_week": "每周执行",
"interval": "间隔执行"
},
"current_settings": "当前配置",
"day": "日",
"document_quote": "文档引用",
"document_quote_tip": "通常用于接受用户上传的文档内容(这需要文档解析),也可以用于引用其他字符串数据。",
"document_upload": "文档上传",
"edit_app": "编辑应用",
"edit_info": "编辑信息",
"execute_time": "执行时间",
"export_config_successful": "已复制配置,自动过滤部分敏感信息,请注意检查是否仍有敏感数据",
"export_configs": "导出配置",
"feedback_count": "用户反馈",
"file_recover": "文件将覆盖当前内容",
"file_upload": "文件上传",
"file_upload_tip": "开启后,可以上传文档/图片。文档保留7天,图片保留15天。使用该功能可能产生较多额外费用。为保证使用体验,使用该功能时,请选择上下文长度较大的AI模型。",
"go_to_chat": "去对话",
"go_to_run": "去运行",
"image_upload": "图片上传",
"image_upload_tip": "请确保选择可处理图片的视觉模型",
"import_configs": "导入配置",
"import_configs_failed": "导入配置失败,请确保配置正常!",
"import_configs_success": "导入成功",
"initial_form": "初始状态",
"interval": {
"12_hours": "每12小时",
"2_hours": "每2小时",
"3_hours": "每3小时",
"4_hours": "每4小时",
"6_hours": "每6小时",
"per_hour": "每小时"
},
"intro": "是一个大模型应用编排系统,提供开箱即用的数据处理、模型调用等能力,可以快速的构建知识库并通过 Flow 可视化进行工作流编排,实现复杂的知识库场景!",
"llm_not_support_vision": "该模型不支持图片识别",
"llm_use_vision": "启用图片识别",
"llm_use_vision_tip": "启用图片识别后,该模型会自动接收来自“对话框上传”的图片,以及“用户问题”中的图片链接。",
"logs_empty": "还没有日志噢~",
"logs_message_total": "消息总数",
"logs_title": "标题",
"logs_chat_user": "使用者",
"mark_count": "标注答案数量",
"module": {
"Confirm Sync": "将会更新至最新的模板配置,不存在模板中的字段将会被删除(包括所有自定义字段),建议您先复制一份节点,再更新原来节点的版本。",
"Custom Title Tip": "该标题名字会展示在对话过程中",
"No Modules": "没找到插件",
"type": "\"{{type}}\"类型\n{{description}}"
},
"modules": {
"Title is required": "模块名不能为空"
},
"month": {
"unit": "号"
},
"move_app": "移动应用",
"not_json_file": "请选择JSON文件",
"paste_config": "粘贴配置",
"or_drag_JSON": "或拖入JSON文件",
"plugin_cost_per_times": "{{cost}}/次",
"plugin_dispatch": "插件调用",
"plugin_dispatch_tip": "给模型附加额外的能力,具体调用哪些插件,将由模型自主决定。\n若选择了插件,知识库调用将自动作为一个特殊的插件。",
"publish_channel": "发布渠道",
"publish_success": "发布成功",
"saved_success": "保存成功",
"search_app": "搜索应用,按Enter开始搜索",
"setting_app": "应用配置",
"setting_plugin": "插件配置",
"template": {
"simple_robot": "简易机器人",
"standard_template": "标准模板",
"standard_template_des": "标准提示词,用于结构不固定的知识库。",
"qa_template": "问答模板",
"qa_template_des": "适合 QA 问答结构的知识库,可以让AI较为严格的按预设内容回答",
"standard_strict": "标准严格模板",
"standard_strict_des": "在标准模板基础上,对模型的回答做更严格的要求。",
"hard_strict": "严格问答模板",
"hard_strict_des": "在问答模板基础上,对模型的回答做更严格的要求。"
},
"templateMarket": {
"Search_template": "搜索模板",
"Template_market": "模板市场",
"Use": "使用",
"no_intro": "还没有介绍~",
"templateTags": {
"Image_generation": "图片生成",
"Office_services": "办公服务",
"Recommendation": "推荐",
"Roleplay": "角色扮演",
"Web_search": "联网搜索",
"Writing": "文本创作"
}
},
"template_market": "模板市场",
"template_market_description": "在模板市场探索更多玩法,配置教程与使用引导,带你理解并上手各种应用",
"template_market_empty_data": "找不到合适的模板",
"time_zone": "时区",
"tool_input_param_tip": "该插件正常运行需要配置相关信息",
"transition_to_workflow": "转成工作流",
"transition_to_workflow_create_new_placeholder": "创建一个新的应用,而不是修改当前应用",
"transition_to_workflow_create_new_tip": "转化成工作流后,将无法转化回简易模式,请确认!",
"type": {
"All": "全部",
"Create http plugin tip": "通过 OpenAPI Schema 批量创建插件,兼容 GPTs 格式",
"Create one plugin tip": "可以自定义输入和输出的工作流,通常用于封装重复使用的工作流",
"Create plugin bot": "创建插件",
"Create simple bot": "创建简易应用",
"Create simple bot tip": "通过填表单形式,创建简单的 AI 应用,适合新手",
"Create workflow bot": "创建工作流",
"Create workflow tip": "通过低代码的方式,构建逻辑复杂的多轮对话 AI 应用,推荐高级玩家使用",
"Http plugin": "HTTP 插件",
"Plugin": "插件",
"Simple bot": "简易应用",
"Workflow bot": "工作流"
},
"upload_file_max_amount": "最大文件数量",
"upload_file_max_amount_tip": "1.单次上传文件的最大数量。\n2.对话窗口记忆的最大文件数量:每轮对话会自动获取历史中的文件,超出范围的文件会被遗忘。",
"variable": {
"select type_desc": "可以定义一个无需用户填写的全局变量。\n该变量的值可以来自于 API 接口,分享链接的 Query 或通过【变量更新】模块进行赋值。",
"textarea_type_desc": "允许用户最多输入4000字的对话框。"
},
"version": {
"Revert success": "回滚成功"
},
"vision_model_title": "启用图片识别",
"week": {
"Friday": "星期五",
"Monday": "星期一",
"Saturday": "星期六",
"Sunday": "星期日",
"Thursday": "星期四",
"Tuesday": "星期二",
"Wednesday": "星期三"
},
"workflow": {
"Input guide": "填写说明",
"file_url": "文档链接",
"read_files": "文档解析",
"form_input": "表单输入",
"form_input_description_placeholder": "例如:\n补充您的信息",
"form_input_tip": "该模块可以配置多种输入,引导用户输入特定内容。",
"input_description_tip": "你可以添加一段说明文字,用以向用户说明需要输入的内容",
"read_files_result": "文档解析结果",
"read_files_result_desc": "文档原文,由文件名和文档内容组成,多个文件之间通过横线隔开。",
"read_files_tip": "解析对话中所有上传的文档,并返回对应文档内容",
"select_description": "说明文字",
"select_description_placeholder": "例如: \n冰箱里是否有西红柿?",
"select_description_tip": "你可以添加一段说明文字,用以向用户说明每个选项代表的含义。",
"select_result": "选择的结果",
"template": {
"communication": "通信"
},
"user_file_input": "文件链接",
"user_file_input_desc": "用户上传的文档和图片链接",
"user_select": "用户选择",
"user_select_tip": "该模块可配置多个选项,以供对话时选择。不同选项可导向不同工作流支线"
},
"permission": {
"des": {
"read": "可使用该应用进行对话",
"write": "可查看和编辑应用",
"manage": "写权限基础上,可配置发布渠道、查看对话日志、分配该应用权限"
}
}
}
{
"Delete_all": "清空词库",
"ai_reasoning": "思考过程",
"chat_history": "聊天记录",
"chat_input_guide_lexicon_is_empty": "还没有配置词库",
"citations": "{{num}}条引用",
"click_contextual_preview": "点击查看上下文预览",
"config_input_guide": "配置输入引导",
"config_input_guide_lexicon": "配置词库",
"config_input_guide_lexicon_title": "配置词库",
"content_empty": "内容为空",
"contextual": "{{num}}条上下文",
"contextual_preview": "上下文预览 {{num}} 条",
"csv_input_lexicon_tip": "仅支持 CSV 批量导入,点击下载模板",
"custom_input_guide_url": "自定义词库地址",
"delete_all_input_guide_confirm": "确定要清空输入引导词库吗?",
"empty_directory": "这个目录已经没东西可选了~",
"file_amount_over": "超出最大文件数量 {{max}}",
"file_input": "系统文件",
"file_input_tip": "可通过【插件开始】节点的“文件链接”获取对应文件的链接",
"in_progress": "进行中",
"input_guide": "输入引导",
"input_guide_lexicon": "词库",
"input_guide_tip": "可以配置一些预设的问题。在用户输入问题时,会从这些预设问题中获取相关问题进行提示。",
"insert_input_guide,_some_data_already_exists": "有重复数据,已自动过滤,共插入 {{len}} 条数据",
"is_chatting": "正在聊天中...请等待结束",
"items": "条",
"module_runtime_and": "模块运行时间和",
"multiple_AI_conversations": "多组 AI 对话",
"new_input_guide_lexicon": "新词库",
"no_workflow_response": "没有运行数据",
"plugins_output": "插件输出",
"question_tip": "从上到下,为各个模块的响应顺序",
"response": {
"node_inputs": "节点输入"
},
"select": "选择",
"select_file": "选择文件",
"select_img": "选择图片",
"select_file_img": "上传文件/图片",
"stream_output": "流输出",
"view_citations": "查看引用",
"web_site_sync": "Web站点同步"
}
\ No newline at end of file
{
"App": "应用",
"Export": "导出",
"FAQ": {
"ai_point_a": "每次调用AI模型时,都会消耗一定的费用。具体的计算标准可参考上方的“费用计算标准”。\nToken计算采用GPT3.5相同公式,1Token≈0.7中文字符≈0.9英文单词,连续出现的字符可能被认为是1个Tokens。",
"ai_point_expire_a": "会过期。当前套餐过期后,AI积分将会清空,并更新为新套餐的AI积分。年度套餐的AI积分时长为1年,而不是每个月。",
"ai_point_expire_q": "AI积分会过期么?",
"ai_point_q": "什么是AI积分?",
"check_subscription_a": "账号-个人信息-套餐详情-使用情况。您可以查看所拥有套餐的生效和到期时间。当付费套餐到期后将自动切换免费版。",
"check_subscription_q": "在哪里查看已订阅的套餐?",
"dataset_compute_a": "1条知识库存储等于1条知识库索引。一条知识库数据可以包含1条或多条知识库索引。增强训练中,1条数据会生成5条索引。",
"dataset_compute_q": "知识库存储怎么计算?",
"dataset_index_a": "不会。但知识库索引超出时,无法插入和更新知识库内容。",
"dataset_index_q": "知识库索引超出会删除么?",
"free_user_clean_a": "免费版团队(免费版且未购买额外套餐)连续 30 天未登录系统,系统会自动清除该团队下所有知识库内容。",
"free_user_clean_q": "免费版数据会清除么?",
"package_overlay_a": "可以的。每次购买的资源包都是独立的,在其有效期内将会叠加使用。AI积分会优先扣除最先过期的资源包。",
"package_overlay_q": "额外资源包可以叠加么?",
"switch_package_a": "套餐使用规则为优先使用更高级的套餐,因此,购买的新套餐若比当前套餐更高级,则新套餐立即生效:否则将继续使用当前套餐。",
"switch_package_q": "是否切换订阅套餐?"
},
"Folder": "文件夹",
"Login": "登录",
"Submit": "提交",
"Move": "移动",
"Name": "名称",
"None": "无",
"Rename": "重命名",
"Resume": "恢复",
"Running": "运行中",
"UnKnow": "未知",
"add_new_param": "新增参数",
"Warning": "提示",
"add_new": "新增",
"back": "返回",
"chose_condition": "选择条件",
"chosen": "已选",
"classification": "分类",
"click_to_resume": "点击恢复",
"code_editor": "代码编辑",
"code_error": {
"app_error": {
"invalid_app_type": "错误的应用类型",
"invalid_owner": "非法的应用所有者",
"not_exist": "应用不存在",
"un_auth_app": "无权操作该应用"
},
"chat_error": {
"un_auth": "没有权限操作此对话记录"
},
"error_code": {
"400": "请求失败",
"401": "无访问权限",
"403": "紧张访问",
"404": "请求不存在",
"405": "请求方法错误",
"406": "请求格式错误",
"410": "资源已删除",
"422": "验证错误",
"500": "服务器发生错误",
"502": "网关错误",
"503": "服务器暂时过载或正在维护",
"504": "网关超时"
},
"error_message": {
"403": "凭证错误或权限不足",
"510": "账户余额不足",
"511": "没有权限操作此模型",
"513": "没有权限读取该文件",
"514": "Api Key 不合法"
},
"openapi_error": {
"api_key_not_exist": "Api Key 不存在",
"exceed_limit": "最多 10 组 API 密钥",
"un_auth": "无权操作该 Api Key"
},
"outlink_error": {
"invalid_link": "分享链接无效",
"link_not_exist": "分享链接不存在",
"un_auth_user": "身份校验失败"
},
"plugin_error": {
"not_exist": "插件不存在",
"un_auth": "无权操作该插件"
},
"system_error": {
"community_version_num_limit": "超出数量限制"
},
"team_error": {
"ai_points_not_enough": "",
"app_amount_not_enough": "应用数量已达上限~",
"dataset_amount_not_enough": "知识库数量已达上限~",
"dataset_size_not_enough": "知识库容量不足,请先扩容~",
"over_size": "error.team.overSize",
"plugin_amount_not_enough": "插件数量已达上限~",
"re_rank_not_enough": "无权使用检索重排~",
"un_auth": "缺少权限",
"website_sync_not_enough": "无权使用Web站点同步~"
},
"token_error_code": {
"403": "登录状态无效,请重新登录"
},
"user_error": {
"balance_not_enough": "账号余额不足~",
"bin_visitor": "您的身份校验未通过",
"bin_visitor_guest": "您当前身份为游客,无权操作",
"un_auth_user": "找不到该用户"
}
},
"common": {
"Action": "操作",
"Add": "添加",
"Add New": "新增",
"Add Success": "添加成功",
"Add_new_input": "新增输入",
"All": "全部",
"Cancel": "取消",
"Choose": "选择",
"Close": "关闭",
"Config": "配置",
"Confirm": "确认",
"Confirm Create": "确认创建",
"Confirm Import": "确认导入",
"Confirm Move": "移动到这",
"Confirm Update": "确认更新",
"Confirm to leave the page": "确认离开该页面?",
"Continue_Adding": "继续添加",
"Copy": "复制",
"Copy Successful": "复制成功",
"Copy_failed": "复制失败,请手动复制",
"Create Failed": "创建异常",
"Create New": "新建",
"Create Success": "创建成功",
"Create Time": "创建时间",
"Creating": "创建中",
"Custom Title": "自定义标题",
"Delete": "删除",
"Delete Failed": "删除失败",
"Delete Success": "删除成功",
"Delete Warning": "删除警告",
"Delete folder": "删除文件夹",
"Detail": "详情",
"Documents": "文档",
"Done": "完成",
"Edit": "编辑",
"Exit": "退出",
"Exit Directly": "直接退出",
"Expired Time": "过期时间",
"File": "文件",
"Finish": "完成",
"Import": "导入",
"Import failed": "导入失败",
"Import success": "导入成功",
"Input": "输入",
"Input folder description": "文件夹描述",
"Input name": "取个名字",
"Intro": "介绍",
"Last Step": "上一步",
"Last use time": "最后使用时间",
"Load Failed": "加载失败",
"Loading": "加载中...",
"More": "更多",
"Move": "移动",
"MultipleRowSelect": {
"No data": "没有可选值"
},
"Name": "名称",
"Next Step": "下一步",
"No more data": "没有更多了~",
"Not open": "未开启",
"OK": "好的",
"Open": "打开",
"Operation": "操作",
"Other": "其他",
"Output": "输出",
"Params": "参数",
"Password inconsistency": "两次密码不一致",
"Permission": "权限",
"Please Input Name": "请输入名称",
"Read document": "查看文档",
"Read intro": "查看说明",
"Remove": "移除",
"Rename": "重命名",
"Request Error": "请求异常",
"Require Input": "必填",
"Restart": "重新开始",
"Role": "权限",
"Root folder": "根目录",
"Run": "运行",
"Save": "保存",
"Save Failed": "保存异常",
"Save Success": "保存成功",
"Save_and_exit": "保存并退出",
"Search": "搜索",
"Select File Failed": "选择文件异常",
"Select template": "选择模板",
"Set Avatar": "点击设置头像",
"Set Name": "取个名字",
"Setting": "设置",
"Status": "状态",
"Submit failed": "提交失败",
"Success": "成功",
"Sync success": "同步成功",
"Team": "团队",
"Team Tags Set": "标签",
"Un used": "未使用",
"UnKnow": "未知",
"UnKnow Source": "未知来源",
"Unlimited": "无限制",
"Update": "更新",
"Update Failed": "更新异常",
"Update Success": "更新成功",
"Update Successful": "更新成功",
"Username": "用户名",
"StaionName": "岗位信息",
"Device": "设备",
"StaionId": "岗位ID",
"Waiting": "等待中",
"Warning": "警告",
"Website": "网站",
"all_result": "完整结果",
"avatar": {
"Select Avatar": "点击选择头像",
"Select Failed": "选择头像异常"
},
"base_config": "基础配置",
"choosable": "可选",
"confirm": {
"Common Tip": "操作确认"
},
"copy_to_clipboard": "复制到剪贴板",
"course": {
"Read Course": "查看教程"
},
"empty": {
"Common Tip": "没有什么数据噢~"
},
"error": {
"Select avatar failed": "头像选择异常",
"unKnow": "出现了点意外~"
},
"export_to_json": "导出为 JSON",
"failed": "失败",
"folder": {
"Drag Tip": "点我可拖动",
"Move Success": "移动成功",
"Move to": "移动到",
"No Folder": "没有子目录了,就放这里吧",
"Open folder": "打开文件夹",
"Root Path": "根目录",
"empty": "这个目录已经没东西可选了~",
"open_dataset": "打开知识库"
},
"have_done": "已完成",
"input": {
"Repeat Value": "有重复的值"
},
"is_requesting": "请求中……",
"jsonEditor": {
"Parse error": "JSON 可能有误,请仔细检查"
},
"json_config": "JSON 配置",
"link": {
"UnValid": "无效的链接"
},
"month": "月",
"name_is_empty": "名称不能为空",
"no_intro": "暂无介绍",
"not_support": "不支持",
"page_center": "页面居中",
"redo_tip": "恢复 ctrl shift z",
"redo_tip_mac": "恢复 ⌘ shift z",
"request_end": "已加载全部",
"request_more": "点击加载更多",
"speech": {
"error tip": "语音转文字失败",
"not support": "您的浏览器不支持语音输入"
},
"submit_success": "提交成功",
"submitted": "已提交",
"support": "支持",
"system": {
"Commercial version function": "该功能暂未开放",
"Help Chatbot": "机器人助手",
"Use Helper": "使用帮助"
},
"ui": {
"textarea": {
"Magnifying": "放大"
}
},
"undo_tip": "撤销 ctrl z",
"undo_tip_mac": "撤销 ⌘ z ",
"upload_file": "上传文件",
"zoomin_tip": "缩小 ctrl -",
"zoomin_tip_mac": "缩小 ⌘ -",
"zoomout_tip": "放大 ctrl +",
"zoomout_tip_mac": "放大 ⌘ +"
},
"comon": {
"Continue_Adding": "继续添加"
},
"confirm_choice": "确认选择",
"contribute_app_template": "贡献模板",
"core": {
"Chat": "对话",
"Max Token": "单条数据上限",
"ai": {
"AI settings": "AI 配置",
"Ai point price": "费用消耗",
"Max context": "最大上下文",
"Model": "AI 模型",
"Not deploy rerank model": "未部署重排模型",
"Prompt": "提示词",
"Support tool": "函数调用",
"model": {
"Dataset Agent Model": "文件处理模型",
"Vector Model": "索引模型",
"doc_index_and_dialog": "文档索引 & 对话索引"
}
},
"app": {
"Ai response": "返回 AI 内容",
"reasoning_response": "输出思考",
"Api request": "API 访问",
"Api request desc": "通过 API 接入到已有系统中,或企微、飞书等",
"App intro": "应用介绍",
"Chat Variable": "对话框变量",
"Config schedule plan": "配置定时执行",
"Config whisper": "配置语音输入",
"Interval timer config": "定时执行配置",
"Interval timer run": "定时执行",
"Interval timer tip": "可定时执行应用",
"Make a brief introduction of your app": "给你的 AI 应用一个介绍",
"Max histories": "聊天记录数量",
"Max tokens": "回复上限",
"Name and avatar": "头像 & 名称",
"Publish": "发布",
"Publish Confirm": "确认发布应用?会立即更新所有发布渠道的应用状态。",
"Publish app tip": "发布应用后,所有发布渠道将会立即使用该版本",
"Question Guide": "猜你想问",
"Question Guide Tip": "对话结束后,会为生成 3 个引导性问题。",
"Quote prompt": "引用模板提示词",
"Quote templates": "引用内容模板",
"Random": "发散",
"Search team tags": "搜索标签",
"Select TTS": "选择语音播放模式",
"Select app from template": "从模板中选择",
"Select quote template": "选择引用提示模板",
"Set a name for your app": "给应用设置一个名称",
"Setting ai property": "点击配置 AI 模型相关属性",
"Share link": "免登录窗口",
"Share link desc": "分享链接给其他用户,无需登录即可直接进行使用",
"Share link desc detail": "可以直接分享该模型给其他用户去进行对话,对方无需登录即可直接进行对话。注意,这个功能会消耗你账号的余额,请保管好链接!",
"TTS": "语音播放",
"TTS Tip": "开启后,每次对话后可使用语音播放功能。使用该功能可能产生额外费用。",
"TTS start": "朗读内容",
"Team tags": "团队标签",
"Temperature": "温度",
"Tool call": "工具调用",
"ToolCall": {
"No plugin": "没有可用的插件",
"Parameter setting": "输入参数",
"System": "系统",
"Team": "团队"
},
"Welcome Text": "对话开场白",
"Whisper": "语音输入",
"Whisper config": "语音输入配置",
"deterministic": "严谨",
"edit": {
"Prompt Editor": "提示词编辑",
"Query extension background prompt": "对话背景描述",
"Query extension background tip": "描述当前对话的范围,便于 AI 为当前问题进行补全和扩展。填写的内容,通常为该助手"
},
"edit_content": "应用信息编辑",
"error": {
"App name can not be empty": "应用名不能为空",
"Get app failed": "获取应用异常"
},
"feedback": {
"Custom feedback": "自定义反馈",
"close custom feedback": "关闭反馈"
},
"have_saved": "已保存",
"logs": {
"Source And Time": "来源 & 时间"
},
"more": "查看更多",
"no_app": "还没有应用,快去创建一个吧!",
"not_saved": "未保存",
"outLink": {
"Can Drag": "图标可拖拽",
"Default open": "默认打开",
"Iframe block title": "复制下面 iframe 加入到你的网站中",
"Link block title": "将下面链接复制到浏览器打开",
"Script Close Icon": "关闭图标",
"Script Open Icon": "打开图标",
"Script block title": "将下面代码加入到你的网站中",
"Select Mode": "开始使用",
"Select Using Way": "选择使用方式",
"Show History": "展示历史对话"
},
"publish": {
"Fei shu bot": "飞书",
"Fei shu bot publish": "发布到飞书机器人"
},
"schedule": {
"Default prompt": "默认问题",
"Default prompt placeholder": "执行应用时的默认问题",
"Every day": "每天 {{hour}}:00",
"Every month": "每月 {{day}} 号 {{hour}}:00",
"Every week": "每周{{day}} {{hour}}:00",
"Interval": "每 {{interval}} 小时",
"Open schedule": "定时执行"
},
"setting": "应用信息设置",
"share": {
"Amount limit tip": "最多创建 10 组",
"Create link": "创建新链接",
"Create link tip": "创建成功。已复制分享地址,可直接分享使用",
"Ip limit title": "IP 限流(人/分钟)",
"Is response quote": "返回引用",
"Not share link": "没有创建分享链接",
"Role check": "身份校验"
},
"tip": {
"Add a intro to app": "快来给应用一个介绍~",
"chatNodeSystemPromptTip": "模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。可通过输入 / 插入选择变量\n如果关联了知识库,你还可以通过适当的描述,来引导模型何时去调用知识库搜索。例如:\n你是电影《星际穿越》的助手,当用户询问与《星际穿越》相关的内容时,请搜索知识库并结合搜索结果进行回答。",
"variableTip": "可以在对话开始前,要求用户填写一些内容作为本轮对话的特定变量。该模块位于开场引导之后。\n变量可以通过 {{变量key}} 的形式注入到其他模块 string 类型的输入中,例如:提示词、限定词等",
"welcomeTextTip": "每次对话开始前,发送一个初始内容。支持标准 Markdown 语法,可使用的额外标记:\n[快捷按键]:用户点击后可以直接发送该问题"
},
"tool_label": {
"doc": "使用文档",
"github": "GitHub地址",
"price": "计费说明",
"view_doc": "查看说明文档"
},
"tts": {
"Close": "不使用",
"Speech model": "语音模型",
"Speech speed": "语速",
"Test Listen": "试听",
"Test Listen Text": "你好,这是语音测试,如果你能听到这句话,说明语音播放功能正常",
"Web": "浏览器自带(免费)"
},
"whisper": {
"Auto send": "自动发送",
"Auto send tip": "语音输入完毕后直接发送,不需要再手动点击发送按键",
"Auto tts response": "自动语音回复",
"Auto tts response tip": "通过语音输入发送的问题,会直接以语音的形式响应,请确保打开了语音播报功能。",
"Close": "关闭",
"Not tts tip": "你没有开启语音播放,该功能无法使用",
"Open": "开启",
"Switch": "开启语音输入"
}
},
"chat": {
"Admin Mark Content": "纠正后的回复",
"Audio Not Support": "设备不支持语音播放",
"Audio Speech Error": "语音播报异常",
"Cancel Speak": "取消语音输入",
"Confirm to clear history": "确认清空该应用的在线聊天记录?分享和 API 调用的记录不会被清空。",
"Confirm to clear share chat history": "确认删除所有聊天记录?",
"Converting to text": "正在转换为文本...",
"Custom History Title": "自定义历史记录标题",
"Custom History Title Description": "如果设置为空,会自动跟随聊天记录。",
"Exit Chat": "退出聊天",
"Failed to initialize chat": "初始化聊天失败",
"Feedback Failed": "提交反馈异常",
"Feedback Modal": "结果反馈",
"Feedback Modal Tip": "输入你觉得回答不满意的地方",
"Feedback Submit": "提交反馈",
"Feedback Success": "反馈成功!",
"Finish Speak": "语音输入完成",
"History": "记录",
"History Amount": "{{amount}} 条记录",
"Mark": "标注预期回答",
"Mark Description": "当前标注功能为测试版。\n\n点击添加标注后,需要选择一个知识库,以便存储标注数据。你可以通过该功能快速的标注问题和预期回答,以便引导模型下次的回答。\n\n目前,标注功能同知识库其他数据一样,受模型的影响,不代表标注后 100% 符合预期。\n\n标注数据仅单向与知识库同步,如果知识库修改了该标注数据,日志展示的标注数据无法同步。",
"Mark Description Title": "标注功能介绍",
"New Chat": "新对话",
"Pin": "置顶",
"Question Guide": "猜你想问",
"Quote": "引用",
"Quote Amount": "知识库引用({{amount}} 条)",
"Read Mark Description": "查看标注功能介绍",
"Recent use": "应用列表",
"Record": "语音输入",
"Restart": "重开对话",
"Run test": "运行预览",
"Select dataset": "选择知识库",
"Select dataset Desc": "选择一个知识库存储预期答案",
"Send Message": "发送",
"Speaking": "我在听,请说...",
"Start Chat": "开始对话",
"Type a message": "输入问题,发送 [Enter]/换行 [Ctrl(Alt/Shift) + Enter]",
"Unpin": "取消置顶",
"You need to a chat app": "你没有可用的应用",
"error": {
"Chat error": "对话出现异常",
"Messages empty": "接口内容为空,可能文本超长了~",
"Select dataset empty": "你没有选择知识库",
"User input empty": "传入的用户问题为空",
"data_error": "获取数据异常"
},
"feedback": {
"Close User Like": "用户表示赞同\n点击关闭该标记",
"Feedback Close": "关闭反馈",
"No Content": "用户没有填写具体反馈内容",
"Read User dislike": "用户表示反对\n点击查看内容"
},
"logs": {
"api": "API 调用",
"feishu": "飞书",
"free_login": "免登录链接",
"official_account": "公众号",
"online": "在线使用",
"share": "外部链接调用",
"team": "团队空间对话",
"test": "测试",
"wecom": "企业微信"
},
"markdown": {
"Edit Question": "编辑问题",
"Quick Question": "点我立即提问",
"Send Question": "发送问题"
},
"quote": {
"Quote Tip": "此处仅显示实际引用内容,若数据有更新,此处不会实时更新",
"Read Quote": "查看引用"
},
"response": {
"Complete Response": "完整响应",
"Extension model": "问题优化模型",
"Read complete response": "查看详情",
"Read complete response tips": "点击查看详细流程",
"Tool call tokens": "工具调用 tokens 消耗",
"context total length": "上下文总长度",
"module cq": "问题分类列表",
"module cq result": "分类结果",
"module extract description": "提取背景描述",
"module extract result": "提取结果",
"module historyPreview": "记录预览(仅展示部分内容)",
"module http result": "响应体",
"module if else Result": "判断器结果",
"module limit": "单次搜索上限",
"module maxToken": "最大响应 tokens",
"module model": "模型",
"module name": "模型名",
"module query": "问题/检索词",
"module quoteList": "引用内容",
"module similarity": "相似度",
"module temperature": "温度",
"module time": "运行时长",
"module tokens": "AI Tokens 消耗",
"plugin output": "插件输出值",
"search using reRank": "结果重排",
"text output": "文本输出",
"update_var_result": "变量更新结果(按顺序展示多个变量更新结果)",
"user_select_result": "用户选择结果"
},
"retry": "重新生成",
"tts": {
"Stop Speech": "停止"
}
},
"common": {
"tip": {
"leave page": "内容已修改,确认离开页面吗?"
}
},
"dataset": {
"Choose Dataset": "关联知识库",
"Collection": "数据集",
"Create dataset": "创建一个{{name}}",
"Dataset": "知识库",
"Dataset ID": "知识库 ID",
"Delete Confirm": "确认删除该知识库?删除后数据无法恢复,请确认!",
"Empty Dataset": "空数据集",
"Empty Dataset Tips": "还没有知识库,快去创建一个吧!",
"Folder placeholder": "这是一个目录",
"Go Dataset": "前往知识库",
"Intro Placeholder": "这个知识库还没有介绍~",
"Manual collection": "手动数据集",
"My Dataset": "我的知识库",
"Query extension intro": "开启问题优化功能,可以提高提高连续对话时,知识库搜索的精度。开启该功能后,在进行知识库搜索时,会根据对话记录,利用 AI 补全问题缺失的信息。",
"Quote Length": "引用内容长度",
"Read Dataset": "查看知识库详情",
"Set Website Config": "开始配置网站信息",
"Start export": "已开始导出",
"Table collection": "表格数据集",
"Text collection": "文本数据集",
"collection": {
"Click top config website": "点击配置网站",
"Collection name": "数据集名称",
"Collection raw text": "数据集内容",
"Empty Tip": "数据集空空如也",
"QA Prompt": "QA 拆分引导词",
"Start Sync Tip": "确认开始同步数据?将会删除旧数据后重新获取,请确认!",
"Sync": "同步数据",
"Sync Collection": "数据同步",
"Website Empty Tip": "还没有关联网站",
"Website Link": "Web 站点地址",
"id": "集合 ID",
"metadata": {
"Chunk Size": "分割大小",
"Createtime": "创建时间",
"Raw text length": "原文长度",
"Training Type": "训练模式",
"Updatetime": "更新时间",
"Web page selector": "网站选择器",
"metadata": "元数据",
"read source": "查看原始内容",
"source": "数据来源",
"source name": "来源名",
"source size": "来源大小"
},
"status": {
"active": "已就绪"
},
"sync": {
"result": {
"sameRaw": "内容未变动,无需更新",
"success": "开始同步"
}
},
"training": {}
},
"data": {
"Auxiliary Data": "辅助数据",
"Auxiliary Data Placeholder": "该部分为可选填项,通常是为了与前面的【数据内容】配合,构建结构化提示词,用于特殊场景,最多 {{maxToken}} 字。",
"Auxiliary Data Tip": "该部分为可选填项\n该内容通常是为了与前面的数据内容配合,构建结构化提示词,用于特殊场景",
"Data Content": "相关数据内容",
"Data Content Placeholder": "该输入框是必填项,该内容通常是对于知识点的描述,也可以是用户的问题,最多 {{maxToken}} 字。",
"Data Content Tip": "该输入框是必填项\n该内容通常是对于知识点的描述,也可以是用户的问题。",
"Default Index Tip": "无法编辑,默认索引会使用【相关数据内容】与【辅助数据】的文本直接生成索引。",
"Edit": "编辑数据",
"Empty Tip": "这个集合还没有数据~",
"Main Content": "主要内容",
"Search data placeholder": "搜索相关数据",
"Too Long": "总长度超长了",
"Total Amount": "{{total}} 组",
"group": "组",
"unit": "条"
},
"embedding model tip": "索引模型可以将自然语言转成向量,用于进行语义检索。\n注意,不同索引模型无法一起使用,选择完索引模型后将无法修改。",
"error": {
"Data not found": "数据不存在或已被删除",
"Start Sync Failed": "开始同步失败",
"invalidVectorModelOrQAModel": "VectorModel 或 QA 模型错误",
"unAuthDataset": "无权操作该知识库",
"unAuthDatasetCollection": "无权操作该数据集",
"unAuthDatasetData": "无权操作该数据",
"unAuthDatasetFile": "无权操作该文件",
"unCreateCollection": "无权操作该数据",
"unLinkCollection": "不是网络链接集合"
},
"externalFile": "外部文件库",
"file": "文件",
"folder": "目录",
"import": {
"Auto mode Estimated Price Tips": "需调用文件处理模型,需要消耗较多 tokens:{{price}} 元/1K tokens",
"Auto process": "自动",
"Auto process desc": "自动设置分割和预处理规则",
"Chunk Range": "范围:{{min}}~{{max}}",
"Chunk Split": "直接分段",
"Chunk Split Tip": "将文本按一定的规则进行分段处理后,转成可进行语义搜索的格式,适合绝大多数场景。不需要调用模型额外处理,成本低。",
"Custom process": "自定义规则",
"Custom process desc": "自定义设置分制和预处理规则",
"Custom prompt": "自定义提示词",
"Custom split char": "自定义分隔符",
"Custom split char Tips": "允许你根据自定义的分隔符进行分块。通常用于已处理好的数据,使用特定的分隔符来精确分块。",
"Custom text": "自定义文本",
"Custom text desc": "手动输入一段文本作为数据集",
"Data Preprocessing": "数据处理",
"Data process params": "数据处理参数",
"Down load csv template": "点击下载 CSV 模板",
"Embedding Estimated Price Tips": "仅使用索引模型,消耗少量 AI 积分:{{price}} 元/1K tokens",
"Ideal chunk length": "理想分块长度",
"Ideal chunk length Tips": "按结束符号进行分段,并将多个分段组成一个分块,该值决定了分块的预估大小。",
"Import success": "导入成功,请等待训练",
"Link name": "网络链接",
"Link name placeholder": "仅支持静态链接,如果上传后数据为空,可能该链接无法被读取\n每行一个,每次最多 10 个链接",
"Local file": "本地文件",
"Local file desc": "上传 PDF、TXT、DOCX 等格式的文件",
"Preview chunks": "预览分段(最多 5 段)",
"Preview raw text": "预览源文本(最多 3000 字)",
"Process way": "处理方式",
"QA Estimated Price Tips": "需调用文件处理模型,需要消耗较多费用:{{price}} 元/1K tokens",
"QA Import": "QA 拆分",
"QA Import Tip": "根据一定规则,将文本拆成一段较大的段落,调用 AI 为该段落生成问答对。有非常高的检索精度,但是会丢失很多内容细节。",
"Select file": "选择文件",
"Select source": "选择来源",
"Source name": "来源名",
"Sources list": "来源列表",
"Start upload": "开始上传",
"Total files": "共 {{total}} 个文件",
"Training mode": "训练模式",
"Upload data": "上传数据",
"Upload file progress": "文件上传进度",
"Upload status": "状态",
"Web link": "网页链接",
"Web link desc": "读取静态网页内容作为数据集"
},
"link": "链接",
"search": {
"Dataset Search Params": "知识库搜索配置",
"Empty result response": "空搜索回复",
"Filter": "搜索过滤",
"Max Tokens": "引用上限",
"Max Tokens Tips": "单次搜索最大的 token 数量,中文约 1 字=1.7 tokens,英文约 1 字=1 token",
"Min Similarity": "最低相关度",
"Min Similarity Tips": "不同索引模型的相关度有区别,请通过搜索测试来选择合适的数值,使用 Rerank 时,相关度可能会很低。",
"No support similarity": "仅使用结果重排或语义检索时,支持相关度过滤",
"Nonsupport": "不支持",
"Params Setting": "搜索参数设置",
"Quote index": "第几个引用",
"ReRank": "结果重排",
"ReRank desc": "使用重排模型来进行二次排序,可增强综合排名。",
"Source id": "来源 ID",
"Source name": "引用来源名",
"Using query extension": "使用问题优化",
"mode": {
"embedding": "语义检索",
"embedding desc": "使用向量进行文本相关性查询",
"fullTextRecall": "全文检索",
"fullTextRecall desc": "使用传统的全文检索,适合查找一些关键词和主谓语特殊的数据",
"mixedRecall": "混合检索",
"mixedRecall desc": "使用向量检索与全文检索的综合结果返回,使用 RRF 算法进行排序。"
},
"score": {
"embedding": "语义检索",
"embedding desc": "通过计算向量之间的距离获取得分,范围为 0~1。",
"fullText": "全文检索",
"fullText desc": "计算相同关键词的得分,范围为 0~无穷。",
"reRank": "结果重排",
"reRank desc": "通过 Rerank 模型计算句子之间的关联度,范围为 0~1。",
"rrf": "综合排名",
"rrf desc": "通过倒排计算的方式,合并多个检索结果。"
},
"search mode": "搜索模式"
},
"status": {
"active": "已就绪",
"syncing": "同步中"
},
"test": {
"Batch test": "批量测试",
"Batch test Placeholder": "选择一个 CSV 文件",
"Search Test": "搜索测试",
"Test": "测试",
"Test Result": "测试结果",
"Test Text": "单个文本测试",
"Test Text Placeholder": "输入需要测试的文本",
"Test params": "测试参数",
"delete test history": "删除该测试结果",
"test history": "测试历史",
"test result placeholder": "测试结果将在这里展示",
"test result tip": "根据知识库内容与测试文本的相似度进行排序,你可以根据测试结果调整对应的文本。\n注意:测试记录中的数据可能已经被修改过,点击某条测试数据后将展示最新的数据。"
},
"training": {
"Agent queue": "QA 训练排队",
"Auto mode": "增强处理(实验)",
"Auto mode Tip": "通过子索引以及调用模型生成相关问题与摘要,来增加数据块的语义丰富度,更利于检索。需要消耗更多的存储空间和增加 AI 调用次数。",
"Chunk mode": "直接分段",
"Full": "预计 5 分钟以上",
"Leisure": "空闲",
"QA mode": "问答拆分",
"Vector queue": "索引排队",
"Waiting": "预计 5 分钟",
"Website Sync": "Web 站点同步",
"tag": "排队情况"
},
"website": {
"Base Url": "根地址",
"Config": "Web 站点配置",
"Config Description": "Web 站点同步功能允许你填写一个网站的根地址,系统会自动深度抓取相关的网页进行知识库训练。仅会抓取静态的网站,以项目文档、博客为主。",
"Confirm Create Tips": "确认同步该站点,同步任务将随后开启,请确认!",
"Confirm Update Tips": "确认更新站点配置?会立即按新的配置开始同步,请确认!",
"Selector": "选择器",
"Selector Course": "使用教程",
"Start Sync": "开始同步",
"UnValid Website Tip": "您的站点可能非静态站点,无法同步"
}
},
"module": {
"Add question type": "添加问题类型",
"Add_option": "添加选项",
"Can not connect self": "不能连接自身",
"Data Type": "数据类型",
"Dataset quote": {
"label": "知识库引用",
"select": "选择知识库引用"
},
"Default Value": "默认值",
"Default value": "默认值",
"Default value placeholder": "不填则默认返回空字符",
"Diagram": "示意图",
"Edit intro": "编辑描述",
"Field Description": "字段描述",
"Field Name": "字段名",
"Http request props": "请求参数",
"Http request settings": "请求配置",
"Http timeout": "超时时长",
"Input Type": "输入类型",
"input_form": "输入字段",
"input_name": "输入名",
"input_type": "输入类型",
"input_description": "输入描述",
"Laf sync params": "同步参数",
"Max Length": "最大长度",
"Max Length placeholder": "输入文本的最大长度",
"Max Value": "最大值",
"Min Value": "最小值",
"QueryExtension": {
"placeholder": "例如:\n关于 Python 的介绍和使用等问题。\n当前对话与游戏《GTA5》有关。"
},
"Quote prompt setting": "引用提示词配置",
"Select app": "选择应用",
"Setting quote prompt": "配置引用提示词",
"Variable": "全局变量",
"Variable Setting": "变量设置",
"edit": {
"Field Name Cannot Be Empty": "字段名不能为空"
},
"extract": {
"Add field": "新增字段",
"Enum Description": "列举出该字段可能的值,每行一个",
"Enum Value": "枚举值",
"Field Description Placeholder": "姓名/年龄/SQL 语句……",
"Field Setting Title": "提取字段配置",
"Required": "必须返回",
"Required Description": "即使无法提取该字段,也会使用默认值进行返回",
"Target field": "目标字段"
},
"http": {
"Add props": "添加参数",
"AppId": "应用 ID",
"ChatId": "当前对话 ID",
"Current time": "当前时间",
"Histories": "历史记录",
"Key already exists": "Key 已经存在",
"Key cannot be empty": "参数名不能为空",
"Props name": "参数名",
"Props tip": "可以设置 HTTP 请求的相关参数\n可通过输入 / 来调用变量,当前可使用变量:\n{{variable}}",
"Props value": "参数值",
"ResponseChatItemId": "AI 回复的 ID",
"Url and params have been split": "路径参数已被自动加入 Params 中",
"curl import": "cURL 导入",
"curl import placeholder": "请输入 cURL 格式内容,将会提取第一个接口的请求信息。"
},
"input": {
"Add Branch": "添加分支",
"add": "添加条件",
"description": {
"Background": "你可以添加一些特定内容的介绍,从而更好的识别用户的问题类型。这个内容通常是给模型介绍一个它不知道的内容。",
"HTTP Dynamic Input": "接收前方节点的输出值作为变量,这些变量可以被 HTTP 请求参数使用。",
"Http Request Header": "自定义请求头,请严格填入 JSON 字符串。\n1. 确保最后一个属性没有逗号\n2. 确保 key 包含双引号\n例如:{\"Authorization\":\"Bearer xxx\"}",
"Http Request Url": "新的 HTTP 请求地址。如果出现两个“请求地址”,可以删除该模块重新加入,会拉取最新的模块配置。",
"Response content": "可以使用 \\n 来实现连续换行。\n可以通过外部模块输入实现回复,外部模块输入时会覆盖当前填写的内容。\n如传入非字符串类型数据将会自动转成字符串"
},
"label": {
"Background": "背景知识",
"Http Request Url": "请求地址",
"Response content": "回复的内容",
"Select dataset": "选择知识库",
"aiModel": "AI 模型",
"chat history": "聊天记录",
"user question": "用户问题"
},
"placeholder": {
"Classify background": "例如:\n1. AIGC(人工智能生成内容)是指使用人工智能技术自动或半自动地生成数字内容,如文本、图像、音乐、视频等。\n2. AIGC 技术包括但不限于自然语言处理、计算机视觉、机器学习和深度学习。这些技术可以创建新内容或修改现有内容,以满足特定的创意、教育、娱乐或信息需求。"
}
},
"laf": {
"Select laf function": "选择 laf 函数"
},
"output": {
"description": {
"Ai response content": "将在 stream 回复完毕后触发",
"New context": "将本次回复内容拼接上历史记录,作为新的上下文返回",
"query extension result": "以字符串数组的形式输出,可将该结果直接连接到“知识库搜索”的“用户问题”中,建议不要连接到“AI 对话”的“用户问题”中"
},
"label": {
"Ai response content": "AI 回复内容",
"New context": "新的上下文",
"query extension result": "优化结果"
}
},
"template": {
"AI function": "AI能力",
"AI response switch tip": "如果你希望当前节点不输出内容,可以关闭该开关。AI 输出的内容不会展示给用户,你可以手动的使用“AI 回复内容”进行特殊处理。",
"AI support tool tip": "支持函数调用的模型,可以更好的使用工具调用。",
"Basic Node": "基础功能",
"Query extension": "问题优化",
"System Plugin": "系统插件",
"System input module": "系统输入",
"Team app": "团队应用",
"Tool module": "工具",
"UnKnow Module": "未知模块",
"ai_chat": "AI 对话",
"ai_chat_intro": "AI 大模型对话",
"config_params": "可以配置应用的系统参数",
"empty_plugin": "空白插件",
"empty_workflow": "空白工作流",
"http body placeholder": "与 Apifox 相同的语法",
"self_input": "自定义插件输入",
"self_output": "自定义插件输出",
"system_config": "系统配置",
"system_config_info": "可以配置应用的系统参数",
"work_start": "流程开始"
},
"templates": {
"Load plugin error": "加载插件失败"
},
"variable": {
"Custom type": "自定义变量",
"add option": "添加选项",
"input type": "文本",
"key": "变量 key",
"key already exists": "Key 已经存在",
"key is required": "变量 key 是必须的",
"select type": "下拉单选",
"text max length": "最大长度",
"textarea type": "段落",
"variable name": "变量名",
"variable name is required": "变量名不能为空",
"variable option is required": "选项不能全空",
"variable option is value is required": "选项内容不能为空",
"variable options": "选项"
},
"variable add option": "添加选项"
},
"plugin": {
"Custom headers": "自定义请求头",
"Free": "该插件无需费用消耗~",
"Get Plugin Module Detail Failed": "加载插件异常",
"Http plugin intro placeholder": "仅做展示,无实际效果",
"cost": "费用消耗:"
},
"view_chat_detail": "查看对话详情",
"workflow": {
"dynamic_input": "动态输入",
"Can not delete node": "该节点不允许删除",
"Change input type tip": "修改输入类型会清空已填写的值,请确认!",
"Check Failed": "工作流校验失败,请检查节点是否正确填值,以及连线是否正常",
"Confirm stop debug": "确认终止调试?调试信息将会不保留。",
"Copy node": "已复制节点",
"Custom inputs": "自定义输入",
"Custom outputs": "自定义输出",
"Dataset quote": "知识库引用",
"Debug": "调试",
"Debug Node": "Debug 模式",
"Failed": "运行失败",
"Not intro": "这个节点没有介绍~",
"Run": "运行",
"Running": "运行中",
"Save and publish": "保存并发布",
"Save to cloud": "仅保存",
"Skipped": "跳过运行",
"Stop debug": "停止调试",
"Success": "运行成功",
"Value type": "数据类型",
"Variable": {
"Variable type": "变量类型"
},
"debug": {
"Done": "完成调试",
"Hide result": "隐藏结果",
"Not result": "无运行结果",
"Run result": "运行结果",
"Show result": "展示结果"
},
"inputType": {
"JSON Editor": "JSON 输入框",
"Manual input": "手动输入",
"textInput": "文本输入框",
"Manual select": "手动选择",
"Reference": "变量引用",
"custom": "自定义变量",
"dynamicTargetInput": "动态外部数据",
"input": "单行输入框",
"number input": "数字输入框",
"select": "单选框",
"selectApp": "应用选择",
"selectDataset": "知识库选择",
"selectLLMModel": "对话模型选择",
"switch": "开关",
"textarea": "多行输入框"
},
"publish": {
"OnRevert version": "点击回退到该版本",
"OnRevert version confirm": "确认回退至该版本?会为您保存编辑中版本的配置,并为回退版本创建一个新的发布版本。",
"histories": "发布记录"
},
"template": {
"Interactive": "交互",
"Multimodal": "多模态",
"Search": "搜索"
},
"tool": {
"Handle": "工具连接器",
"Select Tool": "选择工具"
},
"value": "值",
"variable": "变量"
}
},
"create": "去创建",
"cron_job_run_app": "定时任务",
"dataset": {
"Confirm move the folder": "确认移动到该目录",
"Confirm to delete the data": "确认删除该数据?",
"Confirm to delete the file": "确认删除该文件及其所有数据?",
"Create Folder": "创建文件夹",
"Create manual collection": "创建手动数据集",
"Delete Dataset Error": "删除知识库异常",
"Edit Folder": "编辑文件夹",
"Edit Info": "编辑信息",
"Export": "导出",
"Export Dataset Limit Error": "导出数据失败",
"Folder Name": "输入文件夹名称",
"Insert Data": "插入",
"Manual collection Tip": "手动数据集允许创建一个空的容器装入数据",
"Move Failed": "移动出现错误~",
"Select Dataset": "选择该知识库",
"Select Dataset Tips": "仅能选择同一个索引模型的知识库",
"Select Folder": "进入文件夹",
"Training Name": "数据训练",
"collections": {
"Collection Embedding": "{{total}} 组索引中",
"Confirm to delete the folder": "确认删除该文件夹及里面所有内容?",
"Create And Import": "新建/导入",
"Data Amount": "数据总量",
"Select Collection": "选择文件",
"Select One Collection To Store": "选择一个文件进行存储"
},
"data": {
"Can not edit": "无编辑权限",
"Custom Index Number": "自定义索引{{number}}",
"Default Index": "默认索引",
"Delete Tip": "确认删除该条数据?",
"Index Placeholder": "输入索引文本内容",
"Input Success Tip": "导入数据成功",
"Update Success Tip": "更新数据成功",
"edit": {
"Index": "数据索引({{amount}})",
"divide_content": "分块内容"
},
"input is empty": "数据内容不能为空 "
},
"dataset_name": "知识库名称",
"deleteFolderTips": "确认删除该文件夹及其包含的所有知识库?删除后数据无法恢复,请确认!",
"test": {
"noResult": "搜索结果为空"
}
},
"error": {
"Create failed": "创建失败",
"fileNotFound": "文件找不到了~",
"inheritPermissionError": "权限继承错误",
"missingParams": "参数缺失",
"upload_file_error_filename": "{{name}} 上传失败",
"username_empty": "账号不能为空"
},
"error.code_error": "验证码错误",
"extraction_results": "提取结果",
"field_name": "字段名",
"free": "免费",
"get_QR_failed": "获取二维码失败",
"get_app_failed": "获取应用失败",
"get_laf_failed": "获取Laf函数列表失败",
"has_verification": "已验证,点击取消绑定",
"info": {
"buy_extra": "购买额外套餐",
"csv_download": "点击下载批量测试模板",
"csv_message": "读取 CSV 文件第一列进行批量测试,单次最多支持 100 组数据。",
"felid_message": "字段key必须是纯英文字母或数字,并且不能以数字开头。",
"free_plan": "免费版团队连续30天未登录系统时,系统会自动清理账号知识库。",
"include": "包含标准套餐与额外资源包",
"node_info": "调整该模块会对工具调用时机有影响。\n你可以通过精确的描述该模块功能,引导模型进行工具调用。",
"old_version_attention": "检测到您的高级编排为旧版,系统将为您自动格式化成新版工作流。\n\n由于版本差异较大,会导致一些工作流无法正常排布,请重新手动连接工作流。如仍异常,可尝试删除对应节点后重新添加。\n\n你可以直接点击调试进行工作流测试,调试完毕后点击发布。直到你点击发布,新工作流才会真正保存生效。\n\n在你发布新工作流前,自动保存不会生效。",
"open_api_notice": "可以填写 OpenAI/OneAPI的相关密钥。如果你填写了该内容,在线上平台使用【AI对话】、【问题分类】和【内容提取】将会走你填写的Key,不会计费。请注意你的Key 是否有访问对应模型的权限。GPT模型可以选择 FastAI。",
"open_api_placeholder": "请求地址,默认为 openai 官方。可填中转地址,未自动补全 \"v1\"",
"resource": "资源用量"
},
"invalid_variable": "无效变量",
"is_open": "是否开启",
"is_using": "正在使用",
"item_description": "字段描述",
"item_name": "字段名",
"key_repetition": "key 重复",
"navbar": {
"Account": "账号",
"Chat": "聊天",
"Datasets": "知识库管理",
"Studio": "应用管理",
"Tools": "工具",
"Store": "创新中心",
"Admin": "系统管理"
},
"new_create": "新建",
"no": "否",
"no_laf_env": "系统未配置Laf环境",
"not_yet_introduced": "暂无介绍",
"option": "选项",
"pay": {
"amount": "金额",
"package_tip": {
"buy": "您购买的套餐等级低于当前套餐,该套餐将在当前套餐过期后生效。\n您可在账号—个人信息—套餐详情里,查看套餐使用情况。",
"renewal": "您正在续费套餐。您可在账号—个人信息—套餐详情里,查看套餐使用情况。",
"upgrade": "您购买的套餐等级高于当前套餐,该套餐将即刻生效,当前套餐将延后生效。您可在账号—个人信息—套餐详情里,查看套餐使用情况。"
},
"wechat": "请微信扫码支付: {{price}}元\n请勿关闭页面",
"yuan": "{{amount}}元"
},
"permission": {
"Collaborator": "协作者",
"Default permission": "默认权限",
"Manage": "管理",
"No InheritPermission": "已限制权限,不再继承父级文件夹的权限,",
"Not collaborator": "暂无协作者",
"Owner": "创建者",
"Permission": "权限",
"Permission config": "权限配置",
"Private": "私有",
"Private Tip": "仅自己可用",
"Public": "团队",
"Public Tip": "团队所有成员可使用",
"Remove InheritPermission Confirm": "此操作会导致权限继承失效,是否进行?",
"Resume InheritPermission Confirm": "是否恢复为继承父级文件夹的权限?",
"Resume InheritPermission Failed": "恢复失败",
"Resume InheritPermission Success": "恢复成功",
"change_owner": "转移所有权",
"change_owner_failed": "转移所有权失败",
"change_owner_placeholder": "输入用户名查找账号",
"change_owner_success": "成功转移所有权",
"change_owner_tip": "转移后您的权限不会保留",
"change_owner_to": "转移给",
"manager": "管理员",
"read": "读权限",
"write": "写权限"
},
"plugin": {
"App": "选择应用",
"Currentapp": "当前应用",
"Description": "描述",
"Edit Http Plugin": "编辑 HTTP 插件",
"Enter PAT": "请输入访问凭证(PAT)",
"Get Plugin Module Detail Failed": "获取插件信息异常",
"Import Plugin": "导入 HTTP 插件",
"Import from URL": "从 URL 导入。https://xxxx",
"Intro": "插件介绍",
"Invalid Env": "laf 环境错误",
"Invalid Schema": "Schema 无效",
"Invalid URL": "URL 无效",
"Method": "方法",
"Path": "路径",
"Please bind laf accout first": "请先绑定 laf 账号",
"Plugin List": "插件列表",
"Search plugin": "搜索插件",
"Search_app": "搜索应用",
"Set Name": "给插件取个名字",
"contribute": "贡献插件",
"go to laf": "去编写",
"path": "路径"
},
"required": "必须",
"resume_failed": "恢复失败",
"select_reference_variable": "选择引用变量",
"share_link": "分享链接",
"support": {
"account": {
"Individuation": "个性化"
},
"inform": {
"Read": "已读"
},
"openapi": {
"Api baseurl": "API 根地址",
"Api manager": "API 密钥管理",
"Copy success": "已复制 API 地址",
"New api key": "新的 API 密钥",
"New api key tip": "请保管好你的密钥,密钥不会再次展示~"
},
"outlink": {
"Delete link tip": "确认删除该免登录链接?删除后,该链接将会立即失效,对话日志仍会保留,请确认!",
"Max usage points": "费用上限",
"Max usage points tip": "该链接最多允许使用多少费用,超出后将无法使用。-1 代表无限制。",
"Usage points": "费用消耗",
"share": {
"Response Quote": "返回引用",
"Response Quote tips": "在分享链接中返回引用内容,但不会允许用户下载原文档"
}
},
"permission": {
"Permission": "权限"
},
"standard": {
"AI Bonus Points": "AI 费用",
"due_date": "到期时间",
"storage": "存储量",
"type": "类型"
},
"team": {
"limit": {
"No permission rerank": "无权使用结果重排,请升级您的套餐"
}
},
"user": {
"Avatar": "头像",
"Go laf env": "点击前往 {{env}} 获取 PAT 凭证。",
"Laf account course": "查看绑定 laf 账号教程。",
"Laf account intro": "绑定你的 laf 账号后,你将可以在工作流中使用 laf 模块,实现在线编写代码。",
"Need to login": "请先登录",
"Price": "计费标准",
"User self info": "个人信息",
"auth": {
"Sending Code": "正在发送"
},
"captcha_placeholder": "请输入验证码",
"inform": {
"System message": "系统消息"
},
"login": {
"Email": "邮箱",
"Github": "GitHub 登录",
"Google": "Google 登录",
"Password": "密码",
"Password login": "密码登录",
"Phone": "手机号登录",
"Phone number": "手机号",
"Provider error": "登录异常,请重试",
"Username": "用户名",
"Wechat": "微信登录",
"can_not_login": "无法登录,点击联系",
"error": "登录异常",
"security_failed": "安全校验失败",
"wx_qr_login": "微信扫码登录"
},
"logout": {
"confirm": "确认退出登录?"
},
"team": {
"Dataset usage": "知识库容量",
"Team Tags Async Success": "同步完成",
"member": "成员"
}
},
"wallet": {
"Ai point every thousand tokens": "{{points}} 元/1K tokens",
"Amount": "金额",
"Buy": "购买",
"Not sufficient": "您的余额不足。",
"Plan expired time": "套餐到期时间",
"Standard Plan Detail": "套餐详情",
"To read plan": "查看套餐",
"amount_0": "购买数量不能为0",
"apply_invoice": "申请开票",
"bill": {
"Number": "订单号",
"Status": "状态",
"Type": "订单类型",
"payWay": {
"Way": "支付方式",
"balance": "余额支付",
"wx": "微信支付"
},
"status": {
"closed": "已关闭",
"notpay": "未支付",
"refund": "已退款",
"success": "支付成功"
}
},
"bill_detail": "账单详情",
"bill_tag": {
"bill": "账单记录",
"default_header": "默认抬头",
"invoice": "开票记录"
},
"billable_invoice": "可开票账单",
"buy_resource": "购买资源包",
"has_invoice": "是否已开票",
"invoice_amount": "开票金额",
"invoice_data": {
"bank": "开户银行",
"bank_account": "开户账号",
"company_address": "公司地址",
"company_phone": "公司电话",
"email": "邮箱地址",
"need_special_invoice": "是否需要专票",
"organization_name": "组织名称",
"unit_code": "统一信用代码"
},
"invoice_detail": "发票详情",
"invoice_info": "发票将在 3-7 个工作日内发送至邮箱,请耐心等待",
"invoicing": "开票",
"moduleName": {
"index": "索引生成",
"qa": "QA 拆分"
},
"noBill": "无账单记录~",
"no_invoice": "暂无开票记录",
"subscription": {
"AI points": "AI 积分",
"AI points click to read tip": "每次调用 AI 模型时,都会消耗一定的 AI 积分(类似于 token)。点击可查看详细计算规则。",
"AI points usage": "AI 积分使用量",
"AI points usage tip": "每次调用 AI 模型时,都会消耗一定的 AI 积分。具体的计算标准可参考上方的“计费标准”",
"Ai points": "AI 积分计算标准",
"Current plan": "当前套餐",
"Extra ai points": "额外 AI 积分",
"Extra dataset size": "额外知识库容量",
"Extra plan": "额外资源包",
"Extra plan tip": "标准套餐不够时,您可以购买额外资源包继续使用",
"FAQ": "常见问题",
"Month amount": "月数",
"Next plan": "未来套餐",
"Stand plan level": "订阅套餐",
"Sub plan": "订阅套餐",
"Sub plan tip": "免费使用 {{title}} 或升级更高的套餐",
"Team plan and usage": "套餐与用量",
"Training weight": "训练优先级:{{weight}}",
"Update extra ai points": "额外 AI 积分",
"Update extra dataset size": "额外存储量",
"Upgrade plan": "升级套餐",
"ai_model": "AI语言模型",
"function": {
"History store": "{{amount}} 天对话记录保留",
"Max app": "{{amount}} 个应用&插件",
"Max dataset": "{{amount}} 个知识库",
"Max dataset size": "{{amount}} 组知识库索引",
"Max members": "{{amount}} 个团队成员",
"Points": "{{amount}} 元"
},
"mode": {
"Month": "按月",
"Period": "订阅周期",
"Year": "按年",
"Year sale": "赠送两个月"
},
"point": "元",
"rerank": "检索结果重排",
"standardSubLevel": {
"custom": "自定义版",
"enterprise": "企业版",
"enterprise_desc": "适合中小企业在生产环境构建知识库应用",
"experience": "体验版",
"experience_desc": "可解锁 Eaigc 完整功能",
"free": "免费版",
"free desc": "每月均可免费使用基础功能,连续 30 天未登录系统,将会自动清除知识库",
"team": "团队版",
"team_desc": "适合小团队构建知识库应用并提供对外服务"
},
"status": {
"active": "生效中",
"expired": "已过期",
"inactive": "待使用"
},
"token_compute": "点击查看在线 Tokens 计算器",
"type": {
"balance": "余额充值",
"extraDatasetSize": "知识库扩容",
"extraPoints": "AI 费用套餐",
"standard": "套餐订阅"
},
"web_site_sync": "Web 站点同步"
},
"usage": {
"Ai model": "AI 模型",
"App name": "应用名",
"Audio Speech": "语音播放",
"Bill Module": "扣费模块",
"Duration": "时长(秒)",
"Extension result": "问题优化结果",
"Module name": "模块名",
"Source": "来源",
"Text Length": "文本长度",
"Time": "生成时间",
"Token Length": "token 长度",
"Total": "总金额",
"Total points": "AI 费用消耗",
"Usage Detail": "使用详情",
"Whisper": "语音输入"
}
}
},
"sync_link": "同步链接",
"system": {
"Concat us": "联系我们",
"Help Document": "帮助文档"
},
"tag_list": "标签列表",
"team_tag": "团队标签",
"template": {
"Quote Content Tip": "可以自定义引用内容的结构,以更好的适配不同场景。可以使用一些变量来进行模板配置:\n{{q}} - 检索内容,{{a}} - 预期内容,{{source}} - 来源,{{sourceId}} - 来源文件名,{{index}} - 第 n 个引用,他们都是可选的,下面是默认值:\n{{default}}",
"Quote Prompt Tip": "可以用 {{quote}} 来插入引用内容模板,使用 {{question}} 来插入问题。下面是默认值:\n{{default}}"
},
"textarea_variable_picker_tip": "输入\"/\"可选择变量",
"tool_field": "工具字段参数配置",
"undefined_var": "引用了未定义的变量,是否自动添加?",
"unit": {
"character": "字符",
"minute": "分钟"
},
"unusable_variable": "无可用变量",
"upload_file_error": "上传文件失败",
"user": {
"Account": "账号",
"Amount of earnings": "收益(¥)",
"Amount of inviter": "累计邀请人数",
"Application Name": "项目名",
"Avatar": "头像",
"Change": "变更",
"Copy invite url": "复制邀请链接",
"Edit name": "点击修改昵称",
"Invite Url": "邀请链接",
"Invite url tip": "通过该链接注册的好友将永久与你绑定,其充值时你会获得一定余额奖励。\n此外,好友使用手机号注册时,你将立即获得 5 元奖励。\n奖励会发送到您的默认团队中。",
"Laf Account Setting": "laf 账号配置",
"Language": "语言",
"Member Name": "昵称",
"Notification Receive": "通知接收",
"Notification Receive Bind": "请先绑定通知接收途径",
"Old password is error": "旧密码错误",
"OpenAI Account Setting": "OpenAI 账号配置",
"Password": "密码",
"Pay": "充值",
"Promotion": "促销",
"Promotion Rate": "返现比例",
"Promotion rate tip": "好友充值时你将获得一定比例的余额奖励",
"Replace": "更换",
"Set OpenAI Account Failed": "设置 OpenAI 账号异常",
"Team": "团队",
"Time": "时间",
"Timezone": "时区",
"Update Password": "修改密码",
"Update password failed": "修改密码异常",
"Update password successful": "修改密码成功",
"apikey": {
"key": "API 密钥"
},
"confirm_password": "确认密码",
"new_password": "新密码",
"no_invite_records": "暂无邀请记录",
"no_notice": "暂无通知",
"no_usage_records": "暂无使用记录",
"old_password": "旧密码",
"password_message": "密码最少 4 位最多 60 位",
"team": {
"Balance": "团队余额",
"Check Team": "切换",
"Confirm Invite": "确认邀请",
"Create Team": "创建新团队",
"Invite Member": "邀请成员",
"Invite Member Failed Tip": "邀请成员出现异常",
"Invite Member Result Tip": "邀请结果提示",
"Invite Member Success Tip": "邀请成员完成\n成功:{{success}} 人\n用户名无效:{{inValid}}\n已在团队中:{{inTeam}}",
"Invite Member Tips": "对方可查阅或使用团队内的其他资源",
"Leave Team": "离开团队",
"Leave Team Failed": "离开团队异常",
"Member": "成员",
"Member Name": "成员名",
"Over Max Member Tip": "团队最多 {{max}} 人",
"Personal Team": "个人团队",
"Processing invitations": "处理邀请",
"Processing invitations Tips": "你有 {{amount}} 个需要处理的团队邀请",
"Remove Member Confirm Tip": "确认将 {{username}} 移出团队?",
"Select Team": "团队选择",
"Set Name": "给团队取个名字",
"Switch Team Failed": "切换团队异常",
"Tags Async": "保存",
"Team Name": "团队名",
"Team Tags Async": "标签同步",
"Team Tags Async Success": "链接报错成功,标签信息更新",
"Update Team": "更新团队信息",
"invite": {
"Accept Confirm": "确认加入该团队?",
"Accepted": "已加入团队",
"Deal Width Footer Tip": "处理完会自动关闭噢~",
"Reject": "已拒绝邀请",
"Reject Confirm": "确认拒绝该邀请?",
"accept": "接受",
"reject": "拒绝"
},
"member": {
"Confirm Leave": "确认离开该团队?",
"active": "已加入",
"reject": "拒绝",
"waiting": "待接受"
},
"role": {
"Admin": "管理员",
"Owner": "创建者"
}
},
"type": "类型"
},
"verification": "验证",
"xx_search_result": "{{key}} 的搜索结果",
"yes": "是"
}
{
"Enable": "启用",
"collection": {
"Create update time": "创建/更新时间",
"Training type": "训练模式"
},
"collection_tags": "集合标签",
"common_dataset": "通用知识库",
"common_dataset_desc": "可通过导入文件、网页链接或手动录入形式构建知识库",
"confirm_to_rebuild_embedding_tip": "确认为知识库切换索引?\n切换索引是一个非常重量的操作,需要对您知识库内所有数据进行重新索引,时间可能较长,请确保账号内剩余积分充足。\n\n此外,你还需要注意修改选择该知识库的应用,避免它们与其他索引模型知识库混用。",
"dataset": {
"no_collections": "暂无数据集",
"no_tags": "暂无标签"
},
"external_file": "外部文件库",
"external_file_dataset_desc": "可以从外部文件库导入文件构建知识库,文件不会进行二次存储",
"external_id": "文件阅读 ID",
"external_read_url": "外部预览地址",
"external_read_url_tip": "可以配置你文件库的阅读地址。便于对用户进行阅读鉴权操作。目前可以使用 {{fileId}} 变量来指代外部文件 ID。",
"external_url": "文件访问 URL",
"file_model_function_tip": "用于增强索引和 QA 生成",
"filename": "文件名",
"folder_dataset": "文件夹",
"rebuild_embedding_start_tip": "切换索引模型任务已开始",
"rebuilding_index_count": "重建中索引数量:{{count}}",
"tag": {
"Add New": "新建",
"Add_new_tag": "新建标签",
"Edit_tag": "编辑标签",
"add": "创建",
"cancel": "取消选择",
"delete_tag_confirm": "确定删除标签?",
"manage": "标签管理",
"searchOrAddTag": "搜索或添加标签",
"tags": "标签",
"total_tags": "共{{total}}个标签"
},
"the_knowledge_base_has_indexes_that_are_being_trained_or_being_rebuilt": "知识库有训练中或正在重建的索引",
"website_dataset": "Web 站点同步",
"website_dataset_desc": "Web 站点同步允许你直接使用一个网页链接构建知识库",
"permission": {
"des": {
"read": "可查看知识库内容",
"write": "可增加和变更知识库内容",
"manage": "可管理整个知识库数据和信息"
}
}
}
\ No newline at end of file
{
"bucket_chat": "对话文件",
"bucket_file": "知识库文件",
"click_to_view_raw_source": "点击查看来源",
"file_name": "文件名",
"file_size": "文件大小",
"release_the_mouse_to_upload_the_file": "松开鼠标上传文件",
"select_and_drag_file_tip": "点击或拖动文件到此处上传",
"select_file_amount_limit": "最多选择 {{max}} 个文件",
"some_file_count_exceeds_limit": "超出 {{maxCount}} 个文件,已自动截取",
"some_file_size_exceeds_limit": "部分文件超出 {{maxSize}},已被过滤",
"support_file_type": "支持 {{fileType}} 类型文件",
"support_max_count": "最多支持 {{maxCount}} 个文件",
"support_max_size": "单个文件最大 {{maxSize}}",
"upload_failed": "上传异常",
"reached_max_file_count": "已达到最大文件数量",
"upload_error_description": "单次只支持上传多个文件或者一个文件夹"
}
\ No newline at end of file
{
"Login": "登录",
"forget_password": "忘记密码?",
"login_failed": "登录异常",
"login_success": "登录成功",
"password_condition": "密码最多 60 位",
"policy_tip": "使用即代表你同意我们的",
"privacy": "隐私协议",
"register": "注册账号",
"root_password_placeholder": "root 用户密码为环境变量 DEFAULT_ROOT_PSW 的值",
"terms": "服务协议",
"use_root_login": "使用 root 用户登录"
}
\ No newline at end of file
{
"app_key_tips": "这些 key 已有当前应用标识,具体使用可参考文档:",
"basic_info": "基本信息",
"copy_link_hint": "将下面链接复制到指定位置",
"create_api_key": "创建新 key",
"create_link": "创建链接",
"edit_api_key": "编辑 key 信息",
"edit_feishu_bot": "编辑飞书机器人",
"edit_link": "编辑",
"feishu_api": "飞书接口",
"feishu_bot": "飞书机器人",
"feishu_bot_desc": "通过 API 直接接入飞书机器人",
"key_alias": "key 的别名,仅用于展示",
"key_tips": "你可以使用 API 密钥访问一些特定的接口(无法访问应用,访问应用需使用应用内的 API key)",
"link_name": "分享链接的名字",
"new_feishu_bot": "新增飞书机器人",
"official_account": {
"create_modal_title": "创建微信公众号接入",
"desc": "通过 API 直接接入微信公众号",
"edit_modal_title": "编辑微信公众号接入",
"name": "微信公众号接入",
"params": "微信公众号参数"
},
"publish_name": "名称",
"qpm_is_empty": "QPM 不能为空",
"qpm_tips": "每个 IP 每分钟最多提问多少次",
"request_address": "请求地址",
"show_share_link_modal_title": "开始使用",
"token_auth": "身份验证",
"token_auth_tips": "身份校验服务器地址,如填写该值,每次对话前都会向指定服务器发送一个请求,进行身份校验",
"token_auth_use_cases": "查看身份验证使用说明",
"wecom": {
"api": "企微 API",
"bot": "企业微信机器人",
"bot_desc": "通过 API 直接接入企业微信机器人",
"create_modal_title": "创建企微机器人",
"edit_modal_title": "编辑企微机器人",
"title": "发布到企业微信机器人"
}
}
\ No newline at end of file
{
"bill": {
"balance": "余额",
"buy_plan": "购买套餐",
"contact_customer_service": "联系客服",
"conversion": "兑换",
"convert_error": "兑换失败",
"convert_success": "兑换成功",
"current_token_price": "当前积分价格",
"not_need_invoice": "余额支付,无法开票",
"price": "价格",
"renew_plan": "续费套餐",
"standard_valid_tip": "套餐使用规则:系统优先使用更高级的套餐,原未用完的套餐将延后生效",
"token_expire_1year": "积分有效期一年",
"tokens": "积分",
"use_balance": "使用余额",
"use_balance_hint": "由于系统升级,原“自动续费从余额扣款”模式取消,余额充值入口关闭。您的余额可用于购买积分",
"valid_time": "生效时间",
"you_can_convert": "您可兑换",
"yuan": "元"
},
"bill_and_invoices": "账单",
"bind_inform_account_error": "绑定通知账号异常",
"bind_inform_account_success": "绑定通知账号成功",
"delete": {
"admin_failed": "删除管理员失败",
"admin_success": "删除管理员成功"
},
"has_chosen": "已选择",
"individuation": "个性化",
"login": {
"error": "登录异常",
"password_condition": "密码最多 60 位",
"success": "登录成功"
},
"name": "名称",
"notice": "通知",
"notification": {
"Bind Notification Pipe Hint": "请绑定通知接收账号,以确保您能正常接收套餐过期提醒等通知,保障您的服务正常运行。",
"remind_owner_bind": "请提醒创建者绑定通知账号"
},
"operations": "操作",
"password": {
"code_required": "验证码不能为空",
"code_send_error": "验证码发送异常",
"code_sended": "验证码已发送",
"confirm": "确认密码",
"email_phone_error": "邮箱/手机号格式错误",
"email_phone_void": "邮箱/手机号不能为空",
"get_code": "获取验证码",
"get_code_again": "s后重新获取",
"new_password": "新密码(4~20位)",
"not_match": "两次密码不一致",
"password_condition": "密码最少 4 位最多 20 位",
"password_required": "密码不能为空",
"retrieve": "找回密码",
"retrieved": "密码已找回",
"retrieved_account": "找回 {{account}} 账号",
"to_login": "去登录",
"verification_code": "验证码"
},
"permission": {
"Manage": "管理员",
"Manage tip": "团队管理员,拥有全部权限",
"Read": "仅读",
"Read desc": "成员仅可阅读相关资源,无法新建资源",
"Write": "可写",
"Write tip": "除了可读资源外,还可以新建新的资源",
"only_collaborators": "仅协作者访问",
"team_read": "团队可访问",
"team_write": "团队可编辑"
},
"permission_des": {
"manage": "可创建资源、邀请、删除成员",
"read": "成员仅可阅读相关资源,无法新建资源",
"write": "除了可读资源外,还可以新建新的资源"
},
"permissions": "权限",
"personal_information": "个人信息",
"personalization": "个性化",
"promotion_records": "推广记录",
"register": {
"confirm": "确认注册",
"register_account": "注册 {{account}} 账号",
"success": "注册成功",
"to_login": "已有账号,去登录"
},
"search_user": "搜索用户名",
"sign_out": "登出",
"synchronization": {
"button": "立即同步",
"placeholder": "请输入同步标签",
"title": "填写标签同步链接,点击同步按钮即可同步"
},
"team": {
"Add manager": "添加管理员",
"add_collaborator": "添加协作者",
"manage_collaborators": "管理协作者",
"no_collaborators": "暂无协作者"
},
"usage": {
"feishu": "飞书",
"official_account": "公众号",
"share": "分享链接",
"wecom": "企业微信"
},
"usage_record": "使用记录"
}
{
"Code": "代码",
"Confirm_sync_node": "将会更新至最新的节点配置,不存在模板中的字段将会被删除(包括所有自定义字段)。\n如果字段较为复杂,建议您先复制一份节点,再更新原来的节点,便于参数复制。",
"Quote_prompt_setting": "引用提示词配置",
"about_xxx_question": "关于 xxx 的问题",
"Variable.Variable type": "变量类型",
"Variable_name": "变量名",
"add_new_input": "新增输入",
"add_new_output": "新增输出",
"append_application_reply_to_history_as_new_context": "将该应用回复内容拼接到历史记录中,作为新的上下文返回",
"application_call": "应用调用",
"assigned_reply": "指定回复",
"choose_another_application_to_call": "选择一个其他应用进行调用",
"classification_result": "分类结果",
"code": {
"Reset template": "还原模板",
"Reset template confirm": "确认还原代码模板?将会重置所有输入和输出至模板值,请注意保存当前代码。"
},
"code_execution": "代码运行",
"collection_metadata_filter": "集合元数据过滤",
"complete_extraction_result": "完整提取结果",
"complete_extraction_result_description": "一个 JSON 字符串,例如:{\"name:\":\"YY\",\"Time\":\"2023/7/2 18:00\"}",
"concatenation_result": "拼接结果",
"concatenation_text": "拼接文本",
"condition_checker": "判断器",
"confirm_delete_field_tip": "确认删除该字段?",
"contains": "包含",
"content_to_retrieve": "需要检索的内容",
"content_to_search": "需要检索的内容",
"create_link_error": "创建链接异常",
"custom_feedback": "自定义反馈",
"custom_input": "自定义输入",
"custom_plugin_output": "自定义插件输出",
"dataset_quote_role": "角色",
"dataset_quote_role_system_option_desc": "历史记录连贯优先(推荐)",
"dataset_quote_role_tip": "设置为 System 时,将会把知识库引用内容放置到 system 消息中,可以确保历史记录的连贯性,但约束效果可能不佳,需要多调试。\n设置为 User 时,将会把知识库引用内容放置到 user 消息中,并且需要指定 {{question}} 变量位置。会对历史记录连贯性有一定影响,但通常约束效果更优。",
"dataset_quote_role_user_option_desc": "强约束优先",
"delete_api": "确认删除该API密钥?删除后该密钥立即失效,对应的对话日志不会删除,请确认!",
"dynamic_input_description": "接收前方节点的输出值作为变量,这些变量可以被 Laf 请求参数使用。",
"dynamic_input_description_concat": "可以引用其他节点的输出,作为文本拼接的变量,输入 / 唤起变量列表",
"edit_input": "编辑输入",
"end_with": "结束为",
"error_info_returns_empty_on_success": "代码运行错误信息,成功时返回空",
"execute_a_simple_script_code_usually_for_complex_data_processing": "执行一段简单的脚本代码,通常用于进行复杂的数据处理。",
"execute_different_branches_based_on_conditions": "根据一定的条件,执行不同的分支。",
"execution_error": "运行错误",
"extraction_requirements_description": "提取要求描述",
"extraction_requirements_description_detail": "给AI一些对应的背景知识或要求描述,引导AI更好的完成任务。\\n该输入框可使用全局变量。",
"extraction_requirements_placeholder": "例如: \\n1. 当前时间为: {{cTime}}。你是一个实验室预约助手,你的任务是帮助用户预约实验室,从文本中获取对应的预约信息。\\n2. 你是谷歌搜索助手,需要从文本中提取出合适的搜索词。",
"feedback_text": "反馈的文本",
"field_description": "字段描述",
"field_description_placeholder": "描述该输入字段的功能,如果为工具调用参数,则该描述会影响模型生成的质量",
"field_name_already_exists": "字段名已经存在",
"field_required": "必填",
"field_used_as_tool_input": "作为工具调用参数",
"form_input_result": "用户完整输入结果",
"form_input_result_tip": "一个包含完整结果的对象",
"filter_description": "目前支持标签和创建时间过滤,需按照以下格式填写:\n{\n \"tags\": {\n \"$and\": [\"标签 1\",\"标签 2\"],\n \"$or\": [\"有 $and 标签时,and 生效,or 不生效\"]\n },\n \"createTime\": {\n \"$gte\": \"YYYY-MM-DD HH:mm 格式即可,集合的创建时间大于该时间\",\n \"$lte\": \"YYYY-MM-DD HH:mm 格式即可,集合的创建时间小于该时间,可和 $gte 共同使用\"\n }\n}",
"full_field_extraction": "字段完全提取",
"full_field_extraction_description": "提取字段全部填充时返回 true (模型提取或使用默认值均属于成功)",
"full_response_data": "完整响应数据",
"greater_than": "大于",
"greater_than_or_equal_to": "大于等于",
"greeting": "打招呼",
"http_raw_response_description": "HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。",
"http_request": "HTTP 请求",
"http_request_error_info": "HTTP请求错误信息,成功时返回空",
"ifelse": {
"Input value": "输入值",
"Select value": "选择值"
},
"input_description": "字段描述",
"input_variable_list": "可输入 / 唤起变量列表",
"intro_assigned_reply": "该模块可以直接回复一段指定的内容。常用于引导、提示。非字符串内容传入时,会转成字符串进行输出。",
"intro_custom_feedback": "该模块被触发时,会给当前的对话记录增加一条反馈。可用于自动记录对话效果等。",
"intro_custom_plugin_output": "自定义配置外部输出,使用插件时,仅暴露自定义配置的输出",
"intro_http_request": "可以发出一个 HTTP 请求,实现更为复杂的操作(联网搜索、数据库查询等)",
"intro_knowledge_base_search_merge": "可以将多个知识库搜索结果进行合并输出。使用 RRF 的合并方式进行最终排序输出。",
"intro_laf_function_call": "可以调用Laf账号下的云函数。",
"intro_plugin_input": "可以配置插件需要哪些输入,利用这些输入来运行插件",
"intro_question_classification": "根据用户的历史记录和当前问题判断该次提问的类型。可以添加多组问题类型,下面是一个模板例子:\n类型1: 打招呼\n类型2: 关于商品“使用”问题\n类型3: 关于商品“购买”问题\n类型4: 其他问题",
"intro_question_optimization": "使用问题优化功能,可以提高知识库连续对话时搜索的精度。使用该功能后,会先利用 AI 根据上下文构建一个或多个新的检索词,这些检索词更利于进行知识库搜索。该模块已内置在知识库搜索模块中,如果您仅进行一次知识库搜索,可直接使用知识库内置的补全功能。",
"intro_text_concatenation": "可对固定或传入的文本进行加工后输出,非字符串类型数据最终会转成字符串类型。",
"intro_text_content_extraction": "可从文本中提取指定的数据,例如:sql语句、搜索关键词、代码等",
"intro_tool_call_termination": "该模块需配置工具调用使用。当该模块被执行时,本次工具调用将会强制结束,并且不再调用AI针对工具调用结果回答问题。",
"is_empty": "为空",
"is_equal_to": "等于",
"is_not_empty": "不为空",
"is_not_equal": "不等于",
"judgment_result": "判断结果",
"knowledge_base_reference": "知识库引用",
"knowledge_base_search_merge": "知识库搜索引用合并",
"laf_function_call_test": "Laf 函数调用(测试)",
"length_equal_to": "长度等于",
"length_greater_than": "长度大于",
"length_greater_than_or_equal_to": "长度大于等",
"length_less_than": "长度小于",
"length_less_than_or_equal_to": "长度小于等于",
"length_not_equal_to": "长度不等于",
"less_than": "小于",
"less_than_or_equal_to": "小于等于",
"max_dialog_rounds": "最多携带多少轮对话记录",
"max_tokens": "最大 Tokens",
"mouse_priority": "鼠标优先",
"new_context": "新的上下文",
"not_contains": "不包含",
"only_the_reference_type_is_supported": "仅支持引用类型",
"optional_value_type": "可选的数据类型",
"optional_value_type_tip": "可以指定 1 个或多个数据类型,用户在动态添加字段时,仅可选择配置的类型",
"other_questions": "其他问题",
"pan_priority": "触摸板优先",
"pass_returned_object_as_output_to_next_nodes": "将代码中 return 的对象作为输出,传递给后续的节点。变量名需要对应 return 的 key",
"plugin": {
"Instruction_Tip": "可以配置一段说明,以解释该插件的用途。每次使用插件前,会显示该段说明。支持标准 Markdown 语法。",
"Instructions": "使用说明"
},
"plugin_input": "插件输入",
"question_classification": "问题分类",
"question_optimization": "问题优化",
"quote_content_placeholder": "可以自定义引用内容的结构,以更好的适配不同场景。可以使用一些变量来进行模板配置\n{{q}} - 主要内容\n{{a}} - 辅助数据\n{{source}} - 来源名\n{{sourceId}} - 来源ID\n{{index}} - 第 n 个引用",
"quote_content_tip": "可以自定义引用内容的结构,以更好的适配不同场景。可以使用一些变量来进行模板配置\n{{q}} - 主要内容\n{{a}} - 辅助数据\n{{source}} - 来源名\n{{sourceId}} - 来源ID\n{{index}} - 第 n 个引用\n他们都是可选的,下面是默认值:\n{{default}}",
"quote_num": "引用{{num}}",
"quote_prompt_tip": "可以用 {{quote}} 来插入引用内容模板,使用 {{question}} 来插入问题(Role=user)。\n下面是默认值:\n{{default}}",
"quote_role_system_tip": "请注意从“引用模板提示词”中移除 {{question}} 变量",
"quote_role_user_tip": "请注意在“引用模板提示词”中添加 {{question}} 变量",
"raw_response": "原始响应",
"regex": "正则",
"reply_text": "回复的文本",
"request_error": "请求错误",
"response": {
"Code log": "Log 日志",
"Custom inputs": "自定义输入",
"Custom outputs": "自定义输出",
"Error": "错误信息",
"Read file result": "文档解析结果预览",
"read files": "解析的文档"
},
"select_an_application": "选择一个应用",
"select_another_application_to_call": "可以选择一个其他应用进行调用",
"special_array_format": "特殊数组格式,搜索结果为空时,返回空数组。",
"start_with": "开始为",
"target_fields_description": "由 '描述' 和 'key' 组成一个目标字段,可提取多个目标字段",
"template": {
"ai_chat": "AI 对话",
"ai_chat_intro": "AI 大模型对话",
"dataset_search": "知识库搜索",
"dataset_search_intro": "调用“语义检索”和“全文检索”能力,从“知识库”中查找可能与问题相关的参考内容",
"system_config": "系统配置",
"tool_call": "工具调用",
"tool_call_intro": "通过AI模型自动选择一个或多个功能块进行调用,也可以对插件进行调用。",
"workflow_start": "流程开始"
},
"text_concatenation": "文本拼接",
"text_content_extraction": "文本内容提取",
"text_to_extract": "需要提取的文本",
"these_variables_will_be_input_parameters_for_code_execution": "这些变量会作为代码的运行的输入参数",
"tool_call_termination": "工具调用终止",
"tool_input": "工具参数",
"tool_custom_field": "自定义工具变量",
"tool_field": "工具参数配置",
"tool_params.enum_placeholder": "apple \npeach \nwatermelon",
"tool_params.enum_values": "枚举值(可选)",
"tool_params.enum_values_tip": "列举出该字段可能的值,每行一个",
"tool_params.params_description": "参数描述",
"tool_params.params_description_placeholder": "姓名/年龄/SQL 语句...",
"tool_params.params_name": "参数名",
"tool_params.params_name_placeholder": "name/age/sql",
"tool_params.tool_params_result": "参数配置结果",
"trigger_after_application_completion": "将在应用完全结束后触发",
"update_link_error": "更新链接异常",
"update_specified_node_output_or_global_variable": "可以更新指定节点的输出值或更新全局变量",
"use_user_id": "使用者 ID",
"user_form_input_config": "表单配置",
"user_form_input_description": "描述",
"user_form_input_name": "标题",
"user_question": "用户问题",
"variable_description": "变量描述",
"user_question_tool_desc": "用户输入的问题(问题需要完善)",
"variable_picker_tips": "可输入节点名或变量名搜索",
"variable_update": "变量更新",
"workflow": {
"My edit": "我的编辑",
"Switch_success": "切换成功",
"Team cloud": "团队云端",
"exit_tips": "您的更改尚未保存,「直接退出」将不会保存您的编辑记录。"
}
}
body input,
body select {
--input-font-size: var(--chakra-fontSizes-sm) !important;
}
.chakra-tooltip {
font-size: var(--chakra-fontSizes-xs) !important;
}
#nprogress .bar {
background: '#1237b3' !important; //自定义颜色
}
.textEllipsis {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.textEllipsis2 {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
.textEllipsis3 {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
.grecaptcha-badge {
display: none !important;
}
.textlg {
background: linear-gradient(to bottom right, #1237b3 0%, #3370ff 40%, #4e83fd 80%, #85b1ff 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
span[tabindex='0'] {
line-height: 1;
}
@keyframes zoomStopIcon {
0% {
transform: scale(0.8);
}
100% {
transform: scale(1.2);
}
}
.react-flow__panel.react-flow__attribution {
z-index: 0;
left: 0;
background: transparent;
}
.react-flow__handle {
&.connecting {
border-color: #039855 !important;
& .flow-handle {
border-color: #039855 !important;
}
}
}
@use './reactflow.scss';
@use './default.scss';
@use './chakraui.scss';
body,
h1,
h2,
h3,
h4,
hr,
p,
blockquote,
dl,
dt,
dd,
ul,
ol,
li,
pre,
form,
fieldset,
legend,
button,
input,
textarea,
th,
td,
svg {
margin: 0;
}
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: transparent;
border-radius: 2px;
}
::-webkit-scrollbar-thumb {
background: rgba(189, 193, 197, 0.7);
border-radius: 2px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(189, 193, 197, 1);
}
div {
&::-webkit-scrollbar-thumb {
background: rgba(189, 193, 197, 0.7) !important;
transition: background 1s;
}
&::-webkit-scrollbar-thumb:hover {
background: rgba(189, 193, 197, 1) !important;
}
}
input::placeholder,
textarea::placeholder {
font-size: var(--chakra-fontSizes-mini);
}
* {
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
-webkit-focus-ring-color: rgba(0, 0, 0, 0);
outline: none;
box-sizing: border-box;
}
#__next {
height: 100%;
}
@media (max-width: 900px) {
html {
font-size: 14px;
}
::-webkit-scrollbar {
width: 2px;
height: 2px;
}
}
@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
body {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
}
import { extendTheme, defineStyleConfig, ComponentStyleConfig } from '@chakra-ui/react';
import {
modalAnatomy,
switchAnatomy,
selectAnatomy,
numberInputAnatomy,
checkboxAnatomy,
tableAnatomy,
radioAnatomy
} from '@chakra-ui/anatomy';
import { createMultiStyleConfigHelpers, defineStyle } from '@chakra-ui/styled-system';
const { definePartsStyle: modalPart, defineMultiStyleConfig: modalMultiStyle } =
createMultiStyleConfigHelpers(modalAnatomy.keys);
const { definePartsStyle: switchPart, defineMultiStyleConfig: switchMultiStyle } =
createMultiStyleConfigHelpers(switchAnatomy.keys);
const { definePartsStyle: selectPart, defineMultiStyleConfig: selectMultiStyle } =
createMultiStyleConfigHelpers(selectAnatomy.keys);
const { definePartsStyle: numInputPart, defineMultiStyleConfig: numInputMultiStyle } =
createMultiStyleConfigHelpers(numberInputAnatomy.keys);
const { definePartsStyle: checkBoxPart, defineMultiStyleConfig: checkBoxMultiStyle } =
createMultiStyleConfigHelpers(checkboxAnatomy.keys);
const { definePartsStyle: tablePart, defineMultiStyleConfig: tableMultiStyle } =
createMultiStyleConfigHelpers(tableAnatomy.keys);
const { definePartsStyle: radioParts, defineMultiStyleConfig: radioStyle } =
createMultiStyleConfigHelpers(radioAnatomy.keys);
const shadowLight = '0px 0px 0px 2.4px rgba(51, 112, 255, 0.15)';
// 按键
const Button = defineStyleConfig({
baseStyle: {
_active: {
transform: 'scale(0.98)'
},
_disabled: {
transform: 'none !important',
_hover: {
filter: 'none'
}
}
},
sizes: {
xs: {
fontSize: 'xs',
px: '2',
py: '0',
h: '24px',
fontWeight: 'normal',
borderRadius: 'sm'
},
xsSquare: {
fontSize: 'xs',
px: '0',
py: '0',
h: '24px',
w: '24px',
fontWeight: 'normal',
borderRadius: 'sm'
},
sm: {
fontSize: 'sm',
px: '3',
py: 0,
fontWeight: 'normal',
h: '30px',
borderRadius: 'sm'
},
smSquare: {
fontSize: 'sm',
px: '0',
py: 0,
fontWeight: 'normal',
h: '30px',
w: '30px',
borderRadius: 'sm'
},
md: {
fontSize: 'sm',
px: '4',
py: 0,
h: '34px',
fontWeight: 'normal',
borderRadius: 'md'
},
mdSquare: {
fontSize: 'sm',
px: '0',
py: 0,
h: '34px',
w: '34px',
fontWeight: 'normal',
borderRadius: 'md'
},
lg: {
fontSize: 'md',
px: '4',
py: 0,
h: '40px',
fontWeight: 'normal',
borderRadius: 'lg'
},
lgSquare: {
fontSize: 'md',
px: '0',
py: 0,
h: '40px',
w: '40px',
fontWeight: 'normal',
borderRadius: 'lg'
}
},
variants: {
primary: {
bg: 'primary.600',
color: 'white',
border: 'none',
boxShadow: '0px 0px 1px 0px rgba(19, 51, 107, 0.08), 0px 1px 2px 0px rgba(19, 51, 107, 0.05)',
_hover: {
filter: 'brightness(120%)'
},
_disabled: {
bg: 'primary.7 !important'
}
},
primaryOutline: {
color: 'primary.600',
border: '1px solid',
borderColor: 'primary.300',
bg: 'white',
transition: 'background 0.1s',
boxShadow: '1',
_hover: {
bg: 'primary.1'
},
_active: {
color: 'primary.600'
},
_disabled: {
bg: 'white !important'
}
},
primaryGhost: {
color: 'primary.600',
border: '1px solid',
borderColor: 'primary.300',
bg: 'primary.50',
transition: 'background 0.1s',
boxShadow: '1',
_hover: {
bg: 'primary.600',
color: 'white',
borderColor: 'primary.600'
},
_disabled: {
color: 'primary.600 !important',
bg: 'primary.50 !important',
borderColor: 'primary.300 !important'
}
},
whiteBase: {
color: 'myGray.600',
border: '1px solid',
borderColor: 'myGray.250',
bg: 'white',
transition: 'background 0.1s',
boxShadow: '0px 0px 1px 0px rgba(19, 51, 107, 0.08), 0px 1px 2px 0px rgba(19, 51, 107, 0.05)',
_hover: {
color: 'primary.600'
},
_active: {
color: 'primary.600'
},
_disabled: {
color: 'myGray.600 !important'
}
},
whitePrimary: {
color: 'myGray.600',
border: '1px solid',
borderColor: 'myGray.250',
bg: 'white',
transition: 'background 0.1s',
boxShadow: '0px 0px 1px 0px rgba(19, 51, 107, 0.08), 0px 1px 2px 0px rgba(19, 51, 107, 0.05)',
_hover: {
color: 'primary.600',
background: 'primary.1',
borderColor: 'primary.300'
},
_active: {
color: 'primary.600'
},
_disabled: {
color: 'myGray.600 !important'
}
},
whiteDanger: {
color: 'myGray.600',
border: '1px solid',
borderColor: 'myGray.250',
bg: 'white',
transition: 'background 0.1s',
boxShadow: '0px 0px 1px 0px rgba(19, 51, 107, 0.08), 0px 1px 2px 0px rgba(19, 51, 107, 0.05)',
_hover: {
color: 'red.600',
background: 'red.1',
borderColor: 'red.300'
},
_active: {
color: 'red.600'
}
},
grayBase: {
bg: 'myGray.150',
color: 'myGray.900',
_hover: {
color: 'primary.600',
bg: 'primary.50'
},
_disabled: {
bg: 'myGray.50 !important'
}
},
grayDanger: {
bg: 'myGray.150',
color: 'myGray.900',
_hover: {
color: 'red.600',
background: 'red.1',
borderColor: 'red.300'
},
_active: {
color: 'red.600'
}
},
transparentBase: {
color: 'myGray.800',
fontWeight: '500',
bg: 'transparent',
transition: 'background 0.1s',
_hover: {
bg: 'myGray.150'
},
_active: {
bg: 'myGray.150'
},
_disabled: {
color: 'myGray.800 !important'
}
},
transparentDanger: {
color: 'myGray.800',
fontWeight: '500',
bg: 'transparent',
transition: 'background 0.1s',
_hover: {
bg: 'myGray.150',
color: 'red.600'
},
_active: {
bg: 'myGray.150'
},
_disabled: {
color: 'myGray.800 !important'
}
},
dangerFill: {
bg: 'red.600',
color: 'white',
border: 'none',
boxShadow: '0px 0px 1px 0px rgba(19, 51, 107, 0.08), 0px 1px 2px 0px rgba(19, 51, 107, 0.05)',
_hover: {
filter: 'brightness(120%)'
},
_disabled: {
bg: 'red.200 !important'
}
}
},
defaultProps: {
size: 'md',
variant: 'primary'
}
});
const Input: ComponentStyleConfig = {
sizes: {
sm: defineStyle({
field: {
h: '32px',
borderRadius: 'md'
}
}),
md: defineStyle({
field: {
h: '34px',
borderRadius: 'md'
}
})
},
variants: {
outline: {
field: {
border: '1px solid',
borderColor: 'borderColor.low',
_focus: {
borderColor: 'primary.500',
boxShadow: shadowLight,
bg: 'white'
},
_disabled: {
color: 'myGray.400',
bg: 'myWhite.300'
}
}
}
},
defaultProps: {
size: 'md',
variant: 'outline'
}
};
const NumberInput = numInputMultiStyle({
sizes: {
sm: defineStyle({
field: {
h: '32px',
borderRadius: 'md',
fontsize: 'sm'
}
}),
md: defineStyle({
field: {
h: '40px',
borderRadius: 'md',
fontsize: 'sm'
}
})
},
variants: {
outline: numInputPart({
field: {
bg: 'myGray.50',
border: '1px solid',
borderColor: 'myGray.200',
_focus: {
borderColor: 'primary.500 !important',
boxShadow: `${shadowLight} !important`,
bg: 'transparent'
},
_disabled: {
color: 'myGray.400 !important',
bg: 'myWhite.300 !important'
}
},
stepper: {
bg: 'transparent',
border: 'none',
color: 'myGray.600',
_active: {
color: 'primary.500'
}
}
})
},
defaultProps: {
variant: 'outline'
}
});
const Textarea: ComponentStyleConfig = {
variants: {
outline: {
border: '1px solid',
borderRadius: 'md',
borderColor: 'myGray.200',
fontSize: 'sm',
_hover: {
borderColor: ''
},
_focus: {
borderColor: 'primary.500',
boxShadow: shadowLight,
bg: 'white'
}
}
},
defaultProps: {
size: 'md',
variant: 'outline'
}
};
const Switch = switchMultiStyle({
baseStyle: switchPart({
track: {
bg: 'myGray.100',
borderWidth: '1px',
borderColor: 'borders.base',
_checked: {
bg: 'primary.600'
}
}
}),
defaultProps: {
size: 'md'
}
});
const Select = selectMultiStyle({
variants: {
outline: selectPart({
field: {
borderColor: 'myGray.200',
_focusWithin: {
boxShadow: shadowLight,
borderColor: 'primary.500'
}
}
})
}
});
const Radio = radioStyle({
baseStyle: radioParts({
control: {
_hover: {
borderColor: 'primary.300',
bg: 'primary.50'
},
_checked: {
borderColor: 'primary.600',
bg: 'primary.50',
boxShadow: shadowLight,
_before: {
bg: 'primary.600'
},
_hover: {
bg: 'primary.50'
}
}
}
})
});
const Checkbox = checkBoxMultiStyle({
baseStyle: checkBoxPart({
label: {
fontFamily: 'mono' // change the font family of the label
},
control: {
borderRadius: 'xs',
bg: 'none',
_checked: {
bg: 'primary.50',
borderColor: 'primary.600',
borderWidth: '1px',
color: 'primary.600',
boxShadow: `${shadowLight} !important`,
_hover: {
bg: 'primary.50'
}
},
_hover: {
borderColor: 'primary.400'
}
}
})
});
const Modal = modalMultiStyle({
sizes: {
md: modalPart({
body: {
py: 4,
px: 7
},
footer: {
pt: 2
}
}),
lg: modalPart({
body: {
pt: 8,
pb: 6,
px: '3.25rem'
},
footer: {
pb: 8,
px: '3.25rem',
pt: 0
}
})
}
});
const Table = tableMultiStyle({
sizes: {
md: defineStyle({
table: {
fontsize: 'sm'
},
thead: {
tr: {
bg: 'myGray.100',
fontSize: 'sm',
th: {
borderBottom: 'none',
overflow: 'hidden',
'&:first-of-type': {
borderLeftRadius: 'md'
},
'&:last-of-type': {
borderRightRadius: 'md'
}
}
}
},
tbody: {
tr: {
td: {
overflow: 'hidden',
'&:first-of-type': {
borderLeftRadius: 'md'
},
'&:last-of-type': {
borderRightRadius: 'md'
}
}
}
}
})
},
defaultProps: {
size: 'md'
}
});
// 全局主题
export const theme = extendTheme({
styles: {
global: {
'html, body': {
color: 'myGray.600',
fontWeight: 'normal',
height: '100%',
overflow: 'hidden',
fontSize: '16px'
},
a: {
color: 'primary.600'
},
'*': {
_focusVisible: {
boxShadow: 'none'
}
}
}
},
colors: {
myWhite: {
100: '#FEFEFE',
200: '#FDFDFE',
300: '#FBFBFC',
400: '#F8FAFB',
500: '#F6F8F9',
600: '#F4F6F8',
700: '#C3C5C6',
800: '#929495',
900: '#626263',
1000: '#313132'
},
myGray: {
'05': 'rgba(17, 24, 36, 0.05)',
1: 'rgba(17, 24, 36, 0.1)',
15: 'rgba(17, 24, 36, 0.15)',
25: '#FBFBFC',
50: '#F7F8FA',
100: '#F4F4F7',
150: '#F0F1F6',
200: '#E8EBF0',
250: '#DFE2EA',
300: '#C4CBD7',
400: '#8A95A7',
500: '#667085',
600: '#485264',
700: '#383F50',
800: '#1D2532',
900: '#111824'
},
primary: {
1: 'rgba(51, 112, 255, 0.1)',
'015': 'rgba(51, 112, 255, 0.15)',
3: 'rgba(51, 112, 255, 0.3)',
5: 'rgba(51, 112, 255, 0.5)',
7: 'rgba(51, 112, 255, 0.7)',
9: 'rgba(51, 112, 255, 0.9)',
50: '#F0F4FF',
100: '#E1EAFF',
200: '#C5D7FF',
300: '#94B5FF',
400: '#5E8FFF',
500: '#487FFF',
600: '#3370FF',
700: '#2B5FD9',
800: '#2450B5',
900: '#1D4091'
},
blue: {
1: 'rgba(51, 112, 255, 0.1)',
'015': 'rgba(51, 112, 255, 0.15)',
3: 'rgba(51, 112, 255, 0.3)',
5: 'rgba(51, 112, 255, 0.5)',
7: 'rgba(51, 112, 255, 0.7)',
9: 'rgba(51, 112, 255, 0.9)',
50: '#F0F4FF',
100: '#E1EAFF',
200: '#C5D7FF',
300: '#94B5FF',
400: '#5E8FFF',
500: '#487FFF',
600: '#3370FF',
700: '#2B5FD9',
800: '#2450B5',
900: '#1D4091'
},
red: {
1: 'rgba(217,45,32,0.1)',
3: 'rgba(217,45,32,0.3)',
5: 'rgba(217,45,32,0.5)',
25: '#FFFBFA',
50: '#FEF3F2',
100: '#FEE4E2',
200: '#FECDCA',
300: '#FDA29B',
400: '#F97066',
500: '#F04438',
600: '#D92D20',
700: '#B42318',
800: '#912018',
900: '#7A271A'
},
green: {
25: '#F9FEFB',
50: '#EDFBF3',
100: '#D1FADF',
200: '#B9F4D1',
300: '#76E4AA',
400: '#32D583',
500: '#12B76A',
600: '#039855',
700: '#027A48',
800: '#05603A',
900: '#054F31'
},
yellow: {
25: '#FFFDFA',
50: '#FFFAEB',
100: '#FEF0C7',
200: '#FEDF89',
300: '#F5C149',
400: '#FDB022',
500: '#F79009',
600: '#DC6803',
700: '#B54708',
800: '#93370D',
900: '#7A2E0E'
},
adora: {
25: '#FCFCFF',
50: '#F0EEFF',
100: '#E4E1FC',
200: '#D3CAFF',
300: '#B6A8FC',
400: '#9E8DFB',
500: '#8774EE',
600: '#6F5DD7',
700: '#5E4EBD',
800: '#4E4198',
900: '#42387D'
},
borderColor: {
low: '#E8EBF0',
base: '#DFE2EA',
high: '#C4CBD7',
highest: '#8A95A7'
}
},
fonts: {
body: 'PingFang,Noto Sans,-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"'
},
fontSizes: {
mini: '0.75rem',
xs: '0.8rem',
sm: '0.875rem',
md: '1rem',
lg: '1.25rem',
xl: '1.5rem',
'2xl': '1.75rem',
'3xl': '2rem',
'4xl': '2.25rem',
'5xl': '2.8rem',
'6xl': '3.6rem'
},
borders: {
sm: '1px solid #E8EBF0',
base: '1px solid #DFE2EA',
md: '1px solid #DAE0E2',
lg: '1px solid #D0E0E2'
},
radii: {
none: '0',
xs: '0.25rem',
sm: '0.375rem',
md: '0.5rem',
semilg: '0.625rem',
lg: '0.75rem',
xl: '1rem'
},
shadows: {
1: '0px 1px 2px 0px rgba(19, 51, 107, 0.05), 0px 0px 1px 0px rgba(19, 51, 107, 0.08)',
1.5: '0px 1px 2px 0px rgba(19, 51, 107, 0.10), 0px 0px 1px 0px rgba(19, 51, 107, 0.15)',
2: '0px 4px 4px 0px rgba(19, 51, 107, 0.05), 0px 0px 1px 0px rgba(19, 51, 107, 0.08)',
3: '0px 4px 10px 0px rgba(19, 51, 107, 0.08), 0px 0px 1px 0px rgba(19, 51, 107, 0.08)',
3.5: '0px 4px 10px 0px rgba(19, 51, 107, 0.10), 0px 0px 1px 0px rgba(19, 51, 107, 0.10)',
4: '0px 12px 16px -4px rgba(19, 51, 107, 0.20), 0px 0px 1px 0px rgba(19, 51, 107, 0.20)',
5: '0px 20px 24px -8px rgba(19, 51, 107, 0.15), 0px 0px 1px 0px rgba(19, 51, 107, 0.15)',
6: '0px 24px 48px -12px rgba(19, 51, 107, 0.20), 0px 0px 1px 0px rgba(19, 51, 107, 0.20)',
7: '0px 32px 64px -12px rgba(19, 51, 107, 0.20), 0px 0px 1px 0px rgba(19, 51, 107, 0.20)',
focus: shadowLight,
outline: 'none'
},
breakpoints: {
sm: '900px',
md: '1200px',
lg: '1500px',
xl: '1800px',
'2xl': '2100px'
},
lgColor: {
activeBlueGradient: 'linear-gradient(to bottom right, #d6e8ff 0%, #f0f7ff 100%)',
hoverBlueGradient: 'linear-gradient(to top left, #d6e8ff 0%, #f0f7ff 100%)',
primary: 'linear-gradient(to bottom right, #2152d9 0%,#3370ff 40%, #4e83fd 100%)',
primary2: 'linear-gradient(to bottom right, #2152d9 0%,#3370ff 30%,#4e83fd 80%, #85b1ff 100%)'
},
components: {
Button,
Input,
Textarea,
Switch,
Select,
NumberInput,
Checkbox,
Modal,
Table,
Radio
}
});
import 'i18next';
import common from '../i18n/zh/common.json';
import dataset from '../i18n/zh/dataset.json';
import app from '../i18n/zh/app.json';
import file from '../i18n/zh/file.json';
import publish from '../i18n/zh/publish.json';
import workflow from '../i18n/zh/workflow.json';
import user from '../i18n/zh/user.json';
import chat from '../i18n/zh/chat.json';
import login from '../i18n/zh/login.json';
export interface I18nNamespaces {
common: typeof common;
dataset: typeof dataset;
app: typeof app;
file: typeof file;
publish: typeof publish;
workflow: typeof workflow;
user: typeof user;
chat: typeof chat;
login: typeof login;
}
export type I18nNsType = (keyof I18nNamespaces)[];
export type NestedKeyOf<ObjectType extends object> = {
[Key in keyof ObjectType & (string | number)]: ObjectType[Key] extends object
? `${Key}` | `${Key}.${NestedKeyOf<ObjectType[Key]>}`
: `${Key}`;
}[keyof ObjectType & (string | number)];
export type ParseKeys<Ns extends keyof I18nNamespaces = keyof I18nNamespaces> = {
[K in Ns]: `${K}:${NestedKeyOf<I18nNamespaces[K]>}`;
}[Ns];
export type I18nKeyFunction = {
<Key extends ParseKeys>(key: Key): Key;
};
declare module 'i18next' {
interface CustomTypeOptions {
returnNull: false;
defaultNS: ['common', 'dataset', 'app', 'file', 'publish', 'workflow', 'user', 'chat', 'login'];
resources: I18nNamespaces;
}
}
import type { AppProps } from 'next/app';
import Script from 'next/script';
// import Layout from '@/components/Layout';
import { appWithTranslation } from 'next-i18next';
// import QueryClientContext from '@/web/context/QueryClient';
import ChakraUIContext from '@eagic/web/context/ChakraUI';
import I18nContextProvider from '@eagic/web/context/I18n';
// import { useInitApp } from '@/web/context/useInitApp';
import { useTranslation } from 'next-i18next';
import '@/web/styles/reset.scss';
// import NextHead from '@/components/common/NextHead';
function App({ Component, pageProps }: AppProps) {
// const { feConfigs, scripts, title } = useInitApp();
const { t } = useTranslation();
return (
<>
{/* <NextHead
title={title}
desc={
feConfigs?.systemDescription ||
process.env.SYSTEM_DESCRIPTION ||
`${title}${t('app:intro')}`
}
icon={feConfigs?.favicon || process.env.SYSTEM_FAVICON}
/> */}
{/* {scripts?.map((item, i) => <Script key={i} strategy="lazyOnload" {...item}></Script>)} */}
{/* <QueryClientContext> */}
<I18nContextProvider>
<ChakraUIContext>
{/* <Layout> */}
<Component {...pageProps} />
{/* </Layout> */}
</ChakraUIContext>
</I18nContextProvider>
{/* </QueryClientContext> */}
</>
);
}
export default appWithTranslation(App);
import { getAIApi } from '@eagic/service/core/ai/config';
import Diff from 'jsdiff-esm';
export type diffDetailType = {
[key: string]: Array<any> | { deliveryTimeDiff?: boolean };
fristFragmentDiff: Array<any>;
tableDiff: Array<any>;
lastFragmentDiff: Array<any>;
outherDiff: { deliveryTimeDiff?: boolean };
};
type resultType = {
diffs: diffDetailType;
diffCount: number;
};
export const diffHandler = async (newDoc: string, oldDoc: string): Promise<resultType> => {
const diffs: diffDetailType = {
fristFragmentDiff: [],
tableDiff: [],
lastFragmentDiff: [],
outherDiff: { deliveryTimeDiff: false }
};
let diffCount = 0;
const regex = /[ \t\r\n\v\f(),\[\]【】()]/g;
// const regex = /\\/g;
let splitKey = '约定如下:';
let newDocFragments = newDoc.replace(regex, '').split(splitKey);
let oldDocFragments = oldDoc.replace(regex, '').split(splitKey);
if (newDocFragments.length > 1 && oldDocFragments.length > 1) {
const contractInfo = await extractInfo(newDoc);
const compareResult = await compareContractInfo(contractInfo);
if (compareResult.includes('晚于')) {
diffs.outherDiff.deliveryTimeDiff = true;
diffCount += 1;
}
diffs.fristFragmentDiff = Diff.diffChars(
replaceFirstFragmentIgnore(oldDocFragments[0] + splitKey),
replaceFirstFragmentIgnore(newDocFragments[0] + splitKey)
);
diffCount += diffs.fristFragmentDiff.filter((diff: any) => diff.added || diff.removed).length;
splitKey = '1.2双方';
newDocFragments = newDocFragments[1].split(splitKey);
oldDocFragments = oldDocFragments[1].split(splitKey);
if (newDocFragments.length > 1 && oldDocFragments.length > 1) {
const newTableExtract = await extractTable(newDocFragments[0]);
const oldTableExtract = await extractTable(oldDocFragments[0]);
diffs.tableDiff = Diff.diffChars(
oldTableExtract.replace(regex, ''),
newTableExtract.replace(regex, '')
);
diffCount += diffs.tableDiff.filter((diff: any) => diff.added || diff.removed).length;
diffs.lastFragmentDiff = Diff.diffChars(
replaceLastFragmentIgnore(oldDocFragments[1]),
replaceLastFragmentIgnore(newDocFragments[1])
);
diffCount += diffs.lastFragmentDiff.filter((diff: any) => diff.added || diff.removed).length;
}
}
return { diffs, diffCount };
};
const extractInfo = async (newDoc: string) => {
const ai = getAIApi();
const response = await ai.chat.completions.create({
model: 'gpt-4o',
temperature: 0,
messages: [
{
role: 'system',
content: `
# Role: 合同信息提取专家
## Profile:
- 你是一位合同信息提取专家,擅长提取合同中的指定内容,按照格式输出。
## Skills:
### Skill 1:甲方或买方信息提取(输出标识为甲方或卖方)
- 提取合同中出现的所有甲方或买方信息,包括公司名称、统一社会信用代码、法定代表人、地址
- 输出甲方或买方信息列表,未提到的信息不输出
### Skill 2:乙方或卖方信息提取(输出标识为乙方或卖方)
- 提取合同中出现的所有乙方或卖方信息,包括公司名称、统一社会信用代码、法定代表人、地址
- 输出乙方或卖方信息列表,未提到的信息不输出
### Skill 3:发货与支付时间提取(输出标识为发货与支付时间)
- 提取合同中出现的发货时间和支付时间信息,请注意:;类似于(最后一批次产品对应发货款应不晚于2023年3月1日由甲方向乙方支付)中的内容才是支付时间。
## Constrains:
1. 输出格式为json且严格按照<Example>格式填充
## Workflow:
1. 阅读文件内容
2. 按照技能1提取甲方或买方信息,输出甲方或买方信息列表,未提到的信息不输出
3. 按照技能2提取乙方或卖方信息,输出乙方或卖方信息列表,未提到的信息不输出
4. 按照技能3提取发货时间和支付时间信息,输出发货时间和支付时间信息
## Example:
- 输出:
{
"甲方或卖方":{
"次数":3,
"原文信息":[{"买方(“甲方”)":"阿里云公司","统一信用代码":"123123","法定代表人":"李世民","地址":"四川省成都市高新区天府大道中段611号"},{"如致甲方":"阿里云公司","地址":"四川省成都市高新区天府大道中段611号"},{"买方(章)":"阿里云公司"}]
},
"乙方或卖方":{
"次数":4,
"原文信息":[{"卖方(“乙方”)":"通威股份有限公司","统一信用代码":"91510000207305821R","法定代表人":"刘舒琪","地址":"四川省成都市高新区天府大道中段588号"},{"户名":"通威股份有限公司"},{"如致乙方":"通威股份有限公司","地址":"四川省成都市高新区天府大道中段588号"},{"卖方(章)":"通威股份有限公司"}]
},
"发货与支付时间":{
"预计发货时间":"2023年3月3日",
"支付时间":"2023年3月1日",
},
}
## Initialization
请一步一步思考,作为<Role>,拥有<Skills>中的8项技能,严格遵守<Constrains>,按照<Workflow>流程,只需要按照<Example>输出结果
`
},
{
role: 'user',
content: newDoc
}
]
});
return response.choices[0].message.content || '';
};
const compareContractInfo = async (info: string) => {
const ai = getAIApi();
const response = await ai.chat.completions.create({
model: 'gpt-4o',
temperature: 0,
messages: [
{
role: 'system',
content: `
# Role: 合同信息提取专家
## Profile:
- 你是一位合同信息比对专家。
## Constrains:
1. 输出格式为文本,且严格按照<OutputFormat>格式填充
## Workflow:
1. 对应输出json中的“甲方公司名称一致性”字段,判断输入的所有"甲方或买方"信息中,公司名称是否一致,如果公司名称一致则输出“公司名称一致”,否则输出“公司名称不一致”
2. 对应输出json中的“乙方公司名称一致性”字段,判断输入的所有"乙方或卖方"信息中,公司名称是否一致,如果公司名称一致则输出“公司名称一致”,否则输出“公司名称不一致”
3. 对应输出json中的“乙方公司名称合规”字段,判断输入的所有"乙方或卖方"信息中,公司名称是否为“通威股份有限公司”或“通威太阳能(合肥)有限公司”,如果是则输出“通威公司”,否则输出“非通威公司”
4. 阅读输入的json信息,对应输出json中的“时间顺序”字段,判断"预计发货时间"是否比"支付时间"晚,如果支付时间比发货时间早,则输出“支付时间早于发货时间”,如果支付时间比发货时间晚,则输出“支付时间晚于发货时间”,如果其中一个没有时间,则输出“时间异常”
## OutputFormat:
甲方公司名称一致性: 公司名称一致,
乙方公司名称一致性: 公司名称不一致,
乙方公司名称合规: 非通威公司
预计发货时间: xx年xx月xx日,
支付时间: xx年xx月xx日,
时间顺序:支付时间晚于/早于发货时间/时间异常,
## Initialization
请一步一步思考,作为<Role>,拥有<Skills>中的4项技能,严格遵守<Constrains>,按照<Workflow>流程,只需要按照<OutputFormat>输出结果
`
},
{
role: 'user',
content: `输入的JSON为:${info}`
}
]
});
return response.choices[0].message.content || '';
};
const extractTable = async (tableDoc: string) => {
const ai = getAIApi();
const response = await ai.chat.completions.create({
model: 'gpt-4o',
temperature: 0,
messages: [
{
role: 'system',
content: `
# Role: 表格信息提取专家
## Profile:
你是一位表格信息提取专家,擅长提取表格中的指定内容,按照格式输出。
## Constrains:
1. 输出格式为字符串,禁止添加markdown的代码段标识,且严格按照<Example>格式填充
## Workflow:
1. 阅读文件内容
2. 提取文件中的表格的表头
3. 提取表格中备注一行的备注内容
## Example:
- 输入:
产品名称产品型号计量单位(块)计量单位(W)单价(元/W)总金额(元)预计发货时间大宝Db001122200002024/10/25金额不含税金额:¥ 增值税金额:¥ (含13%增值税金额)合计:¥ 大写: 备注组件出厂正公差为0~+5W,含税;自提不含运,乙方送货含运费;最终结算金额以实际发货产品价值总额为准;如遇国家增值税税率调整,含税合同总价作相应调整;通威标准A级组件。
- 输出:
产品名称产品型号计量单位(块)计量单位(W)单价(元/W)总金额(元)预计发货时间组件出厂正公差为0~+5W,含税;自提不含运,乙方送货含运费;最终结算金额以实际发货产品价值总额为准;如遇国家增值税税率调整,含税合同总价作相应调整;通威标准A级组件。
## Initialization
请一步一步思考,作为<Role>,严格遵守<Constrains>,按照<Workflow>流程,按照<Example>输出结果`
},
{
role: 'user',
content: tableDoc
}
]
});
return response.choices[0].message.content || '';
};
const replaceFirstFragmentIgnore = (htmlContent: string) => {
const regexStr = [
{ s: '', e: '组件' },
{ s: '合同', e: '买方“甲方”:' },
{ s: '买方“甲方”:', e: '卖方“乙方”:' }
];
return replaceIgnoreItem(htmlContent, regexStr);
};
const replaceLastFragmentIgnore = (htmlContent: string) => {
const regexStr = [
{ s: '提前', e: '个工作日' },
{ s: '下列第', e: '项执行' },
{ s: '前至少', e: '个工作日' },
{ s: '地址:', e: ',“交货地址”;' },
{ s: '收货联系人:', e: '联系号码:' },
{ s: '联系号码:', e: '3、货' },
{ s: '于生效日后', e: '个工作日内' },
{ s: '合同总金额', e: '%作为预付款' },
{ s: '发货日前', e: '个工作日内' },
{ s: '对应总金额', e: '%最后一批次产品' },
{ s: '应不晚于', e: '由甲方' },
{ s: '完成签收后', e: '个工作日内' },
{ s: '单位名称:', e: '纳税人识别号:' },
{ s: '纳税人识别号:', e: '联系电话:' },
{ s: '联系电话:', e: '地址:' },
{ s: '地址:', e: '账号:' },
{ s: '账号:', e: '开户行:' },
{ s: '开户行:', e: '3.3乙方' },
{ s: '户名:', e: '4、合同' },
{ s: '起至2024年', e: '“合同期限”。4' },
{ s: '如致甲方:', e: '如致乙方:' },
{ s: '如致乙方:', e: '联系人:' },
{ s: '联系人:', e: '地址:' },
{ s: '电话:', e: '邮政编码:' },
{ s: '邮政编码:', e: '电子邮箱:' },
{ s: '电子邮箱:', e: '4.3本合同由专用条款' },
{ s: '买方章:', e: '卖方章:' },
{ s: '卖方章:', e: '' }
];
return replaceIgnoreItem(htmlContent, regexStr);
};
const replaceIgnoreItem = (htmlContent: string, regexStr: Array<{ s: string; e: string }>) => {
let reg = new RegExp('', 'y');
let lastIndex = 0;
for (const [i, item] of regexStr.entries()) {
//找到下一个需要替换的index
if (i < regexStr.length) {
reg = new RegExp(`${escapeRegExp(regexStr[i].s)}`, 'y');
reg.lastIndex = lastIndex;
let newMatch = htmlContent.match(reg);
let tempIndex = lastIndex;
while (!newMatch && tempIndex < htmlContent.length) {
tempIndex++;
reg.lastIndex = tempIndex;
newMatch = htmlContent.match(reg);
}
if (newMatch) {
if (item.s == '') {
reg = new RegExp(`.*?${escapeRegExp(item.e)}`, 'y');
} else if (item.e == '') {
reg = new RegExp(`${escapeRegExp(item.s)}.*`, 'y');
} else {
reg = new RegExp(`${escapeRegExp(item.s)}.*?${escapeRegExp(item.e)}`, 'y');
}
reg.lastIndex = tempIndex;
htmlContent = htmlContent.replace(reg, `${item.s}${item.e}`);
lastIndex = tempIndex + item.s.length;
}
}
}
htmlContent = htmlContent.replace(/--PAGEEND--/, '');
return htmlContent;
};
// 转义正则表达式的关键字
export const escapeRegExp = (input: string) => {
// 列出正则表达式的关键字
const regexKeywords = /[-/\\^$*+?.()|[\]{}]/g;
// 使用replace方法来转义关键字
return input.replace(regexKeywords, '\\$&');
};
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@eagic/service/common/response';
import { MongoContract, MongoContractFile } from './schema';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
switch (req.method) {
case 'POST': {
break;
}
case 'GET': {
const fileId = req.query.fileId as string;
const file = await MongoContractFile.findById(fileId);
if (file) {
// 设置响应头,确保浏览器正确处理文件下载
res.setHeader('Content-Type', file.fileMimetype || 'application/octet-stream');
// 发送文件内容
res.send(file.fileBuffer);
} else {
let resObj = {
code: 500,
message: '未找到文件'
};
jsonRes(res, resObj);
}
}
}
}
export const config = {
api: {
bodyParser: false
}
};
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@eagic/service/common/response';
import { MongoContract, MongoContractFile } from './schema';
import { getUploadModel } from '@src/utils/multer';
import fs from 'fs';
import * as mammoth from 'mammoth';
import path from 'path';
import { diffHandler } from './diffHandler';
import { connectMongo } from '@src/app/service/common/mongo/init';
type ResponseType<T = any> = {
code?: number;
message?: string;
data?: T;
error?: any;
url?: string;
};
const upload = getUploadModel({
maxSize: 500 * 1024 * 1024
});
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
let resObj: ResponseType = {};
switch (req.method) {
case 'POST': {
const { file, fileId, userId } = await upload.doUpload(req, res);
if (!file) {
resObj = { code: 400, message: 'No file uploaded' };
} else {
// 读取上传文件内容
const newDocBuffer = fs.readFileSync(file.path);
await connectMongo();
const mongoFile = await MongoContractFile.create({
fileBuffer: newDocBuffer,
fileMimetype: file.mimetype
});
let fileUrl = '';
if (mongoFile) {
fileUrl = `/api/contractCompare/fileController?fileId=${mongoFile._id}`;
} else {
resObj = {
code: 500,
message: '文件存储失败'
};
break;
}
const newDoc = await mammoth.extractRawText({ buffer: newDocBuffer });
// 提取文本内容
const newDocText = newDoc.value;
const contractTemplatePath = path.join(process.cwd(), '/public/docs/contractTemplate.docx');
// 读取模板文件内容
const oldDocBuffer = fs.readFileSync(contractTemplatePath);
const oldDoc = await mammoth.extractRawText({ buffer: oldDocBuffer });
// 提取的文本内容
const oldDocText = oldDoc.value;
// 文本差异对比
const { diffs, diffCount } = await diffHandler(newDocText, oldDocText);
const now = new Date();
const createTime = now
.toLocaleDateString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
})
.replace(/\//g, '-');
try {
const contract = await MongoContract.create({
fileId,
fileUrl,
diffDetail: diffs,
contractName: file.originalname,
diffCount: diffCount,
createTime,
userId
});
// 发送文本内容回客户端
resObj = {
code: 200,
message: '处理成功',
data: contract.fileId
};
} catch (error) {
if ((error as { code: number }).code === 11000) {
resObj = {
code: 500,
message: '文件ID重复'
};
} else {
resObj = {
code: 500,
message: (error as { message: string })?.message
};
}
}
}
break;
}
case 'GET': {
const { fileId, searchText } = req.query;
await connectMongo();
// 有ID查询单个文件
if (fileId) {
const contract = await MongoContract.find({
fileId: { $regex: fileId.toString().replace(/[.*+?^${}()|[\]\\]/g, '\\$&') }
});
if (!contract) {
resObj = { code: 404, message: 'Contract not found' };
} else {
resObj = {
data: {
...contract
}
};
}
//没有ID返回全部
} else {
const contracts = await MongoContract.find({
...(searchText && {
contractName: { $regex: searchText.toString().replace(/[.*+?^${}()|[\]\\]/g, '\\$&') }
})
}).sort({ createTime: -1 });
const contracts2 = contracts.map((contract) => {
return {
...contract._doc // 直接使用合同对象
};
});
resObj = {
data: contracts2
};
}
break;
}
default:
resObj = { code: 500, message: '未定义的请求方法' };
}
res.status(200).json(resObj)
}
export const config = {
api: {
bodyParser: false
}
};
import { connectionMongo, type Model } from '@eagic/service/common/mongo';
const { Schema, model, models } = connectionMongo;
export const contractCollectionName = 'contracts';
export const contractFileCollectionName = 'contracts.files';
export const contractFeedbackCollectionName = 'contracts.feedbacks';
export type ContractModelSchema = {
_doc: Object;
_id: string;
fileId: string;
fileUrl: string;
diffDetail: Object;
contractName: string;
diffCount: number;
createTime: string;
};
const ContractSchema = new Schema({
fileId: {
type: String,
required: true,
unique: true
},
fileUrl: {
type: String,
required: true
},
contractName: {
type: String,
required: true
},
diffDetail: {
type: Object
},
diffCount: {
type: Number,
required: true
},
createTime: {
type: String,
required: true
}
});
export const MongoContract: Model<ContractModelSchema> =
models[contractCollectionName] || model(contractCollectionName, ContractSchema);
MongoContract.syncIndexes();
// contract file modal
export type ContractFileModalSchema = {
_doc: Object;
_id: string;
fileBuffer: Buffer;
fileMimetype: string;
};
const ContractFileSchema = new Schema({
fileBuffer: {
type: Buffer,
required: true
},
fileMimetype: {
type: String,
required: true
}
});
export const MongoContractFile: Model<ContractFileModalSchema> =
models[contractFileCollectionName] || model(contractFileCollectionName, ContractFileSchema);
MongoContractFile.syncIndexes();
// contract file modal
export type ContractFeedbackModalSchema = {
_doc: Object;
_id: string;
contractName: string;
diffNumber: number;
diffContent: string,
feedbackContent: string,
createTime: string
};
const ContractFeedbackSchema = new Schema({
contractName: {
type: String,
required: true
},
diffNumber: {
type: Number,
required: true
},
diffContent: {
type: String,
required: true
},
feedbackContent: {
type: String,
required: true
},
createTime: {
type: String,
required: true
}
});
export const MongoContractFeedback: Model<ContractFeedbackModalSchema> =
models[contractFeedbackCollectionName] || model(contractFeedbackCollectionName, ContractFeedbackSchema);
MongoContractFeedback.syncIndexes();
\ No newline at end of file
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import {
Box,
Button,
Flex,
useTheme,
Text,
effect,
useDisclosure,
SkeletonText
} from '@chakra-ui/react';
import styles from './index.module.scss';
import ContractCompareLayout from './layout';
import { useRouter } from 'next/router';
import { GET } from '@/web/common/api/request';
import { serviceSideProps } from '@src/utils/i18n';
import {
ChevronDownIcon,
ChevronLeftIcon,
ChevronRightIcon,
ChevronUpIcon
} from '@chakra-ui/icons';
import MyIcon from '@eagic/web/components/common/Icon';
import dynamic from 'next/dynamic';
import { diffDocument } from '@/web/common/utils/diffDocUtil';
import { diffDetailType } from '@src/pages/api/contractCompare/diffHandler';
import ContractDiffFeedback from './components/ContractDiffFeedback';
const DiffDocument = dynamic(
() => import('./components/DiffDocument'),
{ ssr: false }
);
type contractType = {
fileId: string;
diffDetail: diffDetailType;
contractName: string;
diffCount: number;
createTime: string;
fileUrl: string;
};
const DetailPage = ({ params }: { params: any }) => {
const [viewModal, setViewModal] = useState<string>('bigMap');
const popoverContentRef = useRef<HTMLDivElement>(null);
const polylineRef = useRef<SVGPolylineElement>(null);
const polygonRef = useRef<SVGPolygonElement>(null);
const rectRef = useRef<SVGRectElement>(null);
const contractNodeRef = useRef<HTMLDivElement>(null);
const [currentDiffIndex, setCurrentDiffIndex] = useState<number>(0);
const [diffTypeIsAdd, setDiffTypeIsAdd] = useState<boolean>(false);
const [popoverContent, setPopoverContent] = useState<string>('');
const [diffCount, setDiffCount] = useState<number>(0);
const [contract, setContract] = useState<contractType>();
const [newDocRenderSuccess, setNewDocRenderSuccess] = useState<boolean>(false);
const [diffRenderSuccess, setdiffRenderSuccess] = useState<boolean>(false);
const router = useRouter();
const { id } = params || router.query;
const {
isOpen: isOpenFeedback,
onClose: onCloseFeedback,
onOpen: onOpenFeedback
} = useDisclosure();
useEffect(() => {
GET<any>(`/contractCompare/mainHandler?fileId=${id}`).then((resp) => {
const contract = resp[0];
setContract(contract);
setDiffCount(contract.diffCount);
});
}, []);
useEffect(() => {
if (newDocRenderSuccess && !diffRenderSuccess) {
const newDocxNode = document.querySelector('.new-docx') as HTMLDivElement;
(newDocxNode.querySelector('.vue-office-docx') as HTMLElement).style.height = 'auto';
const docxWrapper = newDocxNode.querySelector('.docx-wrapper') as HTMLElement;
docxWrapper.style.backgroundColor = '#fff';
docxWrapper.style.padding = '0';
if (newDocxNode && contract && contract.diffCount > 0) {
// setContractNodeRef(newDocxNode);
diffDocument(newDocxNode, contract.diffDetail);
}
setdiffRenderSuccess(true);
}
}, [newDocRenderSuccess]);
useEffect(() => {
if (diffRenderSuccess && polylineRef && popoverContentRef && polygonRef && rectRef) {
const targetElement = document.querySelector('#diff-1');
if (targetElement) {
targetElement.nodeName == 'SPAN' ? setDiffTypeIsAdd(true) : setDiffTypeIsAdd(false);
targetElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
setPopoverContent(targetElement.innerHTML);
polylineRef.current && computePopPostion(1);
setCurrentDiffIndex(1);
// document
// .querySelector('.new-docx')
// ?.parentNode?.addEventListener(
// 'scroll',
// () => polylineRef.current && computePopPostion(1)
// );
}
}
}, [diffRenderSuccess, polylineRef, popoverContentRef, polygonRef, rectRef]);
//计算虚线 三角形 矩形的位置
const computePopPostion = (currentDiffIndex: number) => {
const popoverContentCurrent = popoverContentRef.current;
const polylineCurrent = polylineRef.current;
const polygonCurrent = polygonRef.current;
const rectCurrent = rectRef.current;
const contractNodeCurrent = contractNodeRef.current;
if (
popoverContentCurrent &&
polylineCurrent &&
polygonCurrent &&
rectCurrent &&
contractNodeCurrent
) {
const currentDiffElement = document.querySelector(`#diff-${currentDiffIndex}`);
const rect = currentDiffElement?.getBoundingClientRect();
const popoverRect = popoverContentCurrent?.getBoundingClientRect();
const contractNode = contractNodeCurrent?.getBoundingClientRect();
if (rect) {
// 减去Popover的高度和一些间距
const popoverTopPostion =
rect.top -
contractNode.top +
contractNodeCurrent.scrollTop -
popoverContentCurrent.offsetHeight / 2;
// let popoverTopPostion = rect.top - popoverContentCurrent.offsetHeight / 2;
popoverContentCurrent.style.top = popoverTopPostion < 0 ? '2px' : `${popoverTopPostion}px`;
const slideBarWidth = 0
let pathStartX = rect.right - slideBarWidth - 48 - 7;
let pathStartY = rect.top - contractNode.top + contractNodeCurrent.scrollTop + 14 + 14;
let turningPointX =
(document.querySelector('.new-docx')?.getBoundingClientRect().right || slideBarWidth + 48) -
slideBarWidth -
48;
polylineCurrent.setAttribute(
'points',
` ${pathStartX},${pathStartY} ${turningPointX},${pathStartY} ${
popoverRect.left - slideBarWidth - 48 - 7
},${popoverTopPostion + popoverContentCurrent.offsetHeight / 2}`
);
//三角形位置
let polygonH = 5;
let polygonY2 = pathStartY - 2 - polygonH;
let polygonX2 = pathStartX - ((polygonH / 4) * 5) / 2;
let polygonX3 = pathStartX + ((polygonH / 4) * 5) / 2;
polygonCurrent.setAttribute(
'points',
`${pathStartX},${pathStartY - 2} ${polygonX2},${polygonY2} ${polygonX3},${polygonY2}`
);
// 矩形位置
let rectH = 40;
let rectW = 3;
let rectStartX = turningPointX - rectW;
let rectStartY = pathStartY - rectH / 2;
rectCurrent?.setAttribute('x', rectStartX.toString());
rectCurrent?.setAttribute('y', rectStartY.toString());
}
}
};
const switchDiffHandle = (preIndex: number, nextIndex: number) => {
let doc = document.querySelector(`#diff-${nextIndex}`);
// let contractNodeRef = document.querySelector('.new-docx');
doc && doc.nodeName == 'SPAN' ? setDiffTypeIsAdd(true) : setDiffTypeIsAdd(false);
doc && setPopoverContent(doc.innerHTML);
doc && doc.classList.add('diff-focus');
// contractNodeRef?.parentNode?.removeEventListener('scroll', () => computePopPostion(nextIndex));
//重新计算位置
computePopPostion(nextIndex);
// 添加滚动事件
// contractNodeRef?.parentNode?.addEventListener('scroll', () => computePopPostion(nextIndex));
doc && doc.scrollIntoView({ behavior: 'smooth', block: 'center' });
if (preIndex !== 0) {
let preDoc = document.querySelector(`#diff-${preIndex}`);
preDoc && preDoc.classList.remove('diff-focus');
}
};
return (
<ContractCompareLayout>
<Flex bgColor={'#FAFCFF'} h={'100%'} direction={'column'} pt={8} px={12}>
<Flex direction={'row'} w={'100%'} color="#97A9C6" mb={8}>
<Flex>
<Text onClick={() => router.replace('/docExtract')} cursor="pointer">
首页&nbsp;
</Text>
{'>'}&nbsp;详细信息
</Flex>
{/* <Flex justify="right" align="center" pr={2} marginLeft="auto">
<Text>列表模式 &nbsp;</Text>
<MyIcon name="switchSvg"></MyIcon>
</Flex> */}
</Flex>
{viewModal === 'list' ? (
<Flex
direction={'row'}
h={'770px'}
flexGrow={1}
flexShrink={1}
position="relative"
// overflow={'hidden'}
pb={'20px'}
overflowY={'scroll'}
>
<Flex
w={'50%'}
mr={'20px'}
boxShadow={'0 2px 5px rgba(0,0,0,0.4)'}
borderRadius={'8px'}
>
{/* <Flex className={styles.docxWrapper} ref={contractNodeRef}></Flex> */}
{/* <Flex w={'70%'} /> */}
</Flex>
<Flex
overflowY={'scroll'}
w={'50%'}
mr={'20px'}
boxShadow={'0 0 1px rgba(0,0,0,0.4)'}
borderRadius={'8px'}
>
666
</Flex>
</Flex>
) : (
<Flex
height={770}
flexGrow={1}
flexShrink={1}
position="relative"
// overflow={'hidden'}
overflowY={'scroll'}
boxShadow={'0 0 5px rgba(0,0,0,0.4)'}
mb={2}
borderRadius={'8px'}
ref={contractNodeRef}
>
<Flex w="100%" height={'max-content'} position={'relative'}>
{contract && contract.fileUrl != '' && (
<DiffDocument
src={contract.fileUrl}
customClassName={'new-docx'}
diffCallBack={() => {
setNewDocRenderSuccess(true);
}}
style={{
width: '70%',
display: diffRenderSuccess ? 'block' : 'none',
boxShadow: '1px 1px 6px rgba(0, 0, 0, 0.2)',
height: 'max-content'
}}
/>
)}
{!diffRenderSuccess ? (
<SkeletonText
h={'100%'}
w={'100%'}
noOfLines={6}
spacing="8"
mx={8}
mt={8}
skeletonHeight="6"
/>
) : (
<Flex w={'30%'} justify={'center'} position={'relative'}>
<Flex
sx={{ display: diffRenderSuccess ? 'flex' : 'none' }}
h={160}
w={'80%'}
bg="#2172F3"
borderRadius={'8px'}
ref={popoverContentRef}
position={'absolute'}
zIndex="10"
color="#fff"
p={'20px'}
>
<Flex h={'50%'} align={'center'} justify={'center'} mr={'20px'}>
<Flex
w={'40px'}
h={'40px'}
align={'center'}
justify={'center'}
bgColor={'#FFF'}
borderRadius="999"
>
{diffTypeIsAdd ? (
<MyIcon name="addSvg"></MyIcon>
) : (
<MyIcon name="deleteSvg"></MyIcon>
)}
</Flex>
</Flex>
<Flex h={'100%'} direction={'column'}>
<Box h={'80%'}>
<p>
{currentDiffIndex} / {diffCount}
</p>
<p className={styles.popoverContent}>
{diffTypeIsAdd ? popoverContent : <s>{popoverContent}</s>}
</p>
</Box>
<Box>
<Button
bgColor="rgba(255,255,255,.2)"
h={'44px'}
w={'44px'}
color="#fff"
_hover={{
bgColor: '#FFFFFF',
color: '#2172F3'
}}
borderRadius="4"
border={'1px solid #fff'}
onClick={() =>
setCurrentDiffIndex((preState) => {
const nextIndex = preState <= 1 ? diffCount : preState - 1;
switchDiffHandle(preState, nextIndex);
return nextIndex;
})
}
mr={'10px'}
>
<ChevronUpIcon boxSize="1.5rem" />
</Button>
<Button
bgColor="rgba(255,255,255,.2)"
h={'44px'}
w={'44px'}
color="#fff"
border={'1px solid #fff'}
_hover={{
bgColor: '#FFFFFF',
color: '#2172F3'
}}
borderRadius="4"
onClick={() =>
setCurrentDiffIndex((preState) => {
const nextIndex =
preState >= diffCount ? (diffCount === 0 ? 0 : 1) : preState + 1;
switchDiffHandle(preState, nextIndex);
return nextIndex;
})
}
mr={'10px'}
>
<ChevronDownIcon boxSize="1.5rem" />
</Button>
<Button
bgColor="rgba(255,255,255,.2)"
h={'44px'}
w={'44px'}
color="#fff"
border={'1px solid #fff'}
_hover={{
bgColor: '#FFFFFF',
color: '#2172F3'
}}
borderRadius="4"
onClick={onOpenFeedback}
>
反馈
</Button>
</Box>
</Flex>
</Flex>
</Flex>
)}
<svg
style={{ position: 'absolute' }}
width={'100%'}
height={'100%'}
pointerEvents={'none'}
>
<polygon
ref={polygonRef}
points=""
fill="#2172F3"
stroke="#2172F3"
strokeWidth="2"
/>
<polyline
ref={polylineRef}
points="0,0 0,0"
strokeWidth={1}
fill="none"
stroke={'#2172F3'}
strokeDasharray={'5px'}
/>
<rect
ref={rectRef}
x="0"
y="0"
width="3"
height="40"
fill="#2172F3"
stroke="#2172F3"
strokeWidth="0"
/>
</svg>
</Flex>
</Flex>
)}
</Flex>
{/* {isOpenFeedback && contract && (
<ContractDiffFeedback
onClose={onCloseFeedback}
diffContent={popoverContent}
diffNumber={currentDiffIndex}
contractName={contract.contractName}
/>
)} */}
</ContractCompareLayout>
);
};
export async function getServerSideProps(context: any) {
return {
props: {
// ...(await serviceSideProps(context))
}
};
}
export default DetailPage;
import React, { ChangeEvent, useState } from 'react';
import { Box, Button, ModalBody, ModalFooter, Textarea } from '@chakra-ui/react';
// import { useSystemStore } from '@/web/common/system/useSystemStore';
// import { useUserStore } from '@/web/support/user/useUserStore';
// import { useSystem } from '@eagic/web/hooks/useSystem';
import { useToast } from '@eagic/hooks/useToast';
import MyModal from '@eagic/web/components/common/MyModal';
import styles from './index.module.scss';
import { POST } from '@/web/common/api/request';
type paramsType = {
contractName: string;
diffNumber: number;
diffContent: string;
onClose: () => void;
};
const SystemFeedBack = (params: paramsType) => {
const { diffNumber, onClose, diffContent, contractName } = params;
// const { isPc } = useSystem();
const [feedback, setFeedback] = useState('');
// const { userInfo } = useUserStore();
const { toast } = useToast();
const handleInputChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
setFeedback(event.target.value);
};
// console.log(userInfo);
const handleSubmit = async () => {
if (!feedback.trim()) {
toast({
title: '反馈内容不能为空',
status: 'error',
duration: 3000
});
return;
}
const data = {
contractName,
diffNumber,
diffContent,
feedback
};
POST<any>(`/core/chat/twTools/contractCompare/feedbackController`, data)
.then((resp) => {
toast({
title: '我们已收到您的反馈,感谢您的支持!',
status: 'success',
duration: 2000
});
// 清空输入框
setFeedback('');
onClose();
})
.catch((e) => {
toast({
title: '提交失败' + e.message,
status: 'error',
duration: 4000
});
});
};
return (
<MyModal isOpen onClose={onClose} title={'问题反馈'} w={'480px'}>
<ModalBody justifyContent={'center'} display={'flex'} flexDirection={'column'}>
<Box>
<b>合同名称:</b>
{contractName}
</Box>
<Box>
<b>差异序号:</b>
{diffNumber}
</Box>
<Box mb={'10px'} className={styles.diffContentBox}>
<b>差异内容:</b>
{diffContent}
</Box>
<Textarea
value={feedback}
onChange={handleInputChange}
placeholder="请输入您的反馈..."
size="lg"
resize="none"
width={'100%'}
height={'240px'}
borderColor={'gray.300'}
/>
</ModalBody>
<ModalFooter justifyContent={'center'} display={'flex'}>
<Button
mt={4}
width={'40%'}
colorScheme="blue"
onClick={handleSubmit}
isDisabled={!feedback.trim()}
>
发送反馈
</Button>
</ModalFooter>
</MyModal>
);
};
export default SystemFeedBack;
import React, { useRef, useEffect } from 'react';
import { Box } from '@chakra-ui/react';
import jsPreviewDocx from '@js-preview/docx';
import '@js-preview/docx/lib/index.css';
const DiffDocument = ({
src,
customClassName,
diffCallBack,
style
}: {
src: string | Blob;
customClassName?: string;
diffCallBack: Function;
style?: Object | undefined;
}) => {
const DocBoxRef = useRef<HTMLDivElement>(null);
useEffect(() => {
//初始化时指明要挂载的父元素Dom节点
if (DocBoxRef.current != null && typeof window !== 'undefined') {
const myDocxPreviewer = jsPreviewDocx.init(DocBoxRef.current);
//传递要预览的文件地址即可
myDocxPreviewer
.preview(src)
.then((res) => {
diffCallBack();
})
.catch((e) => {
console.log('预览失败', e);
});
}
}, []);
return <Box className={customClassName} ref={DocBoxRef} sx={style}></Box>;
};
export default React.memo(DiffDocument);
.diffContentBox {
font-size: 16px;
// text-overflow: ellipsis;
// white-space: nowrap;
// -webkit-line-clamp: 2;
// width: 66px;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
/* 限制为两行 */
overflow: hidden;
text-overflow: ellipsis;
white-space: normal;
max-height: 3em;
margin-bottom: 10px;
}
import { MongoImageTypeEnum } from './image/constants';
import { OutLinkChatAuthProps } from '../../support/permission/chat.d';
export type preUploadImgProps = OutLinkChatAuthProps & {
type: `${MongoImageTypeEnum}`;
expiredTime?: Date;
metadata?: Record<string, any>;
};
export type UploadImgProps = preUploadImgProps & {
base64Img: string;
};
export type UrlFetchParams = {
urlList: string[];
selector?: string;
};
export type UrlFetchResponse = {
url: string;
title: string;
content: string;
selector?: string;
}[];
export const fileImgs = [
{ suffix: 'pdf', src: 'file/fill/pdf' },
{ suffix: 'ppt', src: 'file/fill/ppt' },
{ suffix: 'xlsx', src: 'file/fill/xlsx' },
{ suffix: 'csv', src: 'file/fill/csv' },
{ suffix: '(doc|docs)', src: 'file/fill/doc' },
{ suffix: 'txt', src: 'file/fill/txt' },
{ suffix: 'md', src: 'file/fill/markdown' },
{ suffix: 'html', src: 'file/fill/html' }
// { suffix: '.', src: '/imgs/files/file.svg' }
];
export function getFileIcon(name = '', defaultImg = 'file/fill/file') {
return fileImgs.find((item) => new RegExp(item.suffix, 'gi').test(name))?.src || defaultImg;
}
export const imageBaseUrl = '/api/system/img/';
export enum MongoImageTypeEnum {
systemAvatar = 'systemAvatar',
appAvatar = 'appAvatar',
pluginAvatar = 'pluginAvatar',
datasetAvatar = 'datasetAvatar',
userAvatar = 'userAvatar',
teamAvatar = 'teamAvatar',
chatImage = 'chatImage',
collectionImage = 'collectionImage'
}
export const mongoImageTypeMap = {
[MongoImageTypeEnum.systemAvatar]: {
label: 'appAvatar',
unique: true
},
[MongoImageTypeEnum.appAvatar]: {
label: 'appAvatar',
unique: true
},
[MongoImageTypeEnum.pluginAvatar]: {
label: 'pluginAvatar',
unique: true
},
[MongoImageTypeEnum.datasetAvatar]: {
label: 'datasetAvatar',
unique: true
},
[MongoImageTypeEnum.userAvatar]: {
label: 'userAvatar',
unique: true
},
[MongoImageTypeEnum.teamAvatar]: {
label: 'teamAvatar',
unique: true
},
[MongoImageTypeEnum.chatImage]: {
label: 'chatImage',
unique: false
},
[MongoImageTypeEnum.collectionImage]: {
label: 'collectionImage',
unique: false
}
};
export const uniqueImageTypeList = Object.entries(mongoImageTypeMap)
.filter(([key, value]) => value.unique)
.map(([key]) => key as `${MongoImageTypeEnum}`);
export const FolderIcon = 'file/fill/folder';
export const FolderImgUrl = '/imgs/files/folder.svg';
export const HttpPluginImgUrl = '/imgs/app/httpPluginFill.svg';
export const HttpImgUrl = '/imgs/workflow/http.png';
import { MongoImageTypeEnum } from './constants';
export type MongoImageSchemaType = {
_id: string;
teamId: string;
binary: Buffer;
createTime: Date;
expiredTime?: Date;
type: `${MongoImageTypeEnum}`;
metadata?: {
mime?: string; // image mime type.
relatedId?: string; // This id is associated with a set of images
};
};
import { BucketNameEnum } from './constants';
export type FileTokenQuery = {
bucketName: `${BucketNameEnum}`;
teamId: string;
tmbId: string;
fileId: string;
};
.statusAnimation {
animation: statusBox 0.8s linear infinite alternate;
}
.fileSelectBox {
width: 50%;
height: calc(340 / 1500 * 100vw);
// height: 340px;
border-radius: 8px;
background-image: url('/imgs/contractCompare/selectFileBg.png');
background-size: cover;
}
.historyContractBox {
width: 50%;
height: calc(340 / 1500 * 100vw);
background-image: url('/imgs/contractCompare/historyBg.png');
background-size: cover;
border-radius: 8px;
color: #2172f3;
}
.fileSelectBoxTitle {
font-family: Alibaba PuHuiTi;
font-size: 36px;
font-weight: 700;
line-height: 49.39px;
text-align: left;
text-underline-position: from-font;
text-decoration-skip-ink: none;
}
.fileSelectBoxTip {
font-family: Alibaba PuHuiTi;
font-size: 16px;
font-weight: 400;
line-height: 21.95px;
text-align: left;
text-underline-position: from-font;
text-decoration-skip-ink: none;
}
.fileSelectBoxBtn {
display: flex;
justify-content: center;
align-items: center;
width: 166px;
height: 50px;
padding: 11px 23px 11px 23px;
gap: 10px;
border-radius: 4px;
background-color: #fff;
color: #2172f3;
font-family: Alibaba PuHuiTi;
font-size: 20px;
font-weight: 600;
line-height: 27.44px;
text-align: left;
// text-underline-position: from-font;
// text-decoration-skip-ink: none;
cursor: pointer;
}
.docxWrapper {
// background: gray;
// padding: 30px;
// padding-bottom: 100px !important;
display: flex;
flex-flow: column;
align-items: center;
flex-grow: 1;
// box-shadow: 1px 1px 6px rgba(0, 0, 0, 0.2);
// margin: 5px;
// box-shadow: 0 0 5px rgba(0, 0, 0, 0.4);
// overflow-y: scroll;
// padding: 0 50px 50px 50px !important;
border-radius: 8px;
}
.popoverContent {
font-size: 16px;
// text-overflow: ellipsis;
// white-space: nowrap;
// -webkit-line-clamp: 2;
// width: 66px;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
/* 限制为两行 */
overflow: hidden;
text-overflow: ellipsis;
white-space: normal;
max-height: 3em;
margin-bottom: 10px;
}
@keyframes statusBox {
0% {
opacity: 1;
}
100% {
opacity: 0.11;
}
}
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import Head from 'next/head';
import {
Box,
Button,
Flex,
useTheme,
Text,
calc,
Modal,
useDisclosure,
ModalOverlay,
ModalContent,
ModalBody,
ModalFooter,
Progress,
Spinner
} from '@chakra-ui/react';
import styles from './index.module.scss';
import ContractCompareLayout from './layout';
import { useSelectFile } from '@/web/common/hooks/useSelectFile';
import { POST, GET } from '@/web/common/api/request';
import { useRouter } from 'next/router';
import { serviceSideProps } from '@src/utils/i18n';
type FileItemType = {
id: string;
rawFile: File;
type: FileTypeEnum;
name: string;
icon: string; // img is base64
src?: string;
};
enum FileTypeEnum {
image = 'image',
file = 'file'
}
const HomePage = ({ params }: { params: any }) => {
const router = useRouter();
const { fileUrl } = params || router.query;
const fileType: string = '.docx, doc';
const { File, onOpen: onOpenSelectFile } = useSelectFile({
fileType,
multiple: true,
maxCount: 1
});
const { isOpen, onOpen, onClose } = useDisclosure();
const [modalContent, setModalContent] = useState('');
const [progressValue, setProgressValue] = useState<number>(0);
const onSelectFile = useCallback(
async (files: File[]) => {
onOpen();
const file = files.pop();
if (!file) {
return;
}
const data = new FormData();
data.append('file', file, encodeURIComponent(file.name));
data.append('fileId', 'userUp-' + generateRandomString(6));
// data.append('userId', userInfo?._id ?? '');
const response = await POST<string>('/contractCompare/mainHandler', data, {
headers: { 'Content-Type': `multipart/form-data; charset=utf-8` }
});
onClose();
router.replace(`/docExtract/${response}`);
},
[fileType]
);
const generateRandomString = (length: number = 6): string => {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * characters.length);
result += characters[randomIndex];
}
return result;
};
return (
<ContractCompareLayout>
<Box bgColor={'#FAFCFF'} h={'100%'} w={'100%'}>
<Flex direction={'column'} justify={'center'} align={'center'} w={'100%'}>
<Flex
justifyContent={'center'}
fontSize={'2.5rem'}
fontWeight={700}
mt={100}
color={'#29313D'}
fontFamily={'Alibaba PuHuiTi'}
w={'100%'}
>
合同校验,智能助手帮助您~
</Flex>
<Flex justifyContent={'center'} mt={'9px'} w={'100%'}>
<Text fontSize={'1.2rem'} lineHeight={'1.6rem'} color={'#97A9C6'}>
{' '}
整体解决方案,帮助企业实现降本提质增效、产业链供应链协同,助力区域实现产业数字化
</Text>
</Flex>
<Flex px={12} mt={75} w={'80%'}>
<Box className={styles.fileSelectBox} mr={6} bgColor={'#2172F3'} color={'#fff'}>
<Flex direction={'column'} h={'100%'} justify={'center'} pl={'calc(50/1500*100vw)'}>
<Text className={styles.fileSelectBoxTitle}>选择合同文件</Text>
<Text className={styles.fileSelectBoxTip} mt={'20px'}>
仅支持word格式文件
</Text>
<Box
className={styles.fileSelectBoxBtn}
mt={'40px'}
onClick={() => {
onOpenSelectFile();
}}
>
{`立即上传 >`}
<File onSelect={onSelectFile} />
</Box>
</Flex>
</Box>
<Box className={styles.historyContractBox}>
<Flex direction={'column'} h={'100%'} justify={'center'} pl={'calc(50/1500*100vw)'}>
<Text className={styles.fileSelectBoxTitle}>历史合同</Text>
<Text className={styles.fileSelectBoxTip} mt={'20px'}>
储存重要文件
</Text>
<Box
className={styles.fileSelectBoxBtn}
mt={'40px'}
onClick={() => {
// router.replace('/docExtract/historyContract');
}}
>
{`点击查看 >`}
</Box>
</Flex>
</Box>
</Flex>
</Flex>
</Box>
<Modal isOpen={isOpen} onClose={onClose} closeOnOverlayClick={false}>
<ModalOverlay />
<ModalContent w={452} h={70}>
{/* <ModalHeader>Modal Title</ModalHeader> */}
{/* <ModalCloseButton /> */}
<ModalBody h={'65px'}>
{/* <Lorem count={2} /> */}
<Flex height={'100%'} align={'center'} justify={'center'}>
{modalContent}&nbsp;&nbsp;
<Spinner size={'sm'} speed="0.65s" />
</Flex>
</ModalBody>
<ModalFooter p={0} borderRadius={'10px'} h={'5px'}>
<Progress h={'5px'} w={'100%'} value={progressValue} borderRadius={'10px'} />
</ModalFooter>
</ModalContent>
</Modal>
</ContractCompareLayout>
);
};
export async function getServerSideProps(context: any) {
return {
props: {
// ...(await serviceSideProps(context))
}
};
}
export default HomePage;
import { Box, Flex } from '@chakra-ui/react';
import React, { ReactNode } from 'react';
interface Props {
// layout 固定需要一个 NeactNode 类型的 children 为参数
children: ReactNode;
}
const ContractCompareLayout = ({ children }: Props) => {
return (
<Flex h={'100%'} direction={'column'}>
<Flex flexGrow="1" flexShrink="1" overflow={'hidden'}>
<Box w={'100%'} h={'100%'}>
{children}
</Box>
</Flex>
</Flex>
);
};
export default ContractCompareLayout;
import React, { useEffect } from 'react';
import Loading from '@eagic/web/components/common/MyLoading';
import { useRouter } from 'next/router';
const Index = () => {
const router = useRouter();
useEffect(() => {
router.push('/docExtract');
}, [router]);
return <Loading></Loading>;
};
export default Index;
declare module 'jsdiff-esm';
declare module 'jsdom';
declare module 'mammoth';
/* mongo fs bucket */
export enum BucketNameEnum {
dataset = 'dataset',
chat = 'chat'
}
export const bucketNameMap = {
[BucketNameEnum.dataset]: {
label: 'file:bucket_file',
previewExpireMinutes: 30 // 30 minutes
},
[BucketNameEnum.chat]: {
label: 'file:bucket_chat',
previewExpireMinutes: 7 * 24 * 60 // 7 days
}
};
export const ReadFileBaseUrl = '/api/common/file/read';
export const documentFileType = '.txt, .docx, .csv, .xlsx, .pdf, .md, .html, .pptx';
import { I18nNsType } from '@eagic/web/types/i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
export enum LangEnum {
'zh' = 'zh',
'en' = 'en'
}
export const langMap = {
[LangEnum.en]: {
label: 'English',
icon: 'common/language/en'
},
[LangEnum.zh]: {
label: '简体中文',
icon: 'common/language/zh'
}
};
export const serviceSideProps = (content: any, ns: I18nNsType = []) => {
return serverSideTranslations(content.locale, ['common', 'error', ...ns], null, content.locales);
};
import type { NextApiRequest, NextApiResponse } from 'next';
import multer from 'multer';
import path from 'path';
import { BucketNameEnum, bucketNameMap } from '@eagic/global/common/file/constants';
import { getNanoid } from '@eagic/global/common/string/tools';
type FileType = {
fieldname: string;
originalname: string;
encoding: string;
mimetype: string;
filename: string;
path: string;
size: number;
};
/*
maxSize: File max size (MB)
*/
export const getUploadModel = ({ maxSize = 500 }: { maxSize?: number }) => {
maxSize *= 1024 * 1024;
class UploadModel {
uploader = multer({
limits: {
fieldSize: maxSize
},
preservePath: true,
storage: multer.diskStorage({
// destination: (_req, _file, cb) => {
// cb(null, tmpFileDirPath);
// },
filename: async (req, file, cb) => {
const { ext } = path.parse(decodeURIComponent(file.originalname));
cb(null, `${getNanoid()}${ext}`);
}
})
}).single('file');
async doUpload<T = Record<string, any>>(
req: NextApiRequest,
res: NextApiResponse,
originBucketName?: `${BucketNameEnum}`
) {
return new Promise<{
file: FileType;
metadata: Record<string, any>;
data: T;
bucketName?: `${BucketNameEnum}`;
pm?: string;
type?: 'compare' | 'extract';
fileId?: string;
userId?: string;
}>((resolve, reject) => {
// @ts-ignore
this.uploader(req, res, (error) => {
if (error) {
return reject(error);
}
// check bucket name
const bucketName = (req.body?.bucketName || originBucketName) as `${BucketNameEnum}`;
if (bucketName && !bucketNameMap[bucketName]) {
return reject('BucketName is invalid');
}
// @ts-ignore
const file = req.file as FileType;
resolve({
...req.body,
file: {
...file,
originalname: decodeURIComponent(file.originalname)
},
bucketName,
metadata: (() => {
if (!req.body?.metadata) return {};
try {
return JSON.parse(req.body.metadata);
} catch (error) {
return {};
}
})(),
data: (() => {
if (!req.body?.data) return {};
try {
return JSON.parse(req.body.data);
} catch (error) {
return {};
}
})()
});
});
});
}
}
return new UploadModel();
};
import crypto from 'crypto';
import { customAlphabet } from 'nanoid';
/* check string is a mongo objectId */
export function strIsMongoId(id?: string): boolean {
if (!id) return false
const mongoIdPattern = /^[0-9a-fA-F]{24}$/;
return mongoIdPattern.test(id);
}
/* check string is a web link */
export function strIsLink(str?: string) {
if (!str) return false;
if (/^((http|https)?:\/\/|www\.|\/)[^\s/$.?#].[^\s]*$/i.test(str)) return true;
return false;
}
/* hash string */
export const hashStr = (str: string) => {
return crypto.createHash('sha256').update(str).digest('hex');
};
/* simple text, remove chinese space and extra \n */
export const simpleText = (text = '') => {
text = text.trim();
text = text.replace(/([\u4e00-\u9fa5])[\s&&[^\n]]+([\u4e00-\u9fa5])/g, '$1$2');
text = text.replace(/\r\n|\r/g, '\n');
text = text.replace(/\n{3,}/g, '\n\n');
text = text.replace(/[\s&&[^\n]]{2,}/g, ' ');
text = text.replace(/[\x00-\x08]/g, ' ');
return text;
};
/*
replace {{variable}} to value
*/
export function replaceVariable(text: any, obj: Record<string, string | number>) {
if (!(typeof text === 'string')) return text;
for (const key in obj) {
const val = obj[key];
if (!['string', 'number'].includes(typeof val)) continue;
text = text.replace(new RegExp(`{{(${key})}}`, 'g'), String(val));
}
return text || '';
}
/* replace sensitive text */
export const replaceSensitiveText = (text: string) => {
// 1. http link
text = text.replace(/(?<=https?:\/\/)[^\s]+/g, 'xxx');
// 2. nx-xxx 全部替换成xxx
text = text.replace(/ns-[\w-]+/g, 'xxx');
return text;
};
/* Make sure the first letter is definitely lowercase */
export const getNanoid = (size = 12) => {
const firstChar = customAlphabet('abcdefghijklmnopqrstuvwxyz', 1)();
if (size === 1) return firstChar;
const randomsStr = customAlphabet(
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890',
size - 1
)();
return `${firstChar}${randomsStr}`;
};
/* Custom text to reg, need to replace special chats */
export const replaceRegChars = (text: string) => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
export const getRegQueryStr = (text: string, flags = 'i') => {
const formatText = replaceRegChars(text);
const chars = formatText.split('');
const regexPattern = chars.join('.*');
return new RegExp(regexPattern, flags);
};
/* slice json str */
export const sliceJsonStr = (str: string) => {
str = str.replace(/(\\n|\\)/g, '').replace(/ /g, '');
const jsonRegex = /{(?:[^{}]|{(?:[^{}]|{[^{}]*})*})*}/g;
const matches = str.match(jsonRegex);
if (!matches) {
return '';
}
// 找到第一个完整的 JSON 字符串
const jsonStr = matches[0];
return jsonStr;
};
export const sliceStrStartEnd = (str: string, start: number, end: number) => {
const overSize = str.length > start + end;
if (!overSize) return str;
const startContent = str.slice(0, start);
const endContent = overSize ? str.slice(-end) : '';
return `${startContent}${overSize ? `\n\n...[hide ${str.length - start - end} chars]...\n\n` : ''}${endContent}`;
};
export const getDevice = (device?: string) => {
if (!device) return 'fbc';
const lowDevice = device.toLowerCase();
if (lowDevice.includes('woa')) {
if (lowDevice.includes('android') || lowDevice.includes('ios')) {
return 'app';
} else {
return 'itw';
}
} else {
return 'fbc';
}
};
\ No newline at end of file
export const delay = (ms: number) =>
new Promise((resolve) => {
setTimeout(() => {
resolve('');
}, ms);
});
{
"compilerOptions": {
"target": "es2022",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
......@@ -7,7 +8,7 @@
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
......@@ -18,7 +19,9 @@
}
],
"paths": {
"src/*": ["./src/*"]
"@src/*": ["./src/*"],
"@/*": ["./src/app/*"],
"@eagic/*": ["./src/app/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment