V4.9.7 feature (#4669)

* update doc

* feat: Add coupon redemption feature for team subscriptions (#4595)

* feat: Add coupon redemption feature for team subscriptions

- Introduced `TeamCouponSub` and `TeamCouponSchema` types
- Added `redeemCoupon` API endpoint
- Updated UI to include a modal for coupon redemption
- Added new icon and translations for "Redeem coupon"

* perf: remove field teamId

* perf: use dynamic import

* refactor: move to page component

* perf: coupon code

* perf: mcp server

* perf: test

* auto layout (#4634)

* fix 4.9.6 (#4631)

* fix debug quote list

* delete next text node match

* fix extract default boolean value

* export latest 100 chat items

* fix quote item ui

* doc

* fix doc

* feat: auto layout

* perf: auto layout

* fix: auto layout null

* add start node

---------

Co-authored-by: heheer <heheer@sealos.io>

* fix: share link (#4644)

* Add workflow run duration;Get audio duration (#4645)

* add duration

* get audio duration

* Custom config path (#4649)

* feat: 通过环境变量DATA_PATH获取配置文件目录 (#4622)

通过环境变量DATA_PATH获取配置文件目录,以应对不同的部署方式的多样化需求

* feat: custom configjson path

* doc

---------

Co-authored-by: John Chen <sss1991@163.com>

* 程序api调用场景下,如果大量调用带有图片或视频,产生的聊天记录会导致后台mongo数据库异常。这个修改给api客户端一个禁止生成聊天记录的选项,避免这个后果。 (#3964)

* update special chatId

* perf: vector db rename

* update operationLog (#4647)

* update operationLog

* combine operationLogMap

* solve operationI18nLogMap bug

* remoce log

* feat: Rerank usage (#4654)

* refresh concat when update (#4655)

* fix: refresh code

* perf: timer lock

* Fix operationLog (#4657)

* perf: http streamable mcp

* add alipay (#4630)

* perf: subplan ui

* perf: pay code

* hiden bank tip

* Fix: pay error (#4665)

* fix quote number (#4666)

* remove log

---------

Co-authored-by: a.e. <49438478+I-Info@users.noreply.github.com>
Co-authored-by: heheer <heheer@sealos.io>
Co-authored-by: John Chen <sss1991@163.com>
Co-authored-by: gaord <bengao168@msn.com>
Co-authored-by: gggaaallleee <91131304+gggaaallleee@users.noreply.github.com>
This commit is contained in:
Archer
2025-04-26 16:17:21 +08:00
committed by GitHub
parent a669a60fe6
commit 0720bbe4da
143 changed files with 2067 additions and 1093 deletions

View File

@@ -9,11 +9,21 @@ weight: 793
## 🚀 新增内容
1. MCP 工具支持 HTTP Streamable 协议。
2. MCP server 支持编辑工具名,适配部分客户端不支持中文名问题。
3. 工作流右键可自动对齐节点。
4. 支持生产环境自定义`config.json`路径。
5. API 调用,支持传递一个特殊 chatId(`NO_RECORD_HISTORIES`),使得系统不会进行历史记录存储。
6. 支持 Rerank 模型按量计费。
7. 套餐兑换码功能
8. 支付宝支付
## ⚙️ 优化
1. Doc2x 文档解析,增加报错信息捕获,增加超时时长。
2. 调整 PG vector 查询语句,强制使用向量索引。
3. 对话时间统计,准确返回工作流整体运行时间。
4. 从 ai_proxy 获取音频解析时长。
## 🐛 修复
@@ -21,8 +31,10 @@ weight: 793
2. 使用记录仪表盘,无法获取指定成员的使用统计。
3. 仪表盘接口,因未考虑时区问题,统计异常。
4. LLM 模型测试接口,无法测试未启用的 LLM。同时修复模型测试接口会把模型自定义请求地址去除问题。
5. 导出对话记录,限制单条对话记录消息上限 1000 组,避免导出失败
6. 工作流变量下一段文本仍是工作流变量,不触发渲染
7. 调试知识库检索模块,提示无权操作知识库
8. 文本内容提取节点,默认值赋值逻辑
5. Copy app 权限问题
6. 导出对话记录,限制单条对话记录消息上限 1000 组,避免导出失败
7. 工作流变量下一段文本仍是工作流变量,不触发渲染
8. 调试知识库检索模块,提示无权操作知识库
9. 文本内容提取节点,默认值赋值逻辑。
10. 分享链接中,会强制返回嵌套应用中的引用内容。

22
env.d.ts vendored
View File

@@ -23,16 +23,18 @@ declare global {
FE_DOMAIN: string;
FILE_DOMAIN: string;
NEXT_PUBLIC_BASE_URL: string;
LOG_LEVEL: string;
STORE_LOG_LEVEL: string;
USE_IP_LIMIT: string;
WORKFLOW_MAX_RUN_TIMES: string;
WORKFLOW_MAX_LOOP_TIMES: string;
CHECK_INTERNAL_IP: string;
CHAT_LOG_URL: string;
CHAT_LOG_INTERVAL: string;
CHAT_LOG_SOURCE_ID_PREFIX: string;
ALLOWED_ORIGINS: string;
LOG_LEVEL?: string;
STORE_LOG_LEVEL?: string;
USE_IP_LIMIT?: string;
WORKFLOW_MAX_RUN_TIMES?: string;
WORKFLOW_MAX_LOOP_TIMES?: string;
CHECK_INTERNAL_IP?: string;
CHAT_LOG_URL?: string;
CHAT_LOG_INTERVAL?: string;
CHAT_LOG_SOURCE_ID_PREFIX?: string;
ALLOWED_ORIGINS?: string;
SHOW_COUPON?: string;
CONFIG_JSON_PATH?: string;
}
}
}

View File

@@ -60,6 +60,7 @@ export type FastGPTFeConfigsType = {
show_team_chat?: boolean;
show_compliance_copywriting?: boolean;
show_aiproxy?: boolean;
show_coupon?: boolean;
concatMd?: string;
concatMd?: string;
@@ -106,6 +107,12 @@ export type FastGPTFeConfigsType = {
lafEnv?: string;
navbarItems?: NavbarItemType[];
externalProviderWorkflowVariables?: ExternalProviderWorkflowVarType[];
payConfig?: {
wx?: boolean;
alipay?: boolean;
bank?: boolean;
};
};
export type SystemEnvType = {

View File

@@ -26,7 +26,7 @@ export const getDefaultAppForm = (): AppSimpleEditFormType => {
similarity: 0.4,
limit: 3000,
searchMode: DatasetSearchModeEnum.embedding,
usingReRank: false,
usingReRank: true,
rerankModel: '',
rerankWeight: 0.5,
datasetSearchUsingExtensionQuery: true,

View File

@@ -110,6 +110,7 @@ export type ChatItemSchema = (UserChatItemType | SystemChatItemType | AIChatItem
tmbId: string;
appId: string;
time: Date;
durationSeconds?: number;
};
export type AdminFbkType = {
@@ -122,7 +123,6 @@ export type AdminFbkType = {
/* --------- chat item ---------- */
export type ResponseTagItemType = {
totalRunningTime?: number;
totalQuoteList?: SearchDataResponseItemType[];
llmModuleAccount?: number;
historyPreviewLength?: number;
@@ -141,6 +141,7 @@ export type ChatSiteItemType = (UserChatItemType | SystemChatItemType | AIChatIt
ttsBuffer?: Uint8Array;
responseData?: ChatHistoryItemResType[];
time?: Date;
durationSeconds?: number;
} & ChatBoxInputType &
ResponseTagItemType;

View File

@@ -77,13 +77,6 @@ export const getHistoryPreview = (
});
};
export const filterModuleTypeList: any[] = [
FlowNodeTypeEnum.pluginModule,
FlowNodeTypeEnum.datasetSearchNode,
FlowNodeTypeEnum.tools,
FlowNodeTypeEnum.pluginOutput
];
export const filterPublicNodeResponseData = ({
flowResponses = [],
responseDetail = false
@@ -91,12 +84,19 @@ export const filterPublicNodeResponseData = ({
flowResponses?: ChatHistoryItemResType[];
responseDetail?: boolean;
}) => {
const publicNodeMap: Record<string, any> = {
[FlowNodeTypeEnum.pluginModule]: true,
[FlowNodeTypeEnum.datasetSearchNode]: true,
[FlowNodeTypeEnum.tools]: true,
[FlowNodeTypeEnum.pluginOutput]: true
};
const filedList = responseDetail
? ['quoteList', 'moduleType', 'pluginOutput', 'runningTime']
: ['moduleType', 'pluginOutput', 'runningTime'];
return flowResponses
.filter((item) => filterModuleTypeList.includes(item.moduleType))
.filter((item) => publicNodeMap[item.moduleType])
.map((item) => {
const obj: DispatchNodeResponseType = {};
for (let key in item) {

View File

@@ -2,6 +2,7 @@ import { FlowNodeInputTypeEnum } from '../node/constant';
export enum SseResponseEventEnum {
error = 'error',
workflowDuration = 'workflowDuration', // workflow duration
answer = 'answer', // animation stream
fastAnswer = 'fastAnswer', // direct answer text, not animation
flowNodeStatus = 'flowNodeStatus', // update node status

View File

@@ -24,6 +24,7 @@ import { AiChatQuoteRoleType } from '../template/system/aiChat/type';
import { LafAccountType, OpenaiAccountType } from '../../../support/user/team/type';
import { CompletionFinishReason } from '../../ai/type';
import { WorkflowInteractiveResponseType } from '../template/system/interactive/type';
import { SearchDataResponseItemType } from '../../dataset/type';
export type ExternalProviderType = {
openaiAccount?: OpenaiAccountType;
externalWorkflowVariables?: Record<string, string>;
@@ -62,6 +63,8 @@ export type ChatDispatchProps = {
workflowStreamResponse?: WorkflowResponseType;
workflowDispatchDeep?: number;
version?: 'v1' | 'v2';
responseAllData?: boolean;
responseDetail?: boolean;
};
@@ -136,12 +139,15 @@ export type DispatchNodeResponseType = {
finishReason?: CompletionFinishReason;
// dataset search
embeddingModel?: string;
embeddingTokens?: number;
similarity?: number;
limit?: number;
searchMode?: `${DatasetSearchModeEnum}`;
embeddingWeight?: number;
rerankModel?: string;
rerankWeight?: number;
reRankInputTokens?: number;
searchUsingReRank?: boolean;
queryExtensionResult?: {
model: string;

View File

@@ -9,7 +9,7 @@ export type McpKeyType = {
export type McpAppType = {
appId: string;
appName?: string;
toolName: string;
toolAlias?: string;
description: string;
};

View File

@@ -4,6 +4,7 @@ export enum OperationLogEventEnum {
JOIN_TEAM = 'JOIN_TEAM',
CHANGE_MEMBER_NAME = 'CHANGE_MEMBER_NAME',
KICK_OUT_TEAM = 'KICK_OUT_TEAM',
RECOVER_TEAM_MEMBER = 'RECOVER_TEAM_MEMBER',
CREATE_DEPARTMENT = 'CREATE_DEPARTMENT',
CHANGE_DEPARTMENT = 'CHANGE_DEPARTMENT',
DELETE_DEPARTMENT = 'DELETE_DEPARTMENT',

View File

@@ -1,5 +1,11 @@
import { StandardSubLevelEnum, SubModeEnum } from '../sub/constants';
import { BillTypeEnum } from './constants';
import { BillTypeEnum, DrawBillQRItem } from './constants';
export type CreateOrderResponse = {
qrCode?: string;
iframeCode?: string;
markdown?: string;
};
export type CreateStandPlanBill = {
type: BillTypeEnum.standSubPlan;
@@ -22,6 +28,16 @@ export type CreateBillProps =
export type CreateBillResponse = {
billId: string;
codeUrl: string;
readPrice: number;
payment: BillPayWayEnum;
} & CreateOrderResponse;
export type UpdatePaymentProps = {
billId: string;
payWay: BillPayWayEnum;
};
export type CheckPayResultResponse = {
status: BillStatusEnum;
description?: string;
};

View File

@@ -1,3 +1,5 @@
import { i18nT } from '../../../../web/i18n/utils';
export enum BillTypeEnum {
balance = 'balance',
standSubPlan = 'standSubPlan',
@@ -6,16 +8,16 @@ export enum BillTypeEnum {
}
export const billTypeMap = {
[BillTypeEnum.balance]: {
label: 'support.wallet.subscription.type.balance'
label: i18nT('common:support.wallet.subscription.type.balance')
},
[BillTypeEnum.standSubPlan]: {
label: 'support.wallet.subscription.type.standard'
label: i18nT('common:support.wallet.subscription.type.standard')
},
[BillTypeEnum.extraDatasetSub]: {
label: 'support.wallet.subscription.type.extraDatasetSize'
label: i18nT('common:support.wallet.subscription.type.extraDatasetSize')
},
[BillTypeEnum.extraPoints]: {
label: 'support.wallet.subscription.type.extraPoints'
label: i18nT('common:support.wallet.subscription.type.extraPoints')
}
};
@@ -27,32 +29,46 @@ export enum BillStatusEnum {
}
export const billStatusMap = {
[BillStatusEnum.SUCCESS]: {
label: 'support.wallet.bill.status.success'
label: i18nT('common:support.wallet.bill.status.success')
},
[BillStatusEnum.REFUND]: {
label: 'support.wallet.bill.status.refund'
label: i18nT('common:support.wallet.bill.status.refund')
},
[BillStatusEnum.NOTPAY]: {
label: 'support.wallet.bill.status.notpay'
label: i18nT('common:support.wallet.bill.status.notpay')
},
[BillStatusEnum.CLOSED]: {
label: 'support.wallet.bill.status.closed'
label: i18nT('common:support.wallet.bill.status.closed')
}
};
export enum BillPayWayEnum {
balance = 'balance',
wx = 'wx'
wx = 'wx',
alipay = 'alipay',
bank = 'bank',
coupon = 'coupon'
}
export const billPayWayMap = {
[BillPayWayEnum.balance]: {
label: 'support.wallet.bill.payWay.balance'
label: i18nT('common:support.wallet.bill.payWay.balance')
},
[BillPayWayEnum.wx]: {
label: 'support.wallet.bill.payWay.wx'
label: i18nT('common:support.wallet.bill.payWay.wx')
},
[BillPayWayEnum.alipay]: {
label: i18nT('common:support.wallet.bill.payWay.alipay')
},
[BillPayWayEnum.bank]: {
label: i18nT('common:support.wallet.bill.payWay.bank')
},
[BillPayWayEnum.coupon]: {
label: i18nT('account_bill:payway_coupon')
}
};
export const SUB_DATASET_SIZE_RATE = 1000;
export const SUB_EXTRA_POINT_RATE = 1000;
export const MAX_WX_PAY_AMOUNT = 6000;
export const QR_CODE_SIZE = 210;

View File

@@ -1,6 +1,7 @@
import { StandardSubLevelEnum, SubModeEnum, SubTypeEnum } from '../sub/constants';
import { BillPayWayEnum, BillTypeEnum } from './constants';
import { BillPayWayEnum, BillStatusEnum, BillTypeEnum } from './constants';
import { TeamInvoiceHeaderType } from '../../user/team/type';
export type BillSchemaType = {
_id: string;
userId: string;
@@ -8,7 +9,7 @@ export type BillSchemaType = {
tmbId: string;
createTime: Date;
orderId: string;
status: 'SUCCESS' | 'REFUND' | 'NOTPAY' | 'CLOSED';
status: `${BillStatusEnum}`;
type: BillTypeEnum;
price: number;
hasInvoice: boolean;

View File

@@ -1,4 +1,5 @@
import { i18nT } from '../../../../web/i18n/utils';
import { BillTypeEnum } from '../bill/constants';
export enum SubTypeEnum {
standard = 'standard',
@@ -9,15 +10,18 @@ export enum SubTypeEnum {
export const subTypeMap = {
[SubTypeEnum.standard]: {
label: 'support.wallet.subscription.type.standard',
icon: 'support/account/plans'
icon: 'support/account/plans',
orderType: BillTypeEnum.standSubPlan
},
[SubTypeEnum.extraDatasetSize]: {
label: 'support.wallet.subscription.type.extraDatasetSize',
icon: 'core/dataset/datasetLight'
icon: 'core/dataset/datasetLight',
orderType: BillTypeEnum.extraDatasetSub
},
[SubTypeEnum.extraPoints]: {
label: 'support.wallet.subscription.type.extraPoints',
icon: 'core/chat/chatLight'
icon: 'core/chat/chatLight',
orderType: BillTypeEnum.extraPoints
}
};

View File

@@ -0,0 +1,16 @@
import { SubTypeEnum, StandardSubLevelEnum } from '../constants';
export type TeamCouponSub = {
type: `${SubTypeEnum}`; // Sub type
durationDay: number; // Duration day
level?: `${StandardSubLevelEnum}`; // Standard sub level
extraDatasetSize?: number; // Extra dataset size
totalPoints?: number; // Total points(Extrapoints or Standard sub)
};
export type TeamCouponSchema = {
key: string;
subscriptions: TeamCouponSub[];
redeemedAt?: Date;
expiredAt?: Date;
};

View File

@@ -15,7 +15,7 @@ export type TeamStandardSubPlanItemType = {
permissionCustomApiKey: boolean;
permissionCustomCopyright: boolean; // feature
permissionWebsiteSync: boolean;
permissionReRank: boolean;
permissionTeamOperationLog: boolean;
};
export type StandSubPlanLevelMapType = Record<

View File

@@ -3,5 +3,5 @@
"compilerOptions": {
"baseUrl": "."
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.d.ts", "../service/core/app/mcp.ts"]
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.d.ts"]
}

View File

@@ -1,3 +1,4 @@
import { ClientSession } from '../../mongo';
import { MongoTimerLock } from './schema';
import { addMinutes } from 'date-fns';
@@ -6,16 +7,23 @@ import { addMinutes } from 'date-fns';
*/
export const checkTimerLock = async ({
timerId,
lockMinuted
lockMinuted,
session
}: {
timerId: string;
lockMinuted: number;
session?: ClientSession;
}) => {
try {
await MongoTimerLock.create({
timerId,
expiredTime: addMinutes(new Date(), lockMinuted)
});
await MongoTimerLock.create(
[
{
timerId,
expiredTime: addMinutes(new Date(), lockMinuted)
}
],
{ session, ordered: true }
);
return true;
} catch (error) {

View File

@@ -1,11 +1,11 @@
/* vector crud */
import { PgVectorCtrl } from './pg/class';
import { ObVectorCtrl } from './oceanbase/class';
import { PgVectorCtrl } from './pg';
import { ObVectorCtrl } from './oceanbase';
import { getVectorsByText } from '../../core/ai/embedding';
import { DelDatasetVectorCtrlProps, InsertVectorProps } from './controller.d';
import { EmbeddingModelItemType } from '@fastgpt/global/core/ai/model.d';
import { MILVUS_ADDRESS, PG_ADDRESS, OCEANBASE_ADDRESS } from './constants';
import { MilvusCtrl } from './milvus/class';
import { MilvusCtrl } from './milvus';
import { setRedisCache, getRedisCache, delRedisCache, CacheKeyEnum } from '../redis/cache';
import { throttle } from 'lodash';
import { retryFn } from '@fastgpt/global/common/system/utils';

View File

@@ -12,7 +12,7 @@ import type {
InsertVectorControllerProps
} from '../controller.d';
import { delay } from '@fastgpt/global/common/system/utils';
import { addLog } from '../../../common/system/log';
import { addLog } from '../../system/log';
import { customNanoid } from '@fastgpt/global/common/string/tools';
export class MilvusCtrl {

View File

@@ -1,8 +1,8 @@
/* oceanbase vector crud */
import { DatasetVectorTableName } from '../constants';
import { delay } from '@fastgpt/global/common/system/utils';
import { ObClient } from './index';
import { RowDataPacket, ResultSetHeader } from 'mysql2/promise';
import { ObClient } from './controller';
import { RowDataPacket } from 'mysql2/promise';
import {
DelDatasetVectorCtrlProps,
EmbeddingRecallCtrlProps,

View File

@@ -1,9 +1,9 @@
/* pg vector crud */
import { DatasetVectorTableName } from '../constants';
import { delay } from '@fastgpt/global/common/system/utils';
import { PgClient, connectPg } from './index';
import { PgClient, connectPg } from './controller';
import { PgSearchRawType } from '@fastgpt/global/core/dataset/api';
import {
import type {
DelDatasetVectorCtrlProps,
EmbeddingRecallCtrlProps,
EmbeddingRecallResponse,

View File

@@ -2,7 +2,6 @@ import fs from 'fs';
import { getAxiosConfig } from '../config';
import axios from 'axios';
import FormData from 'form-data';
import { getSTTModel } from '../model';
import { STTModelType } from '@fastgpt/global/core/ai/model.d';
export const aiTranscriptions = async ({
@@ -24,7 +23,7 @@ export const aiTranscriptions = async ({
const aiAxiosConfig = getAxiosConfig();
const { data: result } = await axios<{ text: string }>({
const { data: result } = await axios<{ text: string; usage?: { total_tokens: number } }>({
method: 'post',
...(modelData.requestUrl
? { url: modelData.requestUrl }

View File

@@ -3,6 +3,7 @@ import { POST } from '../../../common/api/serverRequest';
import { getDefaultRerankModel } from '../model';
import { getAxiosConfig } from '../config';
import { RerankModelItemType } from '@fastgpt/global/core/ai/model.d';
import { countPromptTokens } from '../../../common/string/tiktoken';
type PostReRankResponse = {
id: string;
@@ -10,8 +11,17 @@ type PostReRankResponse = {
index: number;
relevance_score: number;
}[];
meta?: {
tokens: {
input_tokens: number;
output_tokens: number;
};
};
};
type ReRankCallResult = {
results: { id: string; score?: number }[];
inputTokens: number;
};
type ReRankCallResult = { id: string; score?: number }[];
export function reRankRecall({
model = getDefaultRerankModel(),
@@ -28,18 +38,22 @@ export function reRankRecall({
return Promise.reject('no rerank model');
}
if (documents.length === 0) {
return Promise.resolve([]);
return Promise.resolve({
results: [],
inputTokens: 0
});
}
const { baseUrl, authorization } = getAxiosConfig();
let start = Date.now();
const documentsTextArray = documents.map((doc) => doc.text);
return POST<PostReRankResponse>(
model.requestUrl ? model.requestUrl : `${baseUrl}/rerank`,
{
model: model.model,
query,
documents: documents.map((doc) => doc.text)
documents: documentsTextArray
},
{
headers: {
@@ -49,17 +63,22 @@ export function reRankRecall({
timeout: 30000
}
)
.then((data) => {
.then(async (data) => {
addLog.info('ReRank finish:', { time: Date.now() - start });
if (!data?.results || data?.results?.length === 0) {
addLog.error('ReRank error, empty result', data);
}
return data?.results?.map((item) => ({
id: documents[item.index].id,
score: item.relevance_score
}));
return {
results: data?.results?.map((item) => ({
id: documents[item.index].id,
score: item.relevance_score
})),
inputTokens:
data?.meta?.tokens?.input_tokens ||
(await countPromptTokens(documentsTextArray.join('\n') + query, ''))
};
})
.catch((err) => {
addLog.error('rerank error', err);

View File

@@ -2,50 +2,38 @@ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
import { ToolType } from '@fastgpt/global/core/app/type';
import { addLog } from '../../common/system/log';
import { retryFn } from '@fastgpt/global/common/system/utils';
export class MCPClient {
private client: Client | null = null;
private client: Client;
private url: string;
constructor(config: { url: string }) {
this.url = config.url;
this.client = new Client({
name: 'FastGPT-MCP-client',
version: '1.0.0'
});
}
private async getConnection(): Promise<Client> {
if (this.client) {
return this.client;
}
try {
const client = new Client({
name: 'FastGPT-MCP-http-client',
version: '1.0.0'
});
const transport = new StreamableHTTPClientTransport(new URL(this.url));
await client.connect(transport);
this.client = client;
return client;
await this.client.connect(transport);
return this.client;
} catch (error) {
const client = new Client({
name: 'FastGPT-MCP-sse-client',
version: '1.0.0'
});
const sseTransport = new SSEClientTransport(new URL(this.url));
await client.connect(sseTransport);
this.client = client;
return client;
await this.client.connect(new SSEClientTransport(new URL(this.url)));
return this.client;
}
}
// 内部方法:关闭连接
private async closeConnection() {
if (this.client) {
try {
await this.client.close();
this.client = null;
} catch (error) {
console.error('Failed to close MCP client:', error);
}
try {
await retryFn(() => this.client.close(), 3);
} catch (error) {
addLog.error('[MCP Client] Failed to close connection:', error);
}
}
@@ -58,7 +46,11 @@ export class MCPClient {
const client = await this.getConnection();
const response = await client.listTools();
const tools = (response.tools || []).map((tool: any) => ({
if (!Array.isArray(response.tools)) {
return Promise.reject('[MCP Client] Get tools response is not an array');
}
const tools = response.tools.map((tool) => ({
name: tool.name,
description: tool.description || '',
inputSchema: tool.inputSchema || {
@@ -67,9 +59,10 @@ export class MCPClient {
}
}));
// @ts-ignore
return tools;
} catch (error) {
console.error('Failed to get MCP tools:', error);
addLog.error('[MCP Client] Failed to get tools:', error);
return Promise.reject(error);
} finally {
await this.closeConnection();
@@ -85,28 +78,17 @@ export class MCPClient {
public async toolCall(toolName: string, params: Record<string, any>): Promise<any> {
try {
const client = await this.getConnection();
console.log(`Call tool: ${toolName}`, params);
addLog.debug(`[MCP Client] Call tool: ${toolName}`, params);
const result = await client.callTool({
return await client.callTool({
name: toolName,
arguments: params
});
return result;
} catch (error) {
console.error(`Failed to call tool ${toolName}:`, error);
addLog.error(`[MCP Client] Failed to call tool ${toolName}:`, error);
return Promise.reject(error);
} finally {
await this.closeConnection();
}
}
}
/**
* Create MCP client
* @param config Client configuration, containing url
* @returns MCPClient instance
*/
export default function getMCPClient(config: { url: string }): MCPClient {
return new MCPClient(config);
}

View File

@@ -82,7 +82,8 @@ const ChatItemSchema = new Schema({
[DispatchNodeResponseKeyEnum.nodeResponse]: {
type: Array,
default: []
}
},
durationSeconds: Number
});
try {

View File

@@ -34,6 +34,7 @@ type Props = {
outLinkUid?: string;
content: [UserChatItemType & { dataId?: string }, AIChatItemType & { dataId?: string }];
metadata?: Record<string, any>;
durationSeconds: number; //s
};
export async function saveChat({
@@ -51,8 +52,11 @@ export async function saveChat({
shareId,
outLinkUid,
content,
durationSeconds,
metadata = {}
}: Props) {
if (!chatId || chatId === 'NO_RECORD_HISTORIES') return;
try {
const chat = await MongoChat.findOne(
{
@@ -78,34 +82,33 @@ export async function saveChat({
// Format save chat content: Remove quote q/a
const processedContent = content.map((item) => {
if (item.obj === ChatRoleEnum.AI) {
const nodeResponse = item[DispatchNodeResponseKeyEnum.nodeResponse];
const nodeResponse = item[DispatchNodeResponseKeyEnum.nodeResponse]?.map((responseItem) => {
if (
responseItem.moduleType === FlowNodeTypeEnum.datasetSearchNode &&
responseItem.quoteList
) {
return {
...responseItem,
quoteList: responseItem.quoteList.map((quote: any) => ({
id: quote.id,
chunkIndex: quote.chunkIndex,
datasetId: quote.datasetId,
collectionId: quote.collectionId,
sourceId: quote.sourceId,
sourceName: quote.sourceName,
score: quote.score,
tokens: quote.tokens
}))
};
}
return responseItem;
});
if (nodeResponse) {
return {
...item,
[DispatchNodeResponseKeyEnum.nodeResponse]: nodeResponse.map((responseItem) => {
if (
responseItem.moduleType === FlowNodeTypeEnum.datasetSearchNode &&
responseItem.quoteList
) {
return {
...responseItem,
quoteList: responseItem.quoteList.map((quote: any) => ({
id: quote.id,
chunkIndex: quote.chunkIndex,
datasetId: quote.datasetId,
collectionId: quote.collectionId,
sourceId: quote.sourceId,
sourceName: quote.sourceName,
score: quote.score,
tokens: quote.tokens
}))
};
}
return responseItem;
})
};
}
return {
...item,
[DispatchNodeResponseKeyEnum.nodeResponse]: nodeResponse,
durationSeconds
};
}
return item;
});
@@ -175,13 +178,15 @@ export const updateInteractiveChat = async ({
appId,
userInteractiveVal,
aiResponse,
newVariables
newVariables,
durationSeconds
}: {
chatId: string;
appId: string;
userInteractiveVal: string;
aiResponse: AIChatItemType & { dataId?: string };
newVariables?: Record<string, any>;
durationSeconds: number;
}) => {
if (!chatId) return;
@@ -246,6 +251,10 @@ export const updateInteractiveChat = async ({
chatItem.value = chatItem.value ? [...chatItem.value, ...aiResponse.value] : aiResponse.value;
}
chatItem.durationSeconds = chatItem.durationSeconds
? +(chatItem.durationSeconds + durationSeconds).toFixed(2)
: durationSeconds;
await mongoSessionRun(async (session) => {
await chatItem.save({ session });
await MongoChat.updateOne(

View File

@@ -9,7 +9,7 @@ import { DatasetCollectionSchemaType, DatasetSchemaType } from '@fastgpt/global/
import { MongoDatasetTraining } from '../training/schema';
import { MongoDatasetData } from '../data/schema';
import { delImgByRelatedId } from '../../../common/file/image/controller';
import { deleteDatasetDataVector } from '../../../common/vectorStore/controller';
import { deleteDatasetDataVector } from '../../../common/vectorDB/controller';
import { delFileByFileIdList } from '../../../common/file/gridfs/controller';
import { BucketNameEnum } from '@fastgpt/global/common/file/constants';
import { ClientSession } from '../../../common/mongo';

View File

@@ -5,7 +5,7 @@ import { delCollectionRelatedSource } from './collection/controller';
import { ClientSession } from '../../common/mongo';
import { MongoDatasetTraining } from './training/schema';
import { MongoDatasetData } from './data/schema';
import { deleteDatasetDataVector } from '../../common/vectorStore/controller';
import { deleteDatasetDataVector } from '../../common/vectorDB/controller';
import { MongoDatasetDataText } from './data/dataTextSchema';
import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset';
import { retryFn } from '@fastgpt/global/common/system/utils';

View File

@@ -3,7 +3,7 @@ import {
DatasetSearchModeMap,
SearchScoreTypeEnum
} from '@fastgpt/global/core/dataset/constants';
import { recallFromVectorStore } from '../../../common/vectorStore/controller';
import { recallFromVectorStore } from '../../../common/vectorDB/controller';
import { getVectorsByText } from '../../ai/embedding';
import { getEmbeddingModel, getDefaultRerankModel, getLLMModel } from '../../ai/model';
import { MongoDatasetData } from '../data/schema';
@@ -62,7 +62,8 @@ export type SearchDatasetDataProps = {
export type SearchDatasetDataResponse = {
searchRes: SearchDataResponseItemType[];
tokens: number;
embeddingTokens: number;
reRankInputTokens: number;
searchMode: `${DatasetSearchModeEnum}`;
limit: number;
similarity: number;
@@ -86,8 +87,11 @@ export const datasetDataReRank = async ({
rerankModel?: RerankModelItemType;
data: SearchDataResponseItemType[];
query: string;
}): Promise<SearchDataResponseItemType[]> => {
const results = await reRankRecall({
}): Promise<{
results: SearchDataResponseItemType[];
inputTokens: number;
}> => {
const { results, inputTokens } = await reRankRecall({
model: rerankModel,
query,
documents: data.map((item) => ({
@@ -114,7 +118,10 @@ export const datasetDataReRank = async ({
})
.filter(Boolean) as SearchDataResponseItemType[];
return mergeResult;
return {
results: mergeResult,
inputTokens
};
};
export const filterDatasetDataByMaxTokens = async (
data: SearchDataResponseItemType[],
@@ -694,14 +701,23 @@ export async function searchDatasetData(
const { embeddingLimit, fullTextLimit } = countRecallLimit();
// recall
const { embeddingRecallResults, fullTextRecallResults, tokens } = await multiQueryRecall({
const {
embeddingRecallResults,
fullTextRecallResults,
tokens: embeddingTokens
} = await multiQueryRecall({
embeddingLimit,
fullTextLimit
});
// ReRank results
const reRankResults = await (async () => {
if (!usingReRank) return [];
const { results: reRankResults, inputTokens: reRankInputTokens } = await (async () => {
if (!usingReRank) {
return {
results: [],
inputTokens: 0
};
}
set = new Set<string>(embeddingRecallResults.map((item) => item.id));
const concatRecallResults = embeddingRecallResults.concat(
@@ -725,7 +741,10 @@ export async function searchDatasetData(
});
} catch (error) {
usingReRank = false;
return [];
return {
results: [],
inputTokens: 0
};
}
})();
@@ -790,7 +809,8 @@ export async function searchDatasetData(
return {
searchRes: filterMaxTokensResult,
tokens,
embeddingTokens,
reRankInputTokens,
searchMode,
limit: maxTokens,
similarity,

View File

@@ -12,7 +12,6 @@ import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workfl
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
import { DatasetSearchModeEnum } from '@fastgpt/global/core/dataset/constants';
import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
import { checkTeamReRankPermission } from '../../../../support/permission/teamLimit';
import { MongoDataset } from '../../../dataset/schema';
import { i18nT } from '../../../../../web/i18n/utils';
import { filterDatasetsByTmbId } from '../../../dataset/utils';
@@ -119,6 +118,8 @@ export async function dispatchDatasetSearch(
const vectorModel = getEmbeddingModel(
(await MongoDataset.findById(datasets[0].datasetId, 'vectorModel').lean())?.vectorModel
);
// Get Rerank Model
const rerankModelData = getRerankModel(rerankModel);
// start search
const searchData = {
@@ -132,14 +133,15 @@ export async function dispatchDatasetSearch(
datasetIds,
searchMode,
embeddingWeight,
usingReRank: usingReRank && (await checkTeamReRankPermission(teamId)),
rerankModel: getRerankModel(rerankModel),
usingReRank,
rerankModel: rerankModelData,
rerankWeight,
collectionFilterMatch
};
const {
searchRes,
tokens,
embeddingTokens,
reRankInputTokens,
usingSimilarityFilter,
usingReRank: searchUsingReRank,
queryExtensionResult,
@@ -164,17 +166,29 @@ export async function dispatchDatasetSearch(
const { totalPoints: embeddingTotalPoints, modelName: embeddingModelName } =
formatModelChars2Points({
model: vectorModel.model,
inputTokens: tokens,
inputTokens: embeddingTokens,
modelType: ModelTypeEnum.embedding
});
nodeDispatchUsages.push({
totalPoints: embeddingTotalPoints,
moduleName: node.name,
model: embeddingModelName,
inputTokens: tokens
inputTokens: embeddingTokens
});
// Rerank
const { totalPoints: reRankTotalPoints, modelName: reRankModelName } = formatModelChars2Points({
model: rerankModelData.model,
inputTokens: reRankInputTokens,
modelType: ModelTypeEnum.rerank
});
nodeDispatchUsages.push({
totalPoints: reRankTotalPoints,
moduleName: node.name,
model: reRankModelName,
inputTokens: reRankInputTokens
});
// Query extension
const { totalPoints: queryExtensionTotalPoints } = (() => {
(() => {
if (queryExtensionResult) {
const { totalPoints, modelName } = formatModelChars2Points({
model: queryExtensionResult.model,
@@ -198,7 +212,7 @@ export async function dispatchDatasetSearch(
};
})();
// Deep search
const { totalPoints: deepSearchTotalPoints } = (() => {
(() => {
if (deepSearchResult) {
const { totalPoints, modelName } = formatModelChars2Points({
model: deepSearchResult.model,
@@ -221,20 +235,26 @@ export async function dispatchDatasetSearch(
totalPoints: 0
};
})();
const totalPoints = embeddingTotalPoints + queryExtensionTotalPoints + deepSearchTotalPoints;
const totalPoints = nodeDispatchUsages.reduce((acc, item) => acc + item.totalPoints, 0);
const responseData: DispatchNodeResponseType & { totalPoints: number } = {
totalPoints,
query: userChatInput,
model: vectorModel.model,
inputTokens: tokens,
embeddingModel: vectorModel.name,
embeddingTokens,
similarity: usingSimilarityFilter ? similarity : undefined,
limit,
searchMode,
embeddingWeight: searchMode === DatasetSearchModeEnum.mixedRecall ? embeddingWeight : undefined,
rerankModel: usingReRank ? getRerankModel(rerankModel)?.name : undefined,
rerankWeight: usingReRank ? rerankWeight : undefined,
searchUsingReRank: searchUsingReRank,
// Rerank
...(searchUsingReRank && {
rerankModel: rerankModelData?.name,
rerankWeight: rerankWeight,
reRankInputTokens
}),
searchUsingReRank,
// Results
quoteList: searchRes,
queryExtensionResult,
deepSearchResult

View File

@@ -74,7 +74,7 @@ import { dispatchLoopStart } from './loop/runLoopStart';
import { dispatchFormInput } from './interactive/formInput';
import { dispatchToolParams } from './agent/runTool/toolParams';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { filterModuleTypeList } from '@fastgpt/global/core/chat/utils';
import { filterPublicNodeResponseData } from '@fastgpt/global/core/chat/utils';
import { dispatchRunTool } from './plugin/runTool';
const callbackMap: Record<FlowNodeTypeEnum, Function> = {
@@ -137,8 +137,10 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
stream = false,
version = 'v1',
responseDetail = true,
responseAllData = true,
...props
} = data;
const startTime = Date.now();
rewriteRuntimeWorkFlow(runtimeNodes, runtimeEdges);
@@ -162,16 +164,24 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
[DispatchNodeResponseKeyEnum.runTimes]: 1,
[DispatchNodeResponseKeyEnum.assistantResponses]: [],
[DispatchNodeResponseKeyEnum.toolResponses]: null,
newVariables: removeSystemVariable(variables, externalProvider.externalWorkflowVariables)
newVariables: removeSystemVariable(variables, externalProvider.externalWorkflowVariables),
durationSeconds: 0
};
}
let workflowRunTimes = 0;
// set sse response headers
// Init
if (isRootRuntime) {
// set sse response headers
res?.setHeader('Connection', 'keep-alive'); // Set keepalive for long connection
if (stream && res) {
res.on('close', () => res.end());
res.on('error', () => {
addLog.error('Request error');
res.end();
});
res.setHeader('Content-Type', 'text/event-stream;charset=utf-8');
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('X-Accel-Buffering', 'no');
@@ -191,13 +201,14 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
};
sendStreamTimerSign();
}
}
variables = {
...getSystemVariable(data),
...externalProvider.externalWorkflowVariables,
...variables
};
// Add system variables
variables = {
...getSystemVariable(data),
...externalProvider.externalWorkflowVariables,
...variables
};
}
let chatResponses: ChatHistoryItemResType[] = []; // response request and save to database
let chatAssistantResponse: AIChatItemValueItemType[] = []; // The value will be returned to the user
@@ -640,16 +651,15 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
})();
// Response node response
if (
version === 'v2' &&
!props.isToolCall &&
isRootRuntime &&
formatResponseData &&
!(responseDetail === false && filterModuleTypeList.includes(formatResponseData.moduleType))
) {
if (version === 'v2' && !props.isToolCall && isRootRuntime && formatResponseData) {
props.workflowStreamResponse?.({
event: SseResponseEventEnum.flowNodeResponse,
data: formatResponseData
data: responseAllData
? formatResponseData
: filterPublicNodeResponseData({
flowResponses: [formatResponseData],
responseDetail
})[0]
});
}
@@ -737,6 +747,15 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
}
})();
const durationSeconds = +((Date.now() - startTime) / 1000).toFixed(2);
if (isRootRuntime && stream) {
props.workflowStreamResponse?.({
event: SseResponseEventEnum.workflowDuration,
data: { durationSeconds }
});
}
return {
flowResponses: chatResponses,
flowUsages: chatNodeUsages,
@@ -750,7 +769,8 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
[DispatchNodeResponseKeyEnum.assistantResponses]:
mergeAssistantResponseAnswerText(chatAssistantResponse),
[DispatchNodeResponseKeyEnum.toolResponses]: toolRunResponse,
newVariables: removeSystemVariable(variables, externalProvider.externalWorkflowVariables)
newVariables: removeSystemVariable(variables, externalProvider.externalWorkflowVariables),
durationSeconds
};
} catch (error) {
return Promise.reject(error);

View File

@@ -4,7 +4,8 @@ import {
} from '@fastgpt/global/core/workflow/runtime/type';
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import getMCPClient from '../../../app/mcp';
import { MCPClient } from '../../../app/mcp';
import { getErrText } from '@fastgpt/global/common/error/utils';
type RunToolProps = ModuleDispatchProps<{
toolData: {
@@ -14,7 +15,7 @@ type RunToolProps = ModuleDispatchProps<{
}>;
type RunToolResponse = DispatchNodeResultType<{
[NodeOutputKeyEnum.rawResponse]: any;
[NodeOutputKeyEnum.rawResponse]?: any;
}>;
export const dispatchRunTool = async (props: RunToolProps): Promise<RunToolResponse> => {
@@ -26,7 +27,7 @@ export const dispatchRunTool = async (props: RunToolProps): Promise<RunToolRespo
const { toolData, ...restParams } = params;
const { name: toolName, url } = toolData;
const mcpClient = getMCPClient({ url });
const mcpClient = new MCPClient({ url });
try {
const result = await mcpClient.toolCall(toolName, restParams);
@@ -40,7 +41,12 @@ export const dispatchRunTool = async (props: RunToolProps): Promise<RunToolRespo
[NodeOutputKeyEnum.rawResponse]: result
};
} catch (error) {
console.error('Error running MCP tool:', error);
return Promise.reject(error);
return {
[DispatchNodeResponseKeyEnum.nodeResponse]: {
moduleLogo: avatar,
error: getErrText(error)
},
[DispatchNodeResponseKeyEnum.toolResponses]: getErrText(error)
};
}
};

View File

@@ -26,6 +26,7 @@ export type DispatchFlowResponse = {
[DispatchNodeResponseKeyEnum.assistantResponses]: AIChatItemValueItemType[];
[DispatchNodeResponseKeyEnum.runTimes]: number;
newVariables: Record<string, string>;
durationSeconds: number;
};
export type WorkflowResponseType = ({

View File

@@ -38,14 +38,14 @@ const McpKeySchema = new Schema({
ref: AppCollectionName,
required: true
},
appName: String,
toolName: {
type: String
},
toolAlias: {
type: String
type: String,
required: true
},
description: {
type: String
type: String,
required: true
}
}
],

View File

@@ -14,7 +14,6 @@ export function addOperationLog<T extends OperationLogEventEnum>({
event: T;
params?: TemplateParamsMap[T];
}) {
console.log('Insert log');
retryFn(() =>
MongoOperationLog.create({
tmbId: tmbId,

View File

@@ -1,85 +1,74 @@
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
import { i18nT } from '../../../web/i18n/utils';
export const operationLogI18nMap = {
export const operationLogMap = {
[OperationLogEventEnum.LOGIN]: {
content: i18nT('account_team:log_login'),
typeLabel: i18nT('account_team:login')
typeLabel: i18nT('account_team:login'),
params: {} as { name?: string }
},
[OperationLogEventEnum.CREATE_INVITATION_LINK]: {
content: i18nT('account_team:log_create_invitation_link'),
typeLabel: i18nT('account_team:create_invitation_link')
typeLabel: i18nT('account_team:create_invitation_link'),
params: {} as { name?: string; link: string }
},
[OperationLogEventEnum.JOIN_TEAM]: {
content: i18nT('account_team:log_join_team'),
typeLabel: i18nT('account_team:join_team')
typeLabel: i18nT('account_team:join_team'),
params: {} as { name?: string; link: string }
},
[OperationLogEventEnum.CHANGE_MEMBER_NAME]: {
content: i18nT('account_team:log_change_member_name'),
typeLabel: i18nT('account_team:change_member_name')
typeLabel: i18nT('account_team:change_member_name'),
params: {} as { name?: string; memberName: string; newName: string }
},
[OperationLogEventEnum.KICK_OUT_TEAM]: {
content: i18nT('account_team:log_kick_out_team'),
typeLabel: i18nT('account_team:kick_out_team')
typeLabel: i18nT('account_team:kick_out_team'),
params: {} as { name?: string; memberName: string }
},
[OperationLogEventEnum.RECOVER_TEAM_MEMBER]: {
content: i18nT('account_team:log_recover_team_member'),
typeLabel: i18nT('account_team:recover_team_member'),
params: {} as { name?: string; memberName: string }
},
[OperationLogEventEnum.CREATE_DEPARTMENT]: {
content: i18nT('account_team:log_create_department'),
typeLabel: i18nT('account_team:create_department')
typeLabel: i18nT('account_team:create_department'),
params: {} as { name?: string; departmentName: string }
},
[OperationLogEventEnum.CHANGE_DEPARTMENT]: {
content: i18nT('account_team:log_change_department'),
typeLabel: i18nT('account_team:change_department_name')
typeLabel: i18nT('account_team:change_department_name'),
params: {} as { name?: string; departmentName: string }
},
[OperationLogEventEnum.DELETE_DEPARTMENT]: {
content: i18nT('account_team:log_delete_department'),
typeLabel: i18nT('account_team:delete_department')
typeLabel: i18nT('account_team:delete_department'),
params: {} as { name?: string; departmentName: string }
},
[OperationLogEventEnum.RELOCATE_DEPARTMENT]: {
content: i18nT('account_team:log_relocate_department'),
typeLabel: i18nT('account_team:relocate_department')
typeLabel: i18nT('account_team:relocate_department'),
params: {} as { name?: string; departmentName: string }
},
[OperationLogEventEnum.CREATE_GROUP]: {
content: i18nT('account_team:log_create_group'),
typeLabel: i18nT('account_team:create_group')
typeLabel: i18nT('account_team:create_group'),
params: {} as { name?: string; groupName: string }
},
[OperationLogEventEnum.DELETE_GROUP]: {
content: i18nT('account_team:log_delete_group'),
typeLabel: i18nT('account_team:delete_group')
typeLabel: i18nT('account_team:delete_group'),
params: {} as { name?: string; groupName: string }
},
[OperationLogEventEnum.ASSIGN_PERMISSION]: {
content: i18nT('account_team:log_assign_permission'),
typeLabel: i18nT('account_team:assign_permission')
typeLabel: i18nT('account_team:assign_permission'),
params: {} as { name?: string; objectName: string; permission: string }
}
} as const;
export type TemplateParamsMap = {
[OperationLogEventEnum.LOGIN]: { name?: string };
[OperationLogEventEnum.CREATE_INVITATION_LINK]: { name?: string; link: string };
[OperationLogEventEnum.JOIN_TEAM]: { name?: string; link: string };
[OperationLogEventEnum.CHANGE_MEMBER_NAME]: {
name?: string;
memberName: string;
newName: string;
};
[OperationLogEventEnum.KICK_OUT_TEAM]: {
name?: string;
memberName: string;
};
[OperationLogEventEnum.CREATE_DEPARTMENT]: { name?: string; departmentName: string };
[OperationLogEventEnum.CHANGE_DEPARTMENT]: {
name?: string;
departmentName: string;
};
[OperationLogEventEnum.DELETE_DEPARTMENT]: { name?: string; departmentName: string };
[OperationLogEventEnum.RELOCATE_DEPARTMENT]: {
name?: string;
departmentName: string;
};
[OperationLogEventEnum.CREATE_GROUP]: { name?: string; groupName: string };
[OperationLogEventEnum.DELETE_GROUP]: { name?: string; groupName: string };
[OperationLogEventEnum.ASSIGN_PERMISSION]: {
name?: string;
objectName: string;
permission: string;
};
[K in OperationLogEventEnum]: (typeof operationLogMap)[K]['params'];
};

View File

@@ -74,14 +74,3 @@ export const checkTeamAppLimit = async (teamId: string, amount = 1) => {
return Promise.reject(TeamErrEnum.appAmountNotEnough);
}
};
export const checkTeamReRankPermission = async (teamId: string) => {
const { standardConstants } = await getTeamStandPlan({
teamId
});
if (standardConstants && !standardConstants?.permissionReRank) {
return false;
}
return true;
};

View File

@@ -0,0 +1,33 @@
import { addDays } from 'date-fns';
import { connectionMongo, getMongoModel } from '../../../common/mongo';
const { Schema } = connectionMongo;
import type { TeamCouponSchema } from '@fastgpt/global/support/wallet/sub/coupon/type';
export const couponCollectionName = 'team_sub_coupons';
const CouponSchema = new Schema({
key: {
type: String,
required: true
},
subscriptions: {
type: [Object],
required: true
},
redeemedAt: {
type: Date,
default: undefined
},
expiredAt: {
type: Date,
default: () => addDays(new Date(), 7)
}
});
try {
CouponSchema.index({ key: 1 }, { unique: true });
} catch (error) {
console.log(error);
}
export const MongoTeamCoupon = getMongoModel<TeamCouponSchema>(couponCollectionName, CouponSchema);

View File

@@ -1,4 +1,4 @@
/*
/*
user sub plan
1. type=standard: There will only be 1, and each team will have one
2. type=extraDatasetSize/extraPoints: Can buy multiple

View File

@@ -6,7 +6,7 @@ import {
} from '@fastgpt/global/support/wallet/sub/constants';
import { MongoTeamSub } from './schema';
import { FeTeamPlanStatusType, TeamSubSchema } from '@fastgpt/global/support/wallet/sub/type.d';
import { getVectorCountByTeamId } from '../../../common/vectorStore/controller';
import { getVectorCountByTeamId } from '../../../common/vectorDB/controller';
import dayjs from 'dayjs';
import { ClientSession } from '../../../common/mongo';
import { addMonths } from 'date-fns';

View File

@@ -22,7 +22,7 @@ export const formatModelChars2Points = ({
};
}
const isIOPriceType = typeof modelData.inputPrice === 'number';
const isIOPriceType = typeof modelData.inputPrice === 'number' && modelData.inputPrice > 0;
const totalPoints = isIOPriceType
? (modelData.inputPrice || 0) * (inputTokens / multiple) +

View File

@@ -1,6 +1,7 @@
// @ts-nocheck
export const iconPaths = {
alignLeft: () => import('./icons/alignLeft.svg'),
book: () => import('./icons/book.svg'),
change: () => import('./icons/change.svg'),
chatSend: () => import('./icons/chatSend.svg'),
@@ -17,6 +18,7 @@ export const iconPaths = {
'common/addLight': () => import('./icons/common/addLight.svg'),
'common/addUser': () => import('./icons/common/addUser.svg'),
'common/administrator': () => import('./icons/common/administrator.svg'),
'common/alipay': () => import('./icons/common/alipay.svg'),
'common/app': () => import('./icons/common/app.svg'),
'common/arrowLeft': () => import('./icons/common/arrowLeft.svg'),
'common/arrowRight': () => import('./icons/common/arrowRight.svg'),
@@ -105,15 +107,16 @@ export const iconPaths = {
'common/tickFill': () => import('./icons/common/tickFill.svg'),
'common/toolkit': () => import('./icons/common/toolkit.svg'),
'common/trash': () => import('./icons/common/trash.svg'),
'common/upRightArrowLight': () => import('./icons/common/upRightArrowLight.svg'),
'common/uploadFileFill': () => import('./icons/common/uploadFileFill.svg'),
'common/upperRight': () => import('./icons/common/upperRight.svg'),
'common/upRightArrowLight': () => import('./icons/common/upRightArrowLight.svg'),
'common/userInfo': () => import('./icons/common/userInfo.svg'),
'common/variable': () => import('./icons/common/variable.svg'),
'common/viewLight': () => import('./icons/common/viewLight.svg'),
'common/voiceLight': () => import('./icons/common/voiceLight.svg'),
'common/wallet': () => import('./icons/common/wallet.svg'),
'common/warn': () => import('./icons/common/warn.svg'),
'common/wechat': () => import('./icons/common/wechat.svg'),
'common/wechatFill': () => import('./icons/common/wechatFill.svg'),
'common/wecom': () => import('./icons/common/wecom.svg'),
configmap: () => import('./icons/configmap.svg'),
@@ -143,8 +146,6 @@ export const iconPaths = {
'core/app/simpleMode/tts': () => import('./icons/core/app/simpleMode/tts.svg'),
'core/app/simpleMode/variable': () => import('./icons/core/app/simpleMode/variable.svg'),
'core/app/simpleMode/whisper': () => import('./icons/core/app/simpleMode/whisper.svg'),
'core/app/templates/TranslateRobot': () =>
import('./icons/core/app/templates/TranslateRobot.svg'),
'core/app/templates/animalLife': () => import('./icons/core/app/templates/animalLife.svg'),
'core/app/templates/chinese': () => import('./icons/core/app/templates/chinese.svg'),
'core/app/templates/divination': () => import('./icons/core/app/templates/divination.svg'),
@@ -154,6 +155,8 @@ export const iconPaths = {
'core/app/templates/plugin-dalle': () => import('./icons/core/app/templates/plugin-dalle.svg'),
'core/app/templates/plugin-feishu': () => import('./icons/core/app/templates/plugin-feishu.svg'),
'core/app/templates/stock': () => import('./icons/core/app/templates/stock.svg'),
'core/app/templates/TranslateRobot': () =>
import('./icons/core/app/templates/TranslateRobot.svg'),
'core/app/toolCall': () => import('./icons/core/app/toolCall.svg'),
'core/app/ttsFill': () => import('./icons/core/app/ttsFill.svg'),
'core/app/type/httpPlugin': () => import('./icons/core/app/type/httpPlugin.svg'),
@@ -172,7 +175,6 @@ export const iconPaths = {
'core/app/variable/input': () => import('./icons/core/app/variable/input.svg'),
'core/app/variable/select': () => import('./icons/core/app/variable/select.svg'),
'core/app/variable/textarea': () => import('./icons/core/app/variable/textarea.svg'),
'core/chat/QGFill': () => import('./icons/core/chat/QGFill.svg'),
'core/chat/backText': () => import('./icons/core/chat/backText.svg'),
'core/chat/cancelSpeak': () => import('./icons/core/chat/cancelSpeak.svg'),
'core/chat/chatFill': () => import('./icons/core/chat/chatFill.svg'),
@@ -189,6 +191,7 @@ export const iconPaths = {
'core/chat/fileSelect': () => import('./icons/core/chat/fileSelect.svg'),
'core/chat/finishSpeak': () => import('./icons/core/chat/finishSpeak.svg'),
'core/chat/imgSelect': () => import('./icons/core/chat/imgSelect.svg'),
'core/chat/QGFill': () => import('./icons/core/chat/QGFill.svg'),
'core/chat/quoteFill': () => import('./icons/core/chat/quoteFill.svg'),
'core/chat/quoteSign': () => import('./icons/core/chat/quoteSign.svg'),
'core/chat/recordFill': () => import('./icons/core/chat/recordFill.svg'),
@@ -274,13 +277,12 @@ export const iconPaths = {
'core/workflow/redo': () => import('./icons/core/workflow/redo.svg'),
'core/workflow/revertVersion': () => import('./icons/core/workflow/revertVersion.svg'),
'core/workflow/runError': () => import('./icons/core/workflow/runError.svg'),
'core/workflow/running': () => import('./icons/core/workflow/running.svg'),
'core/workflow/runSkip': () => import('./icons/core/workflow/runSkip.svg'),
'core/workflow/runSuccess': () => import('./icons/core/workflow/runSuccess.svg'),
'core/workflow/running': () => import('./icons/core/workflow/running.svg'),
'core/workflow/template/BI': () => import('./icons/core/workflow/template/BI.svg'),
'core/workflow/template/FileRead': () => import('./icons/core/workflow/template/FileRead.svg'),
'core/workflow/template/aiChat': () => import('./icons/core/workflow/template/aiChat.svg'),
'core/workflow/template/baseChart': () => import('./icons/core/workflow/template/baseChart.svg'),
'core/workflow/template/BI': () => import('./icons/core/workflow/template/BI.svg'),
'core/workflow/template/bing': () => import('./icons/core/workflow/template/bing.svg'),
'core/workflow/template/codeRun': () => import('./icons/core/workflow/template/codeRun.svg'),
'core/workflow/template/customFeedback': () =>
@@ -296,6 +298,7 @@ export const iconPaths = {
'core/workflow/template/extractJson': () =>
import('./icons/core/workflow/template/extractJson.svg'),
'core/workflow/template/fetchUrl': () => import('./icons/core/workflow/template/fetchUrl.svg'),
'core/workflow/template/FileRead': () => import('./icons/core/workflow/template/FileRead.svg'),
'core/workflow/template/formInput': () => import('./icons/core/workflow/template/formInput.svg'),
'core/workflow/template/getTime': () => import('./icons/core/workflow/template/getTime.svg'),
'core/workflow/template/google': () => import('./icons/core/workflow/template/google.svg'),
@@ -325,12 +328,12 @@ export const iconPaths = {
'core/workflow/template/textConcat': () =>
import('./icons/core/workflow/template/textConcat.svg'),
'core/workflow/template/toolCall': () => import('./icons/core/workflow/template/toolCall.svg'),
'core/workflow/template/toolParams': () =>
import('./icons/core/workflow/template/toolParams.svg'),
'core/workflow/template/toolkitActive': () =>
import('./icons/core/workflow/template/toolkitActive.svg'),
'core/workflow/template/toolkitInactive': () =>
import('./icons/core/workflow/template/toolkitInactive.svg'),
'core/workflow/template/toolParams': () =>
import('./icons/core/workflow/template/toolParams.svg'),
'core/workflow/template/userSelect': () =>
import('./icons/core/workflow/template/userSelect.svg'),
'core/workflow/template/variable': () => import('./icons/core/workflow/template/variable.svg'),
@@ -388,10 +391,10 @@ export const iconPaths = {
'modal/selectSource': () => import('./icons/modal/selectSource.svg'),
'modal/setting': () => import('./icons/modal/setting.svg'),
'modal/teamPlans': () => import('./icons/modal/teamPlans.svg'),
'model/BAAI': () => import('./icons/model/BAAI.svg'),
'model/alicloud': () => import('./icons/model/alicloud.svg'),
'model/aws': () => import('./icons/model/aws.svg'),
'model/azure': () => import('./icons/model/azure.svg'),
'model/BAAI': () => import('./icons/model/BAAI.svg'),
'model/baichuan': () => import('./icons/model/baichuan.svg'),
'model/chatglm': () => import('./icons/model/chatglm.svg'),
'model/claude': () => import('./icons/model/claude.svg'),
@@ -439,6 +442,7 @@ export const iconPaths = {
save: () => import('./icons/save.svg'),
sliderTag: () => import('./icons/sliderTag.svg'),
stop: () => import('./icons/stop.svg'),
'support/account/coupon': () => import('./icons/support/account/coupon.svg'),
'support/account/laf': () => import('./icons/support/account/laf.svg'),
'support/account/loginoutLight': () => import('./icons/support/account/loginoutLight.svg'),
'support/account/plans': () => import('./icons/support/account/plans.svg'),

View File

@@ -0,0 +1 @@
<svg t="1745324194466" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2622" ><path d="M96 128h832v96H96zM96 576h832v96H96zM96 352h576v96H96zM96 800h576v96H96z" p-id="2623"></path></svg>

After

Width:  |  Height:  |  Size: 230 B

View File

@@ -0,0 +1,5 @@
<svg width="18" height="19" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Frame">
<path id="Vector" d="M10.1717 12.3797C10.1717 12.3797 9 13.3168 8.57419 13.5626C8.24535 13.7523 7.89703 13.906 7.53525 14.0211C7.2673 14.1102 6.99132 14.1732 6.71119 14.2089C6.4755 14.2348 6.28031 14.2511 6.12563 14.259C6.10832 14.2668 6.08948 14.2707 6.0705 14.2702H5.89331C5.50724 14.2725 5.12255 14.2239 4.74919 14.1257C4.41511 14.0406 4.0989 13.8966 3.81544 13.7004C3.5536 13.5164 3.33796 13.2742 3.18544 12.9928C3.03019 12.7087 2.95312 12.3791 2.95312 12.0034C2.96044 11.6794 3.04875 11.3846 3.2175 11.1191C3.38896 10.8519 3.61285 10.6223 3.87562 10.4441C4.15006 10.2573 4.45255 10.1154 4.77169 10.0239C5.09256 9.92927 5.4269 9.88841 5.76112 9.90299C6.08512 9.91761 6.39844 9.95586 6.7005 10.0183C6.9936 10.0788 7.28331 10.1546 7.56844 10.2456C7.84462 10.3339 8.10619 10.4295 8.35313 10.5324C8.60063 10.6365 8.83406 10.7355 9.05512 10.8317C9.24461 10.9108 9.43631 10.9845 9.63 11.0527C9.84375 10.7799 10.0238 10.5127 10.1717 10.2512C10.4021 9.84644 10.5982 9.42308 10.7578 8.98555C10.8096 8.83818 10.8422 8.73524 10.8574 8.67618H6.05925V8.14518H8.325V6.91893H5.17444V6.38793H8.32556V5.3923C8.32556 5.32649 8.36212 5.27305 8.43581 5.23255C8.5095 5.19205 8.59444 5.16449 8.69006 5.1493C8.80088 5.12736 8.92631 5.11611 9.06581 5.11611H9.91687V6.38736H13.1563V6.91836H9.91687V8.14518H12.4909L12.4926 8.13393V8.14518H12.4909C12.4391 8.47368 12.3514 8.82018 12.2276 9.18468C12.1014 9.55657 11.948 9.91867 11.7686 10.2681C11.5428 10.7094 11.2743 11.1277 10.9671 11.5168C10.9671 11.5168 13.5039 12.7312 16.1826 13.1582C16.6269 12.1721 16.875 11.0769 16.875 9.92493C16.875 5.57568 13.3492 2.04993 9 2.04993C4.65075 2.04993 1.125 5.57568 1.125 9.92493C1.125 14.2736 4.65075 17.7999 9 17.7999C11.6978 17.7999 14.0782 16.4432 15.498 14.3749C14.0557 14.0604 12.5235 13.4242 10.1711 12.3797H10.1717ZM3.9375 11.8431C3.90769 13.1914 5.44275 13.2915 5.67956 13.2988C6.46256 13.3224 7.09425 13.0288 7.58644 12.7386C8.07806 12.4483 8.88075 11.6979 8.91113 11.6726C8.94037 11.6467 8.97019 11.6192 8.99944 11.5899C8.78456 11.4718 8.57362 11.3638 8.36662 11.2642C8.15962 11.1652 7.10663 10.6033 6.17287 10.5077C4.40325 10.3254 3.9465 11.4336 3.9375 11.8431Z" fill="#1777FF"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,8 @@
<svg width="19" height="19" viewBox="0 0 19 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="icon/solid/wechat">
<g id="Union">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.1409 7.49005C12.8586 7.45189 12.5687 7.429 12.2711 7.429C9.02813 7.429 6.39563 9.6342 6.40326 12.3354C6.40326 12.6559 6.44141 12.9611 6.51008 13.2587C6.00648 13.19 5.53339 13.0603 5.08319 12.9L2.97719 13.8081L3.74787 12.2591C2.38202 11.3587 1.50452 9.97757 1.50452 8.41333C1.50452 5.70452 4.13702 3.50696 7.37995 3.50696C10.2414 3.50696 12.6221 5.22381 13.1409 7.49005ZM9.61367 5.76791C9.5248 5.7311 9.42954 5.71215 9.33335 5.71215C9.13907 5.71215 8.95275 5.78933 8.81538 5.92671C8.678 6.06408 8.60082 6.2504 8.60082 6.44468C8.60082 6.63895 8.678 6.82527 8.81538 6.96265C8.95275 7.10002 9.13907 7.1772 9.33335 7.1772C9.42954 7.1772 9.5248 7.15825 9.61367 7.12144C9.70255 7.08463 9.7833 7.03067 9.85132 6.96265C9.91934 6.89463 9.9733 6.81387 10.0101 6.725C10.0469 6.63613 10.0659 6.54087 10.0659 6.44468C10.0659 6.34848 10.0469 6.25323 10.0101 6.16435C9.9733 6.07548 9.91934 5.99473 9.85132 5.92671C9.7833 5.85868 9.70255 5.80473 9.61367 5.76791ZM4.90096 6.97028C5.03833 7.10765 5.22465 7.18483 5.41893 7.18483C5.61321 7.18483 5.79953 7.10765 5.9369 6.97028C6.07428 6.8329 6.15145 6.64658 6.15145 6.45231C6.15145 6.25803 6.07428 6.07171 5.9369 5.93434C5.79953 5.79696 5.61321 5.71979 5.41893 5.71979C5.22465 5.71979 5.03833 5.79696 4.90096 5.93434C4.76359 6.07171 4.68641 6.25803 4.68641 6.45231C4.68641 6.64658 4.76359 6.8329 4.90096 6.97028Z" fill="#1AAD1A"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.271 8.41331C14.9722 8.41331 17.1621 10.1683 17.1621 12.3354C17.1621 13.5944 16.4143 14.6932 15.2697 15.4104L16.1854 17.2417L13.5453 16.1048C13.1409 16.1964 12.7135 16.2574 12.271 16.2574C9.56981 16.2574 7.37987 14.5024 7.37987 12.3354C7.37987 10.1683 9.56981 8.41331 12.271 8.41331ZM10.3913 11.9677C10.5118 12.0482 10.6534 12.0912 10.7983 12.0912C11.2027 12.0912 11.5385 11.7631 11.5308 11.3587C11.5308 11.2138 11.4879 11.0722 11.4074 10.9517C11.3269 10.8312 11.2125 10.7373 11.0786 10.6819C10.9448 10.6265 10.7975 10.6119 10.6554 10.6402C10.5133 10.6685 10.3828 10.7382 10.2803 10.8407C10.1779 10.9431 10.1081 11.0737 10.0799 11.2158C10.0516 11.3578 10.0661 11.5051 10.1215 11.639C10.177 11.7728 10.2709 11.8872 10.3913 11.9677ZM13.2181 11.8766C13.3554 12.014 13.5417 12.0912 13.736 12.0912C13.9303 12.0912 14.1166 12.014 14.254 11.8766C14.3914 11.7393 14.4685 11.5529 14.4685 11.3587C14.4685 11.1644 14.3914 10.9781 14.254 10.8407C14.1166 10.7033 13.9303 10.6261 13.736 10.6261C13.5417 10.6261 13.3554 10.7033 13.2181 10.8407C13.0807 10.9781 13.0035 11.1644 13.0035 11.3587C13.0035 11.5529 13.0807 11.7393 13.2181 11.8766Z" fill="#1AAD1A"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -0,0 +1,9 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="icon/line/coupon_codes">
<g id="Union">
<path d="M7.33334 6.66667C6.96515 6.66667 6.66668 6.96514 6.66668 7.33333V8.5C6.66668 8.86819 6.96515 9.16667 7.33334 9.16667H7.66668C8.03487 9.16667 8.33334 8.86819 8.33334 8.5V7.33333C8.33334 6.96514 8.03487 6.66667 7.66668 6.66667H7.33334Z" fill="#3370FF"/>
<path d="M6.66668 11.5C6.66668 11.1318 6.96515 10.8333 7.33334 10.8333H7.66668C8.03487 10.8333 8.33334 11.1318 8.33334 11.5V12.6667C8.33334 13.0349 8.03487 13.3333 7.66668 13.3333H7.33334C6.96515 13.3333 6.66668 13.0349 6.66668 12.6667V11.5Z" fill="#3370FF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.50001 17.5C1.57954 17.5 0.833344 16.7538 0.833344 15.8333V13.8664C0.833344 13.5644 0.833344 13.4134 0.85697 13.2895C0.877583 13.1815 0.879995 13.1518 0.888963 13.1242C0.897931 13.0966 0.913455 13.0711 0.960293 12.9716C1.01398 12.8576 1.35054 12.3945 2.02366 11.4682C2.32326 11.056 2.50001 10.5486 2.50001 10C2.50001 9.4514 2.32331 8.94407 2.02371 8.53183C1.35057 7.60558 1.01398 7.14243 0.960293 7.02837C0.913455 6.92886 0.897931 6.9034 0.888963 6.8758C0.879995 6.84821 0.877583 6.81849 0.85697 6.71045C0.833344 6.58662 0.833344 6.43562 0.833344 6.13361V4.16667C0.833344 3.24619 1.57954 2.5 2.50001 2.5H17.5C18.4205 2.5 19.1667 3.24619 19.1667 4.16667V6.13361C19.1667 6.43562 19.1667 6.58662 19.143 6.71045C19.1224 6.81849 19.12 6.84821 19.1111 6.8758C19.1021 6.9034 19.0866 6.92886 19.0397 7.02837C18.986 7.14243 18.6495 7.60557 17.9763 8.53183C17.6767 8.94407 17.5 9.4514 17.5 10C17.5 10.5825 17.6992 11.1184 18.0332 11.5434C18.6624 12.3441 18.9769 12.7444 19.0333 12.8564C19.0796 12.9485 19.0965 12.9764 19.1069 13.0065C19.1174 13.0367 19.1213 13.0691 19.1418 13.1702C19.1667 13.293 19.1667 13.4527 19.1667 13.7721V15.8333C19.1667 16.7538 18.4205 17.5 17.5 17.5H2.50001ZM2.52704 6.25159C2.50001 6.1194 2.50001 5.94661 2.50001 5.60102V5.5C2.50001 5.03329 2.50001 4.79993 2.59084 4.62167C2.67073 4.46487 2.79822 4.33739 2.95502 4.25749C3.13328 4.16667 3.36663 4.16667 3.83334 4.16667H6.66668V4.33333C6.66668 4.70152 6.96515 5 7.33334 5H7.66668C8.03487 5 8.33334 4.70152 8.33334 4.33333V4.16667H16.1667C16.6334 4.16667 16.8667 4.16667 17.045 4.25749C17.2018 4.33739 17.3293 4.46487 17.4092 4.62167C17.5 4.79993 17.5 5.03329 17.5 5.5V5.60102C17.5 5.94661 17.5 6.1194 17.473 6.25159C17.4399 6.41345 17.43 6.44182 17.3551 6.58907C17.2939 6.70932 17.1085 6.94215 16.7379 7.40781C16.172 8.11871 15.8333 9.01919 15.8333 10C15.8333 11.0048 16.1887 11.9253 16.7798 12.644C17.1251 13.0639 17.2977 13.2738 17.3577 13.3886C17.4295 13.5258 17.4417 13.5599 17.4735 13.7115C17.5 13.8383 17.5 14.003 17.5 14.3324V14.5C17.5 14.9667 17.5 15.2001 17.4092 15.3783C17.3293 15.5351 17.2018 15.6626 17.045 15.7425C16.8667 15.8333 16.6334 15.8333 16.1667 15.8333H8.33334V15.6667C8.33334 15.2985 8.03487 15 7.66668 15H7.33334C6.96515 15 6.66668 15.2985 6.66668 15.6667V15.8333H3.83334C3.36663 15.8333 3.13328 15.8333 2.95502 15.7425C2.79822 15.6626 2.67073 15.5351 2.59084 15.3783C2.50001 15.2001 2.50001 14.9667 2.50001 14.5V14.399C2.50001 14.0534 2.50001 13.8806 2.52704 13.7484C2.56014 13.5865 2.57005 13.5582 2.64496 13.4109C2.70614 13.2907 2.89148 13.0578 3.26216 12.5922C3.82806 11.8813 4.16668 10.9808 4.16668 10C4.16668 9.01919 3.82806 8.11871 3.26216 7.40781C2.89148 6.94215 2.70614 6.70932 2.64496 6.58907C2.57005 6.44182 2.56014 6.41345 2.52704 6.25159Z" fill="#3370FF"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -65,7 +65,6 @@ export function useLinkedScroll<
let scroolSign = useRef(false);
const { runAsync: loadInitData } = useRequest2(
async ({ scrollWhenFinish, refresh } = { scrollWhenFinish: true, refresh: false }) => {
console.log('loadInitData', params);
if (!currentData || isLoading) return;
const item = dataList.find((item) => item._id === currentData.id);

View File

@@ -11,6 +11,7 @@
"confirm": "confirm",
"contact_phone": "Contact phone number",
"contact_phone_void": "Contact phone number format error",
"day": "sky",
"default_header": "Default header",
"detail": "Details",
"email_address": "Email address",
@@ -22,6 +23,7 @@
"invoice_detail": "Invoice details",
"invoice_sending_info": "The invoice will be sent to your mailbox within 3-7 working days, please be patient.",
"mm": "mm",
"month": "moon",
"need_special_invoice": "Do you need a special ticket?",
"no": "no",
"no_invoice_record": "No bill record~",
@@ -30,14 +32,16 @@
"order_type": "Order type",
"organization_name": "Organization name",
"payment_method": "Payment method",
"payway_coupon": "Redeem code",
"save": "save",
"save_failed": "Save exception",
"save_success": "Saved successfully",
"status": "state",
"sub_mode_custom": "Customize",
"submit_failed": "Submission failed",
"submit_success": "Submission successful",
"submitted": "Submitted",
"subscription_mode_month": "by month",
"subscription_mode_month": "Duration",
"subscription_package": "Subscription package",
"subscription_period": "Subscription cycle",
"support_wallet_amount": "Amount",

View File

@@ -74,5 +74,6 @@
"user_team_team_name": "Team",
"verification_code": "Verification code",
"you_can_convert": "you can redeem",
"yuan": "Yuan"
"yuan": "Yuan",
"redeem_coupon": "Redeem coupon"
}

View File

@@ -59,6 +59,7 @@
"log_join_team": "【{{name}}】Join the team through the invitation link 【{{link}}】",
"log_kick_out_team": "{{name}} removed member {{memberName}}",
"log_login": "【{{name}}】Logined in the system",
"log_recover_team_member": "【{{name}}】Restored member【{{memberName}}】",
"log_relocate_department": "【{{name}}】Displayed department【{{departmentName}}】",
"log_time": "Operation time",
"log_type": "Operation Type",
@@ -83,6 +84,7 @@
"permission_datasetCreate_Tip": "Can create knowledge bases in the root directory (creation permissions in folders are controlled by the folder)",
"permission_manage": "Admin",
"permission_manage_tip": "Can manage members, create groups, manage all groups, and assign permissions to groups and members",
"recover_team_member": "Member Recovery",
"relocate_department": "Department Mobile",
"remark": "remark",
"remove_tip": "Confirm to remove {{username}} from the team?",

View File

@@ -63,7 +63,10 @@
"response.child total points": "Sub-workflow point consumption",
"response.dataset_concat_length": "Combined total",
"response.node_inputs": "Node Inputs",
"response_embedding_model": "Vector model",
"response_embedding_model_tokens": "Vector Model Tokens",
"response_hybrid_weight": "Embedding : Full text = {{emb}} : {{text}}",
"response_rerank_tokens": "Rearrange Model Tokens",
"select": "Select",
"select_file": "Upload File",
"select_file_img": "Upload file / image",

View File

@@ -36,12 +36,18 @@
"Warning": "Warning",
"add_new": "Add New",
"add_new_param": "Add new param",
"all_quotes": "Quote all",
"app.templateMarket.templateTags.Image_generation": "Image generation",
"app.templateMarket.templateTags.Office_services": "Office Services",
"app.templateMarket.templateTags.Roleplay": "role play",
"app.templateMarket.templateTags.Web_search": "Search online",
"app.templateMarket.templateTags.Writing": "Writing",
"back": "Back",
"bill_already_processed": "Order has been processed",
"bill_expired": "Order expired",
"bill_not_pay_processed": "Non-online orders",
"button.extra_dataset_size_tip": "You are purchasing [Extra Knowledge Base Capacity]",
"button.extra_points_tip": "You are purchasing [Extra AI Points]",
"can_copy_content_tip": "It is not possible to copy automatically using the browser, please manually copy the following content",
"chose_condition": "Choose Condition",
"chosen": "Chosen",
@@ -977,14 +983,22 @@
"no": "No",
"no_laf_env": "System Not Configured with Laf Environment",
"not_model_config": "No related model configured",
"not_permission": "The current subscription package does not support team operation logs",
"not_yet_introduced": "No Introduction Yet",
"option": "Option",
"pay.amount": "Amount",
"pay.error_desc": "There was a problem when converting payment routes",
"pay.noclose": "After payment is completed, please wait for the system to update automatically",
"pay.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.",
"pay.package_tip.renewal": "You are renewing the package. You can view the package usage in Account - Personal Information - Package Details.",
"pay.package_tip.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.",
"pay.wechat": "Please scan the QR code on WeChat to pay: {{price}} yuan\n\nPlease do not close the page before payment is completed",
"pay.wx_payment": "WeChat Payment",
"pay.yuan": "{{amount}} Yuan",
"pay_alipay_payment": "Alipay Payment",
"pay_corporate_payment": "Payment to the public",
"pay_money": "Amount payable",
"pay_success": "Payment successfully",
"permission.Collaborator": "Collaborator",
"permission.Default permission": "Default Permission",
"permission.Manage": "Manage",
@@ -1038,6 +1052,7 @@
"required": "Required",
"rerank_weight": "Rearrange weights",
"resume_failed": "Resume Failed",
"scan_code": "Scan the QR code to pay",
"select_reference_variable": "Select Reference Variable",
"share_link": "Share Link",
"support.account.Individuation": "Personalization",
@@ -1120,7 +1135,9 @@
"support.wallet.bill.Status": "Status",
"support.wallet.bill.Type": "Order Type",
"support.wallet.bill.payWay.Way": "Payment Method",
"support.wallet.bill.payWay.alipay": "Alipay Payment",
"support.wallet.bill.payWay.balance": "Balance Payment",
"support.wallet.bill.payWay.bank": "Bank Transfer",
"support.wallet.bill.payWay.wx": "WeChat Payment",
"support.wallet.bill.status.closed": "Closed",
"support.wallet.bill.status.notpay": "Unpaid",
@@ -1181,7 +1198,6 @@
"support.wallet.subscription.mode.Year": "Yearly",
"support.wallet.subscription.mode.Year sale": "Two Months Free",
"support.wallet.subscription.point": "Points",
"support.wallet.subscription.rerank": "Result Re-rank",
"support.wallet.subscription.standardSubLevel.custom": "Custom",
"support.wallet.subscription.standardSubLevel.enterprise": "Enterprise",
"support.wallet.subscription.standardSubLevel.enterprise_desc": "Suitable for small and medium-sized enterprises to build Dataset applications in production environments",
@@ -1194,6 +1210,7 @@
"support.wallet.subscription.status.active": "Active",
"support.wallet.subscription.status.expired": "Expired",
"support.wallet.subscription.status.inactive": "Inactive",
"support.wallet.subscription.team_operation_log": "Record team operation logs",
"support.wallet.subscription.token_compute": "Click to View Online Tokens Calculator",
"support.wallet.subscription.type.balance": "Balance Recharge",
"support.wallet.subscription.type.extraDatasetSize": "Dataset Expansion",

View File

@@ -2,7 +2,6 @@
"app_alias_name": "Tool name",
"app_description": "Application Description",
"app_name": "Application name",
"app_tool_name": "Tool alias",
"apps": "Exposed applications",
"create_mcp": "Create an MCP service",
"create_mcp_server": "Create a new service",
@@ -19,5 +18,8 @@
"search_app": "Search for apps",
"select_app": "Application selection",
"start_use": "Get started",
"tool_name": "Tool name",
"tool_name_placeholder": "It is recommended to use an English name",
"tool_name_tip": "Some Clients only support English, and you can modify this value to the tool name in English.",
"usage_way": "MCP service usage"
}

View File

@@ -15,6 +15,7 @@
"assigned_reply": "Assigned Reply",
"auth_tmb_id": "Auth member",
"auth_tmb_id_tip": "After it is turned on, when the application is released to the outside world, the knowledge base will be filtered based on whether the user has permission to the knowledge base.\n\nIf it is not enabled, the configured knowledge base will be searched directly without permission filtering.",
"auto_align": "Automatic alignment",
"can_not_loop": "This node can't loop.",
"choose_another_application_to_call": "Select another application to call",
"classification_result": "Classification Result",

View File

@@ -11,6 +11,7 @@
"confirm": "确认",
"contact_phone": "联系电话",
"contact_phone_void": "联系电话格式错误",
"day": "天",
"default_header": "默认抬头",
"detail": "详情",
"email_address": "邮箱地址",
@@ -22,6 +23,7 @@
"invoice_detail": "发票详情",
"invoice_sending_info": "发票将在 3-7 个工作日内发送至邮箱,请耐心等待",
"mm": "毫米",
"month": "月",
"need_special_invoice": "是否需要专票",
"no": "否",
"no_invoice_record": "无账单记录~",
@@ -30,14 +32,16 @@
"order_type": "订单类型",
"organization_name": "组织名称",
"payment_method": "支付方式",
"payway_coupon": "兑换码",
"save": "保存",
"save_failed": "保存异常",
"save_success": "保存成功",
"status": "状态",
"sub_mode_custom": "自定义",
"submit_failed": "提交失败",
"submit_success": "提交成功",
"submitted": "已提交",
"subscription_mode_month": "按月",
"subscription_mode_month": "时长",
"subscription_package": "订阅套餐",
"subscription_period": "订阅周期",
"support_wallet_amount": "金额",

View File

@@ -73,5 +73,6 @@
"user_team_team_name": "团队",
"verification_code": "验证码",
"you_can_convert": "您可以兑换",
"yuan": "元"
}
"yuan": "元",
"redeem_coupon": "兑换码"
}

View File

@@ -53,7 +53,20 @@
"label_sync": "标签同步",
"leave": "已离职",
"leave_team_failed": "离开团队异常",
"log_assign_permission": "【{{name}}】更新了【{{objectName}}】的权限:[应用创建:【{{appCreate}}】, 知识库:【{{datasetCreate}}】, API密钥:【{{apiKeyCreate}}】, 管理:【{{manage}}】]",
"log_change_department": "【{{name}}】更新了部门【{{departmentName}}】",
"log_change_member_name": "【{{name}}】将成员【{{memberName}}】重命名为【{{newName}}】",
"log_create_department": "【{{name}}】创建了部门【{{departmentName}}】",
"log_create_group": "【{{name}}】创建了群组【{{groupName}}】",
"log_create_invitation_link": "【{{name}}】创建了邀请链接【{{link}}】",
"log_delete_department": "【{{name}}】删除了部门【{{departmentName}}】",
"log_delete_group": "【{{name}}】删除了群组【{{groupName}}】",
"log_details": "详情",
"log_join_team": "【{{name}}】通过邀请链接【{{link}}】加入团队",
"log_kick_out_team": "【{{name}}】移除了成员【{{memberName}}】",
"log_login": "【{{name}}】登录了系统",
"log_recover_team_member": "【{{name}}】恢复了成员【{{memberName}}】",
"log_relocate_department": "【{{name}}】移动了部门【{{departmentName}}】",
"log_time": "操作时间",
"log_type": "操作类型",
"log_user": "操作人员",
@@ -70,7 +83,16 @@
"org_name": "部门名称",
"owner": "所有者",
"permission": "权限",
"permission_apikeyCreate": "创建 API 密钥",
"permission_apikeyCreate_Tip": "可以创建全局的 APIKey",
"permission_appCreate": "创建应用",
"permission_appCreate_tip": "可以在根目录创建应用,(文件夹下的创建权限由文件夹控制)",
"permission_datasetCreate": "创建知识库",
"permission_datasetCreate_Tip": "可以在根目录创建知识库,(文件夹下的创建权限由文件夹控制)",
"permission_manage": "管理员",
"permission_manage_tip": "可以管理成员、创建群组、管理所有群组、为群组和成员分配权限",
"please_bind_contact": "请绑定联系方式",
"recover_team_member": "成员恢复",
"relocate_department": "部门移动",
"remark": "备注",
"remove_tip": "确认将 {{username}} 移出团队?成员将被标记为“已离职”,不删除操作数据,账号下资源自动转让给团队所有者。",
@@ -93,25 +115,5 @@
"user_team_invite_member": "邀请成员",
"user_team_leave_team": "离开团队",
"user_team_leave_team_failed": "离开团队失败",
"waiting": "待接受",
"permission_appCreate": "创建应用",
"permission_datasetCreate": "创建知识库",
"permission_apikeyCreate": "创建 API 密钥",
"permission_appCreate_tip": "可以在根目录创建应用,(文件夹下的创建权限由文件夹控制)",
"permission_datasetCreate_Tip": "可以在根目录创建知识库,(文件夹下的创建权限由文件夹控制)",
"permission_apikeyCreate_Tip": "可以创建全局的 APIKey",
"permission_manage": "管理员",
"permission_manage_tip": "可以管理成员、创建群组、管理所有群组、为群组和成员分配权限",
"log_login": "【{{name}}】登录了系统",
"log_create_invitation_link": "【{{name}}】创建了邀请链接【{{link}}】",
"log_join_team": "【{{name}}】通过邀请链接【{{link}}】加入团队",
"log_change_member_name": "【{{name}}】将成员【{{memberName}}】重命名为【{{newName}}】",
"log_kick_out_team": "【{{name}}】移除了成员【{{memberName}}】",
"log_create_department": "【{{name}}】创建了部门【{{departmentName}}】",
"log_change_department": "【{{name}}】更新了部门【{{departmentName}}】",
"log_delete_department": "【{{name}}】删除了部门【{{departmentName}}】",
"log_relocate_department": "【{{name}}】移动了部门【{{departmentName}}】",
"log_create_group": "【{{name}}】创建了群组【{{groupName}}】",
"log_delete_group": "【{{name}}】删除了群组【{{groupName}}】",
"log_assign_permission": "【{{name}}】更新了【{{objectName}}】的权限:[应用创建:【{{appCreate}}】, 知识库:【{{datasetCreate}}】, API密钥:【{{apiKeyCreate}}】, 管理:【{{manage}}】]"
"waiting": "待接受"
}

View File

@@ -63,7 +63,10 @@
"response.child total points": "子工作流积分消耗",
"response.dataset_concat_length": "合并后总数",
"response.node_inputs": "节点输入",
"response_embedding_model": "向量模型",
"response_embedding_model_tokens": "向量模型 Tokens",
"response_hybrid_weight": "语义检索 : 全文检索 = {{emb}} : {{text}}",
"response_rerank_tokens": "重排模型 Tokens",
"select": "选择",
"select_file": "上传文件",
"select_file_img": "上传文件/图片",

View File

@@ -36,12 +36,18 @@
"Warning": "提示",
"add_new": "新增",
"add_new_param": "新增参数",
"all_quotes": "全部引用",
"app.templateMarket.templateTags.Image_generation": "图片生成",
"app.templateMarket.templateTags.Office_services": "办公服务",
"app.templateMarket.templateTags.Roleplay": "角色扮演",
"app.templateMarket.templateTags.Web_search": "联网搜索",
"app.templateMarket.templateTags.Writing": "文本创作",
"back": "返回",
"bill_already_processed": "订单已处理",
"bill_expired": "订单已过期",
"bill_not_pay_processed": "非在线订单",
"button.extra_dataset_size_tip": "您正在购买【额外知识库容量】",
"button.extra_points_tip": "您正在购买【额外 AI 积分】",
"can_copy_content_tip": "无法使用浏览器自动复制,请手动复制下面内容",
"chose_condition": "选择条件",
"chosen": "已选",
@@ -976,14 +982,22 @@
"no": "否",
"no_laf_env": "系统未配置Laf环境",
"not_model_config": "未配置相关模型",
"not_permission": "当前订阅套餐不支持团队操作日志",
"not_yet_introduced": "暂无介绍",
"option": "选项",
"pay.amount": "金额",
"pay.error_desc": "转换支付途径时出现了问题",
"pay.noclose": "支付完成后,请等待系统自动更新",
"pay.package_tip.buy": "您购买的套餐等级低于当前套餐,该套餐将在当前套餐过期后生效。\n您可在账号—个人信息—套餐详情里查看套餐使用情况。",
"pay.package_tip.renewal": "您正在续费套餐。您可在账号—个人信息—套餐详情里,查看套餐使用情况。",
"pay.package_tip.upgrade": "您购买的套餐等级高于当前套餐,该套餐将即刻生效,当前套餐将延后生效。您可在账号—个人信息—套餐详情里,查看套餐使用情况。",
"pay.wechat": "请微信扫码支付: {{price}}元\n支付完成前请勿关闭页面",
"pay.wx_payment": "微信支付",
"pay.yuan": "{{amount}}元",
"pay_alipay_payment": "支付宝支付",
"pay_corporate_payment": "对公支付",
"pay_money": "应付金额",
"pay_success": "支付成功",
"permission.Collaborator": "协作者",
"permission.Default permission": "默认权限",
"permission.Manage": "管理",
@@ -1037,6 +1051,7 @@
"required": "必须",
"rerank_weight": "重排权重",
"resume_failed": "恢复失败",
"scan_code": "扫码支付",
"select_reference_variable": "选择引用变量",
"share_link": "分享链接",
"support.account.Individuation": "个性化",
@@ -1119,7 +1134,9 @@
"support.wallet.bill.Status": "状态",
"support.wallet.bill.Type": "订单类型",
"support.wallet.bill.payWay.Way": "支付方式",
"support.wallet.bill.payWay.alipay": "支付宝支付",
"support.wallet.bill.payWay.balance": "余额支付",
"support.wallet.bill.payWay.bank": "对公支付",
"support.wallet.bill.payWay.wx": "微信支付",
"support.wallet.bill.status.closed": "已关闭",
"support.wallet.bill.status.notpay": "未支付",
@@ -1180,7 +1197,6 @@
"support.wallet.subscription.mode.Year": "按年",
"support.wallet.subscription.mode.Year sale": "赠送两个月",
"support.wallet.subscription.point": "积分",
"support.wallet.subscription.rerank": "检索结果重排",
"support.wallet.subscription.standardSubLevel.custom": "自定义版",
"support.wallet.subscription.standardSubLevel.enterprise": "企业版",
"support.wallet.subscription.standardSubLevel.enterprise_desc": "适合中小企业在生产环境构建知识库应用",
@@ -1193,6 +1209,7 @@
"support.wallet.subscription.status.active": "生效中",
"support.wallet.subscription.status.expired": "已过期",
"support.wallet.subscription.status.inactive": "待使用",
"support.wallet.subscription.team_operation_log": "记录团队操作日志",
"support.wallet.subscription.token_compute": "点击查看在线 Tokens 计算器",
"support.wallet.subscription.type.balance": "余额充值",
"support.wallet.subscription.type.extraDatasetSize": "知识库扩容",

View File

@@ -2,7 +2,6 @@
"app_alias_name": "工具名",
"app_description": "应用描述",
"app_name": "应用名",
"app_tool_name": "工具别名",
"apps": "暴露的应用",
"create_mcp": "创建 MCP 服务",
"create_mcp_server": "新建服务",
@@ -19,5 +18,8 @@
"search_app": "搜索应用",
"select_app": "应用选择",
"start_use": "开始使用",
"tool_name": "工具名",
"tool_name_placeholder": "建议使用英文名",
"tool_name_tip": "部分 Client 仅支持英文,可以将该值修改成英文的工具名",
"usage_way": "MCP 服务使用"
}

View File

@@ -15,6 +15,7 @@
"assigned_reply": "指定回复",
"auth_tmb_id": "使用者鉴权",
"auth_tmb_id_tip": "开启后,对外发布该应用时,还会根据用户是否有该知识库权限进行知识库过滤。\n若未开启则直接按配置的知识库进行检索不进行权限过滤。",
"auto_align": "自动对齐",
"can_not_loop": "该节点不支持循环嵌套",
"choose_another_application_to_call": "选择一个其他应用进行调用",
"classification_result": "分类结果",

View File

@@ -11,6 +11,7 @@
"confirm": "確認",
"contact_phone": "聯絡電話",
"contact_phone_void": "聯絡電話格式錯誤",
"day": "天",
"default_header": "預設抬頭",
"detail": "詳細資訊",
"email_address": "郵件地址",
@@ -22,6 +23,7 @@
"invoice_detail": "發票詳細資訊",
"invoice_sending_info": "發票將在 3-7 個工作天內傳送至郵箱,請耐心等待",
"mm": "毫米",
"month": "月",
"need_special_invoice": "是否需要專票",
"no": "否",
"no_invoice_record": "無帳單記錄~",
@@ -30,14 +32,16 @@
"order_type": "訂單類型",
"organization_name": "組織名稱",
"payment_method": "支付方式",
"payway_coupon": "兌換碼",
"save": "儲存",
"save_failed": "儲存異常",
"save_success": "儲存成功",
"status": "狀態",
"sub_mode_custom": "自定義",
"submit_failed": "提交失敗",
"submit_success": "提交成功",
"submitted": "已提交",
"subscription_mode_month": "按月",
"subscription_mode_month": "時長",
"subscription_package": "訂閱套餐",
"subscription_period": "訂閱週期",
"support_wallet_amount": "金額",

View File

@@ -74,5 +74,6 @@
"user_team_team_name": "團隊",
"verification_code": "驗證碼",
"you_can_convert": "您可以兌換",
"yuan": "元"
}
"yuan": "元",
"redeem_coupon": "兌換代碼"
}

View File

@@ -59,6 +59,7 @@
"log_join_team": "【{{name}}】通過邀請鏈接【{{link}}】加入團隊",
"log_kick_out_team": "{{name}} 移除了成員 {{memberName}}",
"log_login": "【{{name}}】登錄了系統",
"log_recover_team_member": "【{{name}}】恢復了成員【{{memberName}}】",
"log_relocate_department": "【{{name}}】移動了部門【{{departmentName}}】",
"log_time": "操作時間",
"log_type": "操作類型",
@@ -83,6 +84,7 @@
"permission_datasetCreate_Tip": "可以在根目錄建立知識庫,(資料夾下的建立權限由資料夾控制)",
"permission_manage": "管理員",
"permission_manage_tip": "可以管理成員、建立群組、管理所有群組、為群組和成員分配權限",
"recover_team_member": "成員恢復",
"relocate_department": "部門移動",
"remark": "備註",
"remove_tip": "確認將 {{username}} 移出團隊?",

View File

@@ -61,7 +61,10 @@
"response.child total points": "子工作流程點數消耗",
"response.dataset_concat_length": "合併總數",
"response.node_inputs": "節點輸入",
"response_embedding_model": "向量模型",
"response_embedding_model_tokens": "向量模型 Tokens",
"response_hybrid_weight": "語義檢索 : 全文檢索 = {{emb}} : {{text}}",
"response_rerank_tokens": "重排模型 Tokens",
"select": "選取",
"select_file": "上傳檔案",
"select_file_img": "上傳檔案 / 圖片",

View File

@@ -36,12 +36,18 @@
"Warning": "警告",
"add_new": "新增",
"add_new_param": "新增參數",
"all_quotes": "全部引用",
"app.templateMarket.templateTags.Image_generation": "圖片生成",
"app.templateMarket.templateTags.Office_services": "辦公服務",
"app.templateMarket.templateTags.Roleplay": "角色扮演",
"app.templateMarket.templateTags.Web_search": "聯網搜索",
"app.templateMarket.templateTags.Writing": "文字創作",
"back": "返回",
"bill_already_processed": "訂單已處理",
"bill_expired": "訂單已過期",
"bill_not_pay_processed": "非在線訂單",
"button.extra_dataset_size_tip": "您正在購買【額外知識庫容量】",
"button.extra_points_tip": "您正在購買【額外 AI 積分】",
"can_copy_content_tip": "無法使用瀏覽器自動複製,請手動複製下面內容",
"chose_condition": "選擇條件",
"chosen": "已選擇",
@@ -976,14 +982,22 @@
"no": "否",
"no_laf_env": "系統未設定 LAF 環境",
"not_model_config": "未設定相關模型",
"not_permission": "當前訂閱套餐不支持團隊操作日誌",
"not_yet_introduced": "暫無介紹",
"option": "選項",
"pay.amount": "金額",
"pay.error_desc": "轉換支付途徑時出現了問題",
"pay.noclose": "支付完成後,請等待系統自動更新",
"pay.package_tip.buy": "您購買的方案等級低於目前方案,該方案將在目前方案過期後生效。\n您可在帳戶 - 個人資訊 - 方案詳細資訊中檢視方案使用情況。",
"pay.package_tip.renewal": "您正在續約方案。您可在帳戶 - 個人資訊 - 方案詳細資訊中檢視方案使用情況。",
"pay.package_tip.upgrade": "您購買的方案等級高於目前方案,該方案將立即生效,目前方案將延後生效。您可在帳戶 - 個人資訊 - 方案詳細資訊中檢視方案使用情況。",
"pay.wechat": "請微信掃碼付款:{{price}}元\n\n付款完成前請勿關閉頁面",
"pay.wx_payment": "微信支付",
"pay.yuan": "{{amount}} 元",
"pay_alipay_payment": "支付寶支付",
"pay_corporate_payment": "對公支付",
"pay_money": "應付金額",
"pay_success": "支付成功",
"permission.Collaborator": "協作者",
"permission.Default permission": "預設權限",
"permission.Manage": "管理",
@@ -1037,6 +1051,7 @@
"required": "必填",
"rerank_weight": "重排權重",
"resume_failed": "恢復失敗",
"scan_code": "掃碼支付",
"select_reference_variable": "選擇引用變數",
"share_link": "分享連結",
"support.account.Individuation": "個人化",
@@ -1119,7 +1134,9 @@
"support.wallet.bill.Status": "狀態",
"support.wallet.bill.Type": "訂單類型",
"support.wallet.bill.payWay.Way": "付款方式",
"support.wallet.bill.payWay.alipay": "支付寶支付",
"support.wallet.bill.payWay.balance": "餘額支付",
"support.wallet.bill.payWay.bank": "對公支付",
"support.wallet.bill.payWay.wx": "微信支付",
"support.wallet.bill.status.closed": "已關閉",
"support.wallet.bill.status.notpay": "未付款",
@@ -1180,7 +1197,6 @@
"support.wallet.subscription.mode.Year": "按年",
"support.wallet.subscription.mode.Year sale": "贈送兩個月",
"support.wallet.subscription.point": "點數",
"support.wallet.subscription.rerank": "結果重新排名",
"support.wallet.subscription.standardSubLevel.custom": "客製版",
"support.wallet.subscription.standardSubLevel.enterprise": "企業版",
"support.wallet.subscription.standardSubLevel.enterprise_desc": "適合中小企業在正式環境建構知識庫應用",
@@ -1193,6 +1209,7 @@
"support.wallet.subscription.status.active": "使用中",
"support.wallet.subscription.status.expired": "已過期",
"support.wallet.subscription.status.inactive": "未使用",
"support.wallet.subscription.team_operation_log": "記錄團隊操作日誌",
"support.wallet.subscription.token_compute": "點選檢視線上 Token 計算機",
"support.wallet.subscription.type.balance": "餘額儲值",
"support.wallet.subscription.type.extraDatasetSize": "知識庫擴充容量",

View File

@@ -2,7 +2,6 @@
"app_alias_name": "工具名",
"app_description": "應用描述",
"app_name": "應用名",
"app_tool_name": "工具別名",
"apps": "暴露的應用",
"create_mcp": "創建 MCP 服務",
"create_mcp_server": "新建服務",
@@ -19,5 +18,8 @@
"search_app": "搜索應用",
"select_app": "應用選擇",
"start_use": "開始使用",
"tool_name": "工具名",
"tool_name_placeholder": "建議使用英文名",
"tool_name_tip": "部分 Client 僅支持英文,可以將該值修改成英文的工具名",
"usage_way": "MCP 服務使用"
}

View File

@@ -15,6 +15,7 @@
"assigned_reply": "指定回覆",
"auth_tmb_id": "使用者鑑權",
"auth_tmb_id_tip": "開啟後,對外發布應用程式時,也會根據使用者是否有該知識庫權限進行知識庫過濾。\n\n若未開啟則直接按設定的知識庫進行檢索不進行權限過濾。",
"auto_align": "自動對齊",
"can_not_loop": "這個節點不能迴圈。",
"choose_another_application_to_call": "選擇另一個應用程式來呼叫",
"classification_result": "分類結果",

46
pnpm-lock.yaml generated
View File

@@ -34,7 +34,7 @@ importers:
version: 10.1.4(socks@2.8.4)
next-i18next:
specifier: 15.4.2
version: 15.4.2(i18next@23.16.8)(next@14.2.26(@babel/core@7.26.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
version: 15.4.2(i18next@23.16.8)(next@14.2.26(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
prettier:
specifier: 3.2.4
version: 3.2.4
@@ -246,7 +246,7 @@ importers:
version: 3.13.0
next:
specifier: 14.2.26
version: 14.2.26(@babel/core@7.26.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1)
version: 14.2.26(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1)
nextjs-cors:
specifier: ^2.2.0
version: 2.2.0(next@14.2.26(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))
@@ -331,7 +331,7 @@ importers:
version: 2.1.1(@chakra-ui/system@2.6.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1))(react@18.3.1)
'@chakra-ui/next-js':
specifier: 2.4.2
version: 2.4.2(@chakra-ui/react@2.10.7(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(next@14.2.26(@babel/core@7.26.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react@18.3.1)
version: 2.4.2(@chakra-ui/react@2.10.7(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(next@14.2.26(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react@18.3.1)
'@chakra-ui/react':
specifier: 2.10.7
version: 2.10.7(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -394,7 +394,7 @@ importers:
version: 4.17.21
next-i18next:
specifier: 15.4.2
version: 15.4.2(i18next@23.16.8)(next@14.2.26(@babel/core@7.26.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
version: 15.4.2(i18next@23.16.8)(next@14.2.26(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
papaparse:
specifier: ^5.4.1
version: 5.4.1
@@ -465,6 +465,9 @@ importers:
'@chakra-ui/system':
specifier: 2.6.1
version: 2.6.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1)
'@dagrejs/dagre':
specifier: ^1.1.4
version: 1.1.4
'@emotion/react':
specifier: 11.11.1
version: 11.11.1(@types/react@18.3.1)(react@18.3.1)
@@ -1661,6 +1664,13 @@ packages:
'@dabh/diagnostics@2.0.3':
resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==}
'@dagrejs/dagre@1.1.4':
resolution: {integrity: sha512-QUTc54Cg/wvmlEUxB+uvoPVKFazM1H18kVHBQNmK2NbrDR5ihOCR6CXLnDSZzMcSQKJtabPUWridBOlJM3WkDg==}
'@dagrejs/graphlib@2.2.4':
resolution: {integrity: sha512-mepCf/e9+SKYy1d02/UkvSy6+6MoyXhVxP8lLDfA7BPE1X1d4dR0sZznmbM8/XVJ1GPM+Svnx7Xj6ZweByWUkw==}
engines: {node: '>17.0.0'}
'@emnapi/core@1.3.1':
resolution: {integrity: sha512-pVGjBIt1Y6gg3EJN8jTcfpP/+uuRksIo055oE/OBkDNcjZqVbfkWCksG1Jp4yZnj3iKWyWX8fdG/j6UDYPbFog==}
@@ -11010,6 +11020,14 @@ snapshots:
next: 14.2.26(@babel/core@7.26.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1)
react: 18.3.1
'@chakra-ui/next-js@2.4.2(@chakra-ui/react@2.10.7(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(next@14.2.26(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react@18.3.1)':
dependencies:
'@chakra-ui/react': 2.10.7(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@emotion/cache': 11.14.0
'@emotion/react': 11.11.1(@types/react@18.3.1)(react@18.3.1)
next: 14.2.26(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1)
react: 18.3.1
'@chakra-ui/object-utils@2.1.0': {}
'@chakra-ui/react-use-safe-layout-effect@2.1.0(react@18.3.1)':
@@ -11136,6 +11154,12 @@ snapshots:
enabled: 2.0.0
kuler: 2.0.0
'@dagrejs/dagre@1.1.4':
dependencies:
'@dagrejs/graphlib': 2.2.4
'@dagrejs/graphlib@2.2.4': {}
'@emnapi/core@1.3.1':
dependencies:
'@emnapi/wasi-threads': 1.0.1
@@ -18109,6 +18133,18 @@ snapshots:
react: 18.3.1
react-i18next: 14.1.2(i18next@23.16.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next-i18next@15.4.2(i18next@23.16.8)(next@14.2.26(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1):
dependencies:
'@babel/runtime': 7.26.10
'@types/hoist-non-react-statics': 3.3.6
core-js: 3.41.0
hoist-non-react-statics: 3.3.2
i18next: 23.16.8
i18next-fs-backend: 2.6.0
next: 14.2.26(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1)
react: 18.3.1
react-i18next: 14.1.2(i18next@23.16.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next@14.2.26(@babel/core@7.26.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1):
dependencies:
'@next/env': 14.2.26
@@ -18164,7 +18200,7 @@ snapshots:
nextjs-cors@2.2.0(next@14.2.26(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1)):
dependencies:
cors: 2.8.5
next: 14.2.26(@babel/core@7.26.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1)
next: 14.2.26(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1)
node-abi@3.74.0:
dependencies:

View File

@@ -62,6 +62,14 @@ WORKFLOW_MAX_LOOP_TIMES=50
# 启用内网 IP 检查
CHECK_INTERNAL_IP=false
# 特殊配置
# 自定义跨域,不配置时,默认都允许跨域(逗号分割)
ALLOWED_ORIGINS=
# 是否展示兑换码功能
SHOW_COUPON=false
# 自定义 config.json 路径
CONFIG_JSON_PATH=
# 对话日志推送服务
# # 日志服务地址
# CHAT_LOG_URL=http://localhost:8080
@@ -69,5 +77,4 @@ CHECK_INTERNAL_IP=false
# CHAT_LOG_INTERVAL=10000
# # 日志来源ID前缀
# CHAT_LOG_SOURCE_ID_PREFIX=fastgpt-
# 自定义跨域,不配置时,默认都允许跨域(逗号分割)
ALLOWED_ORIGINS=

View File

@@ -15,6 +15,7 @@
"@chakra-ui/react": "2.10.7",
"@chakra-ui/styled-system": "2.9.1",
"@chakra-ui/system": "2.6.1",
"@dagrejs/dagre": "^1.1.4",
"@emotion/react": "11.11.1",
"@emotion/styled": "11.11.0",
"@fastgpt/global": "workspace:*",

View File

@@ -0,0 +1,9 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="icon/line/wallet">
<g id="Vector">
<path d="M3.35205 3.84415C1.90167 4.09989 0.93322 5.48298 1.18896 6.93337L1.66743 9.6469V6.69182C1.66743 5.58725 2.56286 4.69182 3.66743 4.69182H6.22302L14.2787 3.27139C15.0039 3.14352 15.6954 3.62775 15.8233 4.35294L15.883 4.69182H16.3327C16.6749 4.69182 16.9971 4.7778 17.2788 4.92933L17.1364 4.12141C16.8806 2.67103 15.4975 1.70257 14.0471 1.95832L3.35205 3.84415Z" fill="#3370FF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.834106 6.69182C0.834106 5.12702 2.10263 3.85849 3.66744 3.85849H16.3327C17.8975 3.85849 19.166 5.12702 19.166 6.69182V15.2493C19.166 16.8141 17.8975 18.0827 16.3327 18.0827H3.66744C2.10263 18.0827 0.834106 16.8141 0.834106 15.2493V6.69182ZM3.66744 5.52516C3.02311 5.52516 2.50077 6.04749 2.50077 6.69182V15.2493C2.50077 15.8937 3.02311 16.416 3.66744 16.416H16.3327C16.977 16.416 17.4993 15.8937 17.4993 15.2493V6.69182C17.4993 6.04749 16.977 5.52516 16.3327 5.52516H3.66744Z" fill="#3370FF"/>
<path d="M15.7837 11.0202C15.7837 11.7539 15.1889 12.3487 14.4553 12.3487C13.7216 12.3487 13.1268 11.7539 13.1268 11.0202C13.1268 10.2866 13.7216 9.69182 14.4553 9.69182C15.1889 9.69182 15.7837 10.2866 15.7837 11.0202Z" fill="#3370FF"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -8,7 +8,8 @@ import {
PopoverBody,
PopoverArrow,
Box,
Flex
Flex,
useDisclosure
} from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
@@ -32,6 +33,8 @@ const A = ({ children, ...props }: any) => {
manual: true
});
const { isOpen, onOpen, onClose } = useDisclosure();
// empty href link
if (!props.href && typeof children?.[0] === 'string') {
const text = useMemo(() => String(children), [children]);
@@ -71,77 +74,95 @@ const A = ({ children, ...props }: any) => {
direction="rtl"
placement="bottom"
strategy={'fixed'}
onOpen={() => runAsync(String(children))}
isOpen={isOpen}
onClose={onClose}
onOpen={() => {
onOpen();
runAsync(String(children));
}}
gutter={4}
>
<PopoverTrigger>
<Button variant={'unstyled'} minH={0} minW={0} h={'auto'}>
<MyTooltip label={t('common:read_quote')}>
<Box
w={5}
h={5}
border={'1px solid'}
w={'14px'}
h={'14px'}
borderRadius={'full'}
borderColor={'myGray.200'}
color={'myGray.500'}
bg={'rgba(0, 0, 0, 0.08)'}
color={'myGray.600'}
fontSize={'10px'}
display={'flex'}
alignItems={'center'}
justifyContent={'center'}
ml={0.5}
transform={'translateY(-2px)'}
transform={'translateY(-3px)'}
>
{index}
</Box>
</MyTooltip>
</Button>
</PopoverTrigger>
<PopoverContent boxShadow={'lg'} w={'400px'} py={4}>
<MyBox isLoading={loading} minH={'224px'}>
<PopoverContent boxShadow={'lg'} w={'500px'} py={4}>
<MyBox isLoading={loading}>
<PopoverArrow />
<PopoverBody
px={4}
py={0}
fontSize={'sm'}
maxW={'400px'}
maxH={'224px'}
overflow={'auto'}
>
<Box
alignItems={'center'}
fontSize={'xs'}
border={'sm'}
borderRadius={'sm'}
overflow={'hidden'}
display={'inline-flex'}
height={6}
>
<Flex
color={'myGray.500'}
bg={'myGray.150'}
w={4}
justifyContent={'center'}
fontSize={'10px'}
h={'full'}
<PopoverBody py={0} px={0} fontSize={'sm'}>
<Flex px={4} pb={1} justifyContent={'space-between'}>
<Box
alignItems={'center'}
fontSize={'xs'}
border={'sm'}
borderRadius={'sm'}
overflow={'hidden'}
display={'inline-flex'}
height={6}
mr={1}
flexShrink={0}
>
{index}
</Flex>
<Flex px={1.5}>
<MyIcon name={icon as any} mr={1} flexShrink={0} w={'12px'} />
<Box
className={'textEllipsis'}
wordBreak={'break-all'}
flex={'1 0 0'}
fontSize={'mini'}
color={'myGray.900'}
<Flex
color={'myGray.500'}
bg={'myGray.150'}
w={4}
justifyContent={'center'}
fontSize={'10px'}
h={'full'}
alignItems={'center'}
mr={1}
flexShrink={0}
>
{sourceData.sourceName}
</Box>
</Flex>
</Box>
<Box>
{index}
</Flex>
<Flex px={1.5}>
<MyIcon name={icon as any} mr={1} flexShrink={0} w={'12px'} />
<Box
className={'textEllipsis'}
wordBreak={'break-all'}
flex={'1 0 0'}
fontSize={'mini'}
color={'myGray.900'}
>
{sourceData.sourceName}
</Box>
</Flex>
</Box>
<Button
variant={'ghost'}
color={'primary.600'}
size={'xs'}
onClick={() => {
onClose();
eventBus.emit(EventNameEnum.openQuoteReader, {
// quoteId: String(children),
sourceId: sourceData.sourceId,
sourceName: sourceData.sourceName,
datasetId: quoteData?.collection.datasetId,
collectionId: quoteData?.collection._id
});
}}
>
{t('common:all_quotes')}
</Button>
</Flex>
<Box h={'300px'} overflow={'auto'} px={4}>
<Markdown source={quoteData?.q} />
{quoteData?.a && <Markdown source={quoteData?.a} />}
</Box>

View File

@@ -94,6 +94,7 @@ const ModelTable = () => {
typeLabel: t('common:model.type.embedding'),
priceLabel: (
<Flex color={'myGray.700'}>
{`${t('common:common.Input')}: `}
<Box fontWeight={'bold'} color={'myGray.900'} mr={0.5}>
{item.charsPointsPrice || 0}
</Box>
@@ -131,7 +132,17 @@ const ModelTable = () => {
const formatRerankModelList = reRankModelList.map((item) => ({
...item,
typeLabel: t('common:model.type.reRank'),
priceLabel: <Flex color={'myGray.700'}>- </Flex>,
priceLabel: item.charsPointsPrice ? (
<Flex color={'myGray.700'}>
{`${t('common:common.Input')}: `}
<Box fontWeight={'bold'} color={'myGray.900'} mr={0.5}>
{item.charsPointsPrice}
</Box>
{` ${t('common:support.wallet.subscription.point')} / 1K Tokens`}
</Flex>
) : (
'-'
),
tagColor: 'red'
}));

View File

@@ -78,7 +78,7 @@ const DatasetParamsModal = ({
defaultValues: {
searchMode,
embeddingWeight: embeddingWeight || 0.5,
usingReRank: !!usingReRank && teamPlanStatus?.standardConstants?.permissionReRank !== false,
usingReRank: usingReRank || true,
rerankModel: rerankModel || defaultModels?.rerank?.model,
rerankWeight: rerankWeight || 0.5,
limit,
@@ -246,11 +246,6 @@ const DatasetParamsModal = ({
<Box color={'myGray.500'} fontSize={'sm'}>
{t('common:core.ai.Not deploy rerank model')}
</Box>
) : teamPlanStatus?.standardConstants &&
!teamPlanStatus?.standardConstants?.permissionReRank ? (
<Box color={'myGray.500'} fontSize={'sm'}>
{t('common:support.team.limit.No permission rerank')}
</Box>
) : (
<Switch {...register('usingReRank')} />
)}

View File

@@ -1,5 +1,5 @@
import { Box, BoxProps, Card, Flex } from '@chakra-ui/react';
import React, { useMemo, useRef } from 'react';
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
import ChatController, { type ChatControllerProps } from './ChatController';
import ChatAvatar from './ChatAvatar';
import { MessageCardStyle } from '../constants';
@@ -26,6 +26,8 @@ import { useSystem } from '@fastgpt/web/hooks/useSystem';
import { formatTimeToChatItemTime } from '@fastgpt/global/common/string/time';
import dayjs from 'dayjs';
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
import { eventBus, EventNameEnum } from '@/web/common/utils/eventbus';
import { addStatisticalDataToHistoryItem } from '@/global/core/chat/utils';
const colorMap = {
[ChatStatusEnum.loading]: {
@@ -141,6 +143,18 @@ const ChatItem = (props: Props) => {
const isChatting = useContextSelector(ChatBoxContext, (v) => v.isChatting);
const chatType = useContextSelector(ChatBoxContext, (v) => v.chatType);
const showNodeStatus = useContextSelector(ChatItemContext, (v) => v.showNodeStatus);
const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
const appId = useContextSelector(ChatBoxContext, (v) => v.appId);
const chatId = useContextSelector(ChatBoxContext, (v) => v.chatId);
const outLinkAuthData = useContextSelector(ChatBoxContext, (v) => v.outLinkAuthData);
const isShowReadRawSource = useContextSelector(ChatItemContext, (v) => v.isShowReadRawSource);
const { totalQuoteList: quoteList = [] } = useMemo(
() => addStatisticalDataToHistoryItem(chat),
[chat]
);
const isChatLog = chatType === 'log';
const { copyData } = useCopyData();
@@ -208,6 +222,59 @@ const ChatItem = (props: Props) => {
return groupedValues;
}, [chat.obj, chat.value, isChatting]);
const handleOpenQuoteReader = useCallback(
({
collectionId,
sourceId,
sourceName,
datasetId
}: {
collectionId?: string;
sourceId?: string;
sourceName?: string;
datasetId?: string;
}) => {
if (!setQuoteData) return;
const collectionIdList = collectionId
? [collectionId]
: [...new Set(quoteList.map((item) => item.collectionId))];
setQuoteData({
rawSearch: quoteList,
metadata:
collectionId && isShowReadRawSource
? {
appId: appId,
chatId: chatId,
chatItemDataId: chat.dataId,
collectionId: collectionId,
sourceId: sourceId || '',
sourceName: sourceName || '',
datasetId: datasetId || '',
outLinkAuthData
}
: {
appId: appId,
chatId: chatId,
chatItemDataId: chat.dataId,
collectionIdList,
sourceId: sourceId,
sourceName: sourceName,
outLinkAuthData
}
});
},
[setQuoteData, quoteList, isShowReadRawSource, appId, chatId, chat.dataId, outLinkAuthData]
);
useEffect(() => {
eventBus.on(EventNameEnum.openQuoteReader, handleOpenQuoteReader);
return () => {
eventBus.off(EventNameEnum.openQuoteReader);
};
}, [handleOpenQuoteReader]);
return (
<Box
_hover={{

View File

@@ -34,17 +34,19 @@ const QuoteList = React.memo(function QuoteList({
const { data: quoteList } = useRequest2(
async () =>
await getQuoteDataList({
datasetDataIdList: rawSearch.map((item) => item.id),
collectionIdList: [...new Set(rawSearch.map((item) => item.collectionId))],
chatItemDataId,
appId,
chatId: RawSourceBoxProps.chatId,
...outLinkAuthData
}),
!!chatItemDataId
? await getQuoteDataList({
datasetDataIdList: rawSearch.map((item) => item.id),
collectionIdList: [...new Set(rawSearch.map((item) => item.collectionId))],
chatItemDataId,
appId,
chatId: RawSourceBoxProps.chatId,
...outLinkAuthData
})
: [],
{
refreshDeps: [rawSearch, RawSourceBoxProps.chatId],
manual: !chatItemDataId
manual: false
}
);

View File

@@ -14,7 +14,7 @@ import { addStatisticalDataToHistoryItem } from '@/global/core/chat/utils';
import { useSize } from 'ahooks';
import { useContextSelector } from 'use-context-selector';
import { ChatBoxContext } from '../Provider';
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
import { eventBus, EventNameEnum } from '@/web/common/utils/eventbus';
const ContextModal = dynamic(() => import('./ContextModal'));
const WholeResponseModal = dynamic(() => import('../../../components/WholeResponseModal'));
@@ -30,23 +30,18 @@ const ResponseTags = ({
const { t } = useTranslation();
const quoteListRef = React.useRef<HTMLDivElement>(null);
const dataId = historyItem.dataId;
const chatTime = historyItem.time || new Date();
const chatTime = historyItem.time || new Date();
const durationSeconds = historyItem.durationSeconds || 0;
const {
totalQuoteList: quoteList = [],
llmModuleAccount = 0,
totalRunningTime: runningTime = 0,
historyPreviewLength = 0
} = useMemo(() => addStatisticalDataToHistoryItem(historyItem), [historyItem]);
const [quoteFolded, setQuoteFolded] = useState<boolean>(true);
const chatType = useContextSelector(ChatBoxContext, (v) => v.chatType);
const appId = useContextSelector(ChatBoxContext, (v) => v.appId);
const chatId = useContextSelector(ChatBoxContext, (v) => v.chatId);
const outLinkAuthData = useContextSelector(ChatBoxContext, (v) => v.outLinkAuthData);
const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
const notSharePage = useMemo(() => chatType !== 'share', [chatType]);
@@ -66,7 +61,6 @@ const ResponseTags = ({
? quoteListRef.current.scrollHeight > (isPc ? 50 : 55)
: true;
const isShowReadRawSource = useContextSelector(ChatItemContext, (v) => v.isShowReadRawSource);
const sourceList = useMemo(() => {
return Object.values(
quoteList.reduce((acc: Record<string, SearchDataResponseItemType[]>, cur) => {
@@ -86,11 +80,20 @@ const ResponseTags = ({
}));
}, [quoteList]);
const openQuoteReader = (item?: {
collectionId?: string;
sourceId?: string;
sourceName?: string;
datasetId?: string;
}) => {
eventBus.emit(EventNameEnum.openQuoteReader, item);
};
const notEmptyTags =
quoteList.length > 0 ||
(llmModuleAccount === 1 && notSharePage) ||
(llmModuleAccount > 1 && notSharePage) ||
(isPc && runningTime > 0) ||
(isPc && durationSeconds > 0) ||
notSharePage;
return !showTags ? null : (
@@ -158,35 +161,7 @@ const ResponseTags = ({
cursor={'pointer'}
onClick={(e) => {
e.stopPropagation();
if (isShowReadRawSource) {
setQuoteData({
rawSearch: quoteList,
metadata: {
appId,
chatId,
chatItemDataId: dataId,
collectionId: item.collectionId,
sourceId: item.sourceId || '',
sourceName: item.sourceName,
datasetId: item.datasetId,
outLinkAuthData
}
});
} else {
setQuoteData({
rawSearch: quoteList,
metadata: {
appId,
chatId,
chatItemDataId: dataId,
collectionIdList: [item.collectionId],
sourceId: item.sourceId || '',
sourceName: item.sourceName,
outLinkAuthData
}
});
}
openQuoteReader(item);
}}
height={6}
>
@@ -241,17 +216,7 @@ const ResponseTags = ({
cursor={'pointer'}
onClick={(e) => {
e.stopPropagation();
setQuoteData({
rawSearch: quoteList,
metadata: {
appId,
chatId,
chatItemDataId: dataId,
collectionIdList: [...new Set(quoteList.map((item) => item.collectionId))],
outLinkAuthData
}
});
openQuoteReader();
}}
>
{t('chat:citations', { num: quoteList.length })}
@@ -279,10 +244,10 @@ const ResponseTags = ({
{t('chat:multiple_AI_conversations')}
</MyTag>
)}
{isPc && runningTime > 0 && (
{isPc && durationSeconds > 0 && (
<MyTooltip label={t('chat:module_runtime_and')}>
<MyTag colorSchema="purple" type="borderSolid" cursor={'default'}>
{runningTime}s
{durationSeconds.toFixed(2)}s
</MyTag>
</MyTooltip>
)}

View File

@@ -221,7 +221,8 @@ const ChatBox = ({
interactive,
autoTTSResponse,
variables,
nodeResponse
nodeResponse,
durationSeconds
}: generatingMessageProps & { autoTTSResponse?: boolean }) => {
setChatRecords((state) =>
state.map((item, index) => {
@@ -342,6 +343,13 @@ const ChatBox = ({
...item,
value: item.value.concat(val)
};
} else if (event === SseResponseEventEnum.workflowDuration && durationSeconds) {
return {
...item,
durationSeconds: item.durationSeconds
? +(item.durationSeconds + durationSeconds).toFixed(2)
: durationSeconds
};
}
return item;

View File

@@ -17,6 +17,7 @@ export type generatingMessageProps = {
interactive?: WorkflowInteractiveResponseType;
variables?: Record<string, any>;
nodeResponse?: ChatHistoryItemResType;
durationSeconds?: number;
};
export type StartChatFnProps = {

View File

@@ -252,29 +252,39 @@ export const WholeResponseContent = ({
}
/>
)}
<Row
label={t('common:core.chat.response.module similarity')}
value={activeModule?.similarity}
/>
<Row label={t('common:core.chat.response.module limit')} value={activeModule?.limit} />
<Row label={t('chat:response_embedding_model')} value={activeModule?.embeddingModel} />
<Row
label={t('chat:response_embedding_model_tokens')}
value={`${activeModule?.embeddingTokens}`}
/>
{activeModule?.searchUsingReRank !== undefined && (
<Row
label={t('common:core.chat.response.search using reRank')}
rawDom={
<Box border={'base'} borderRadius={'md'} p={2}>
{activeModule?.searchUsingReRank ? (
activeModule?.rerankModel ? (
<Box>{`${activeModule.rerankModel}: ${activeModule.rerankWeight}`}</Box>
<>
<Row
label={t('common:core.chat.response.search using reRank')}
rawDom={
<Box border={'base'} borderRadius={'md'} p={2}>
{activeModule?.searchUsingReRank ? (
activeModule?.rerankModel ? (
<Box>{`${activeModule.rerankModel}: ${activeModule.rerankWeight}`}</Box>
) : (
'True'
)
) : (
'True'
)
) : (
`False`
)}
</Box>
}
/>
`False`
)}
</Box>
}
/>
<Row
label={t('chat:response_rerank_tokens')}
value={`${activeModule?.reRankInputTokens}`}
/>
</>
)}
{activeModule.queryExtensionResult && (
<>

View File

@@ -17,9 +17,11 @@ type FormType = {
const UpdateContactModal = ({
onClose,
onSuccess,
mode
}: {
onClose: () => void;
onSuccess?: (val: string) => void;
mode: 'contact' | 'notification_account';
}) => {
const { t } = useTranslation();
@@ -37,20 +39,22 @@ const UpdateContactModal = ({
const verifyCode = watch('verifyCode');
const { runAsync: onSubmit, loading: isLoading } = useRequest2(
(data: FormType) => {
async (data: FormType) => {
if (mode === 'contact') {
return updateContact(data);
await updateContact(data);
} else {
return updateNotificationAccount({
await updateNotificationAccount({
account: data.contact,
verifyCode: data.verifyCode
});
}
return data.contact;
},
{
onSuccess() {
onSuccess(data) {
initUserInfo();
onClose();
onSuccess?.(data);
},
successToast: t('common:support.user.info.bind_notification_success'),
errorToast: t('common:support.user.info.bind_notification_error')

View File

@@ -1,36 +1,98 @@
import MyModal from '@fastgpt/web/components/common/MyModal';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'next-i18next';
import { Box, ModalBody } from '@chakra-ui/react';
import { checkBalancePayResult } from '@/web/support/wallet/bill/api';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { Box, ModalBody, Flex, Button } from '@chakra-ui/react';
import { checkBalancePayResult, putUpdatePayment } from '@/web/support/wallet/bill/api';
import LightTip from '@fastgpt/web/components/common/LightTip';
import QRCode from 'qrcode';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import {
BillPayWayEnum,
BillStatusEnum,
QR_CODE_SIZE
} from '@fastgpt/global/support/wallet/bill/constants';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import Markdown from '@/components/Markdown';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { CreateBillResponse } from '@fastgpt/global/support/wallet/bill/api';
export type QRPayProps = {
readPrice: number;
codeUrl: string;
billId: string;
export type QRPayProps = CreateBillResponse & {
tip?: string;
};
const qrCodeSize = 168;
const QRCodePayModal = ({
tip,
readPrice,
codeUrl,
billId,
payment,
qrCode,
iframeCode,
markdown,
onSuccess
}: QRPayProps & { tip?: string; onSuccess?: () => any }) => {
const { t } = useTranslation();
const { toast } = useToast();
const canvasRef = useRef<HTMLDivElement>(null);
const toast = useToast();
const { feConfigs } = useSystemStore();
const isAlipayConfigured = feConfigs.payConfig?.alipay;
const isWxConfigured = feConfigs.payConfig?.wx;
const isBankConfigured = feConfigs.payConfig?.bank;
const [payWayRenderData, setPayWayRenderData] = useState<{
qrCode?: string;
iframeCode?: string;
markdown?: string;
}>({
qrCode,
iframeCode,
markdown
});
const [selectedPayment, setSelectedPayment] = useState(payment);
const { runAsync: handlePaymentChange, loading: isUpdating } = useRequest2(
async (newPayment: BillPayWayEnum) => {
if (newPayment === selectedPayment) {
return;
}
const response = await putUpdatePayment({ billId, payWay: newPayment });
setPayWayRenderData(response);
setSelectedPayment(newPayment);
},
{
refreshDeps: [billId, selectedPayment]
}
);
// Check pay result
useRequest2(() => checkBalancePayResult(billId), {
manual: false,
pollingInterval: 2000,
onSuccess: ({ status, description }) => {
if (status === BillStatusEnum.SUCCESS) {
toast.toast({
description: t('common:pay_success'),
status: 'success',
duration: 2000
});
onSuccess?.();
} else {
console.log(status, description);
}
}
});
// UI render
// Draw QR code
const drawCode = useCallback(() => {
if (!payWayRenderData.qrCode) return;
const canvas = document.createElement('canvas');
QRCode.toCanvas(canvas, codeUrl, {
width: qrCodeSize,
QRCode.toCanvas(canvas, payWayRenderData.qrCode, {
width: QR_CODE_SIZE,
margin: 0,
color: {
dark: '#000000',
@@ -41,38 +103,125 @@ const QRCodePayModal = ({
if (canvasRef.current) {
canvasRef.current.innerHTML = '';
canvasRef.current.appendChild(canvas);
} else {
drawCode();
}
})
.catch((err) => {
console.error('QRCode generation error:', err);
});
}, [codeUrl]);
.catch(console.error);
}, [payWayRenderData.qrCode]);
useEffect(() => {
drawCode();
}, [drawCode]);
useRequest2(() => checkBalancePayResult(billId), {
manual: false,
pollingInterval: 2000,
onSuccess: (res) => {
if (res) {
onSuccess?.();
// Payment Button
const getPaymentButtonStyles = (isActive: boolean) => ({
baseStyle: {
display: 'flex',
padding: '13px 22px 13px 19px',
justifyContent: 'center',
alignItems: 'center',
flex: '1 0 0',
borderRadius: '7.152px',
border: isActive ? '1px solid #3370FF' : '1px solid #E8EBF0',
background: '#FFF',
_hover: {
background: isActive ? '#FFF' : '#F7F8FA',
border: isActive ? '1px solid #3370FF' : '1px solid #E8EBF0'
},
_active: {
background: '#FFF',
borderColor: '#3370FF'
}
},
errorToast: ''
}
});
const renderPaymentContent = () => {
if (payWayRenderData.qrCode) {
return <Box ref={canvasRef} display={'inline-block'} h={`${QR_CODE_SIZE}px`} />;
}
if (payWayRenderData.iframeCode) {
return (
<iframe
srcDoc={payWayRenderData.iframeCode}
style={{
width: QR_CODE_SIZE + 5,
height: QR_CODE_SIZE + 5,
border: 'none',
display: 'inline-block'
}}
/>
);
}
if (payWayRenderData.markdown) {
return <Markdown source={payWayRenderData.markdown} />;
}
return null;
};
return (
<MyModal isOpen title={t('common:user.Pay')} iconSrc="/imgs/modal/pay.svg">
<ModalBody textAlign={'center'} pb={10} whiteSpace={'pre-wrap'}>
{tip && <LightTip text={tip} mb={8} textAlign={'left'} />}
<Box ref={canvasRef} display={'inline-block'} h={`${qrCodeSize}px`}></Box>
<Box mt={5} textAlign={'center'}>
{t('common:pay.wechat', { price: readPrice })}
<MyModal
isLoading={isUpdating}
isOpen
title={t('common:user.Pay')}
iconSrc="/imgs/modal/wallet.svg"
w={'600px'}
>
<ModalBody textAlign={'center'} padding={['16px 24px', '32px 52px']}>
{tip && <LightTip text={tip} mb={6} textAlign={'left'} />}
<Box>{t('common:pay_money')}</Box>
<Box color="primary.600" fontSize="32px" fontWeight="600" lineHeight="40px" mb={6}>
¥{readPrice.toFixed(2)}
</Box>
{renderPaymentContent()}
{selectedPayment !== BillPayWayEnum.bank && (
<Box
mt={5}
textAlign={'center'}
display="flex"
alignItems="center"
justifyContent="center"
gap={1}
>
<MyIcon name={'common/info'} w={4} h={4} />
{t('common:pay.noclose')}
</Box>
)}
<Flex justifyContent="center" gap={3} mt={6}>
{isWxConfigured && (
<Button
flex={1}
h={10}
onClick={() => handlePaymentChange(BillPayWayEnum.wx)}
color={'myGray.900'}
leftIcon={<MyIcon name={'common/wechat'} />}
sx={getPaymentButtonStyles(selectedPayment === BillPayWayEnum.wx).baseStyle}
>
{t('common:pay.wx_payment')}
</Button>
)}
{isAlipayConfigured && (
<Button
flex={1}
h={10}
color={'myGray.900'}
onClick={() => handlePaymentChange(BillPayWayEnum.alipay)}
leftIcon={<MyIcon name={'common/alipay'} />}
sx={getPaymentButtonStyles(selectedPayment === BillPayWayEnum.alipay).baseStyle}
>
{t('common:pay_alipay_payment')}
</Button>
)}
{isBankConfigured && (
<Button
flex={1}
h={10}
color={'myGray.900'}
onClick={() => handlePaymentChange(BillPayWayEnum.bank)}
sx={getPaymentButtonStyles(selectedPayment === BillPayWayEnum.bank).baseStyle}
>
{t('common:pay_corporate_payment')}
</Button>
)}
</Flex>
</ModalBody>
</MyModal>
);

View File

@@ -38,9 +38,9 @@ const StandardPlanContentList = ({
permissionCustomApiKey: plan.permissionCustomApiKey,
permissionCustomCopyright: plan.permissionCustomCopyright,
trainingWeight: plan.trainingWeight,
permissionReRank: plan.permissionReRank,
totalPoints: plan.totalPoints * (mode === SubModeEnum.month ? 1 : 12),
permissionWebsiteSync: plan.permissionWebsiteSync
permissionWebsiteSync: plan.permissionWebsiteSync,
permissionTeamOperationLog: plan.permissionTeamOperationLog
};
}, [subPlans?.standard, level, mode]);
@@ -113,18 +113,20 @@ const StandardPlanContentList = ({
})}
</Box>
</Flex>
{!!planContent.permissionReRank && (
<Flex alignItems={'center'}>
<MyIcon name={'price/right'} w={'16px'} mr={3} />
<Box color={'myGray.600'}>{t('common:support.wallet.subscription.rerank')}</Box>
</Flex>
)}
{!!planContent.permissionWebsiteSync && (
<Flex alignItems={'center'}>
<MyIcon name={'price/right'} w={'16px'} mr={3} />
<Box color={'myGray.600'}>{t('common:support.wallet.subscription.web_site_sync')}</Box>
</Flex>
)}
{!!planContent.permissionTeamOperationLog && (
<Flex alignItems={'center'}>
<MyIcon name={'price/right'} w={'16px'} mr={3} />
<Box color={'myGray.600'}>
{t('common:support.wallet.subscription.team_operation_log')}
</Box>
</Flex>
)}
</Grid>
) : null;
};

View File

@@ -45,9 +45,6 @@ export function addStatisticalDataToHistoryItem(historyItem: ChatItemType) {
.map((item) => item.quoteList)
.flat()
.filter(Boolean) as SearchDataResponseItemType[],
totalRunningTime: Number(
historyItem.responseData?.reduce((sum, item) => sum + (item.runningTime || 0), 0).toFixed(2)
),
historyPreviewLength: flatResData.find(isLLMNode)?.historyPreview?.length
};
}

View File

@@ -23,7 +23,7 @@ export async function register() {
import('@fastgpt/service/common/mongo/index'),
import('@fastgpt/service/common/system/tools'),
import('@/service/common/system'),
import('@fastgpt/service/common/vectorStore/controller'),
import('@fastgpt/service/common/vectorDB/controller'),
import('@/service/mongo'),
import('@/service/core/app/plugin'),
import('@/service/common/system/volumnMongoWatch'),

View File

@@ -20,13 +20,14 @@ import { useToast } from '@fastgpt/web/hooks/useToast';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useTranslation } from 'next-i18next';
import {
BillStatusEnum,
BillTypeEnum,
billPayWayMap,
billStatusMap,
billTypeMap
} from '@fastgpt/global/support/wallet/bill/constants';
import MyBox from '@fastgpt/web/components/common/MyBox';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { standardSubLevelMap, subModeMap } from '@fastgpt/global/support/wallet/sub/constants';
import MySelect from '@fastgpt/web/components/common/MySelect';
import MyModal from '@fastgpt/web/components/common/MyModal';
@@ -68,39 +69,33 @@ const BillTable = () => {
defaultRequest: false
});
const { mutate: handleRefreshPayOrder, isLoading: isRefreshing } = useRequest({
mutationFn: async (payId: string) => {
try {
const data = await checkBalancePayResult(payId);
const { runAsync: handleRefreshPayOrder, loading: isRefreshing } = useRequest2(
async (payId: string) => {
const { status, description } = await checkBalancePayResult(payId);
if (status === BillStatusEnum.SUCCESS) {
toast({
title: data,
title: t('common:pay_success'),
status: 'success'
});
} catch (error: any) {
} else {
toast({
title: error?.message,
title: t(description as any),
status: 'warning'
});
console.log(error);
}
try {
if (status === BillStatusEnum.SUCCESS || status === BillStatusEnum.CLOSED) {
getData(1);
} catch (error) {}
}
}
});
);
useEffect(() => {
getData(1);
}, [billType]);
return (
<MyBox
isLoading={isLoading || isRefreshing}
position={'relative'}
h={'100%'}
minH={'50vh'}
overflow={'overlay'}
>
<MyBox isLoading={isLoading} position={'relative'} h={'100%'} minH={'50vh'}>
<TableContainer>
<Table>
<Thead>
@@ -135,7 +130,12 @@ const BillTable = () => {
<Td>{t(billStatusMap[item.status]?.label as any)}</Td>
<Td>
{item.status === 'NOTPAY' && (
<Button mr={4} onClick={() => handleRefreshPayOrder(item._id)} size={'sm'}>
<Button
isLoading={isRefreshing}
mr={4}
onClick={() => handleRefreshPayOrder(item._id)}
size={'sm'}
>
{t('account_bill:update')}
</Button>
)}
@@ -210,11 +210,13 @@ function BillDetailModal({ bill, onClose }: { bill: BillSchemaType; onClose: ()
<Box>{t(billPayWayMap[bill.metadata.payWay]?.label as any)}</Box>
</Flex>
)}
<Flex alignItems={'center'} pb={4}>
<FormLabel flex={'0 0 120px'}>{t('account_bill:support_wallet_amount')}:</FormLabel>
<Box>{t('account_bill:yuan', { amount: formatStorePrice2Read(bill.price) })}</Box>
</Flex>
{bill.metadata && (
{!!bill.price && (
<Flex alignItems={'center'} pb={4}>
<FormLabel flex={'0 0 120px'}>{t('account_bill:support_wallet_amount')}:</FormLabel>
<Box>{t('account_bill:yuan', { amount: formatStorePrice2Read(bill.price) })}</Box>
</Flex>
)}
{bill.metadata && !!bill.price && (
<Flex alignItems={'center'} pb={4}>
<FormLabel flex={'0 0 120px'}>{t('account_bill:has_invoice')}:</FormLabel>
{bill.metadata.payWay === 'balance' ? (
@@ -239,7 +241,7 @@ function BillDetailModal({ bill, onClose }: { bill: BillSchemaType; onClose: ()
{bill.metadata?.month !== undefined && (
<Flex alignItems={'center'} pb={4}>
<FormLabel flex={'0 0 120px'}>{t('account_bill:subscription_mode_month')}:</FormLabel>
<Box>{bill.metadata?.month}</Box>
<Box>{`${bill.metadata?.month} ${t('account_bill:month')}`}</Box>
</Flex>
)}
{bill.metadata?.datasetSize !== undefined && (

View File

@@ -221,7 +221,7 @@ const InvoiceHeaderForm = () => {
return (
<>
<MyBox isLoading={isLoading} pt={['1rem', '3.5rem']}>
<MyBox isLoading={isLoading} pt={'1rem'}>
<Flex w={'100%'} overflow={'auto'} justify={'center'} flexDir={'column'} align={'center'}>
<InvoiceHeaderSingleForm inputForm={inputForm} />
<Flex w={'100%'} justify={'center'} mt={'3rem'}>

View File

@@ -22,6 +22,7 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
import dayjs from 'dayjs';
import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/usage/tools';
import MyModal from '@fastgpt/web/components/common/MyModal';
const InvoiceTable = () => {
const { t } = useTranslation();
const [invoiceDetailData, setInvoiceDetailData] = useState<InvoiceSchemaType | ''>('');

View File

@@ -0,0 +1,57 @@
import { redeemCoupon } from '@/web/support/user/team/api';
import { Button, Input, VStack, Text, ModalBody, Box, ModalFooter } from '@chakra-ui/react';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import React from 'react';
import { useTranslation } from 'react-i18next';
const RedeemCouponModal = ({
onClose,
onSuccess
}: {
onClose: () => void;
onSuccess: () => void;
}) => {
const { t } = useTranslation();
const [couponCode, setCouponCode] = React.useState('');
const { runAsync: redeemCouponAsync, loading } = useRequest2(redeemCoupon, {
manual: true,
onSuccess: () => {
onSuccess();
onClose();
},
successToast: t('common:common.Success')
});
return (
<MyModal
isOpen
onClose={onClose}
iconSrc="support/account/coupon"
title={t('account_info:redeem_coupon')}
>
<ModalBody>
<Box fontWeight={500} color={'myGray.900'} mb={'1'}>
{t('account_info:redeem_coupon')}
</Box>
<Input
placeholder={t('account_info:redeem_coupon')}
value={couponCode}
onChange={(e) => setCouponCode(e.target.value)}
/>
</ModalBody>
<ModalFooter>
<Button variant={'whiteBase'} onClick={onClose}>
{t('account_info:cancel')}
</Button>
<Button ml={2} isLoading={loading} onClick={() => redeemCouponAsync(couponCode)}>
{t('account_info:confirm')}
</Button>
</ModalFooter>
</MyModal>
);
};
export default RedeemCouponModal;

View File

@@ -127,11 +127,11 @@ export const ModelEditModal = ({
);
const priceUnit = useMemo(() => {
if (isLLMModel || isEmbeddingModel) return '/ 1k Tokens';
if (isLLMModel || isEmbeddingModel || isRerankModel) return '/ 1k Tokens';
if (isTTSModel) return `/ 1k ${t('common:unit.character')}`;
if (isSTTModel) return `/ 60 ${t('common:unit.seconds')}`;
return '';
}, [isLLMModel, isEmbeddingModel, isTTSModel, t, isSTTModel]);
}, [isLLMModel, isEmbeddingModel, isTTSModel, t, isSTTModel, isRerankModel]);
const { runAsync: updateModel, loading: updatingModel } = useRequest2(
async (data: SystemModelItemType) => {

View File

@@ -141,6 +141,7 @@ const ModelTable = ({ Tab }: { Tab: React.ReactNode }) => {
typeLabel: t('common:model.type.embedding'),
priceLabel: (
<Flex color={'myGray.700'}>
{`${t('common:common.Input')}: `}
<Box fontWeight={'bold'} color={'myGray.900'} mr={0.5}>
{item.charsPointsPrice || 0}
</Box>
@@ -184,7 +185,17 @@ const ModelTable = ({ Tab }: { Tab: React.ReactNode }) => {
.map((item) => ({
...item,
typeLabel: t('common:model.type.reRank'),
priceLabel: <Flex color={'myGray.700'}>- </Flex>,
priceLabel: item.charsPointsPrice ? (
<Flex color={'myGray.700'}>
{`${t('common:common.Input')}: `}
<Box fontWeight={'bold'} color={'myGray.900'} mr={0.5}>
{item.charsPointsPrice}
</Box>
{` ${t('common:support.wallet.subscription.point')} / 1K Tokens`}
</Flex>
) : (
'-'
),
tagColor: 'red'
}));

View File

@@ -190,8 +190,9 @@ function EditModal({
/>
{isOpenContact && (
<UpdateContact
onClose={() => {
onCloseContact();
onClose={onCloseContact}
onSuccess={(val) => {
setValue('notificationAccount', val);
}}
mode="notification_account"
/>

View File

@@ -17,7 +17,7 @@ import MyBox from '@fastgpt/web/components/common/MyBox';
import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination';
import { getOperationLogs } from '@/web/support/user/team/operantionLog/api';
import { TeamPermission } from '@fastgpt/global/support/permission/user/controller';
import { operationLogI18nMap } from '@fastgpt/service/support/operationLog/constants';
import { operationLogMap } from '@fastgpt/service/support/operationLog/constants';
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
import { formatTime2YMDHMS } from '@fastgpt/global/common/string/time';
import UserBox from '@fastgpt/web/components/common/UserBox';
@@ -52,7 +52,7 @@ function OperationLogTable({ Tabs }: { Tabs: React.ReactNode }) {
const eventOptions = useMemo(
() =>
Object.values(OperationLogEventEnum).map((event) => ({
label: t(operationLogI18nMap[event].typeLabel),
label: t(operationLogMap[event].typeLabel),
value: event
})),
[t]
@@ -158,7 +158,7 @@ function OperationLogTable({ Tabs }: { Tabs: React.ReactNode }) {
</Thead>
<Tbody>
{operationLogs?.map((log) => {
const i18nData = operationLogI18nMap[log.event];
const i18nData = operationLogMap[log.event];
const metadata = { ...log.metadata };
if (log.event === OperationLogEventEnum.ASSIGN_PERMISSION) {

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