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:
@@ -25,7 +25,7 @@ import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/usage/tools';
|
||||
import { putUpdateMemberName } from '@/web/support/user/team/api';
|
||||
import { putUpdateMemberName, redeemCoupon } from '@/web/support/user/team/api';
|
||||
import { getDocPath } from '@/web/common/system/doc';
|
||||
import {
|
||||
StandardSubLevelEnum,
|
||||
@@ -47,6 +47,9 @@ import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { useMount } from 'ahooks';
|
||||
import MyDivider from '@fastgpt/web/components/common/MyDivider';
|
||||
|
||||
const RedeemCouponModal = dynamic(() => import('@/pageComponents/account/info/RedeemCouponModal'), {
|
||||
ssr: false
|
||||
});
|
||||
const StandDetailModal = dynamic(
|
||||
() => import('@/pageComponents/account/info/standardDetailModal'),
|
||||
{ ssr: false }
|
||||
@@ -353,8 +356,8 @@ const MyInfo = ({ onOpenContact }: { onOpenContact: () => void }) => {
|
||||
const PlanUsage = () => {
|
||||
const router = useRouter();
|
||||
const { t } = useTranslation();
|
||||
const { userInfo, initUserInfo, teamPlanStatus } = useUserStore();
|
||||
const { subPlans } = useSystemStore();
|
||||
const { userInfo, initUserInfo, teamPlanStatus, initTeamPlanStatus } = useUserStore();
|
||||
const { subPlans, feConfigs } = useSystemStore();
|
||||
const { reset } = useForm<UserUpdateParams>({
|
||||
defaultValues: userInfo as UserType
|
||||
});
|
||||
@@ -365,6 +368,12 @@ const PlanUsage = () => {
|
||||
onOpen: onOpenStandardModal
|
||||
} = useDisclosure();
|
||||
|
||||
const {
|
||||
isOpen: isOpenRedeemCouponModal,
|
||||
onClose: onCloseRedeemCouponModal,
|
||||
onOpen: onOpenRedeemCouponModal
|
||||
} = useDisclosure();
|
||||
|
||||
const planName = useMemo(() => {
|
||||
if (!teamPlanStatus?.standard?.currentSubLevel) return '';
|
||||
return standardSubLevelMap[teamPlanStatus.standard.currentSubLevel].label;
|
||||
@@ -460,14 +469,19 @@ const PlanUsage = () => {
|
||||
</Flex>
|
||||
<ModelPriceModal>
|
||||
{({ onOpen }) => (
|
||||
<Button ml={4} size={'sm'} onClick={onOpen}>
|
||||
<Button ml={3} size={'sm'} onClick={onOpen}>
|
||||
{t('account_info:billing_standard')}
|
||||
</Button>
|
||||
)}
|
||||
</ModelPriceModal>
|
||||
<Button ml={4} variant={'whitePrimary'} size={'sm'} onClick={onOpenStandardModal}>
|
||||
<Button ml={3} variant={'whitePrimary'} size={'sm'} onClick={onOpenStandardModal}>
|
||||
{t('account_info:package_details')}
|
||||
</Button>
|
||||
{userInfo?.permission.isOwner && feConfigs?.show_coupon && (
|
||||
<Button ml={3} variant={'whitePrimary'} size={'sm'} onClick={onOpenRedeemCouponModal}>
|
||||
{t('account_info:redeem_coupon')}
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
<Box
|
||||
mt={[3, 6]}
|
||||
@@ -603,6 +617,12 @@ const PlanUsage = () => {
|
||||
</Box>
|
||||
</Box>
|
||||
{isOpenStandardModal && <StandDetailModal onClose={onCloseStandardModal} />}
|
||||
{isOpenRedeemCouponModal && (
|
||||
<RedeemCouponModal
|
||||
onClose={onCloseRedeemCouponModal}
|
||||
onSuccess={() => initTeamPlanStatus()}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
) : null;
|
||||
};
|
||||
|
||||
@@ -13,6 +13,8 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
|
||||
import { TeamContext, TeamModalContextProvider } from '@/pageComponents/account/team/context';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
|
||||
const MemberTable = dynamic(() => import('@/pageComponents/account/team/MemberTable'));
|
||||
const PermissionManage = dynamic(
|
||||
@@ -48,7 +50,19 @@ const Team = () => {
|
||||
const { teamTab = TeamTabEnum.member } = router.query as { teamTab: `${TeamTabEnum}` };
|
||||
|
||||
const { t } = useTranslation();
|
||||
const { userInfo } = useUserStore();
|
||||
const { userInfo, teamPlanStatus } = useUserStore();
|
||||
const standardPlan = teamPlanStatus?.standard;
|
||||
const level = standardPlan?.currentSubLevel;
|
||||
const { subPlans } = useSystemStore();
|
||||
const planContent = useMemo(() => {
|
||||
const plan = level !== undefined ? subPlans?.standard?.[level] : undefined;
|
||||
|
||||
if (!plan) return;
|
||||
return {
|
||||
permissionTeamOperationLog: plan.permissionTeamOperationLog
|
||||
};
|
||||
}, [subPlans?.standard, level]);
|
||||
const { toast } = useToast();
|
||||
|
||||
const { setEditTeamData, teamSize } = useContextSelector(TeamContext, (v) => v);
|
||||
|
||||
@@ -65,6 +79,13 @@ const Team = () => {
|
||||
px={'1rem'}
|
||||
value={teamTab}
|
||||
onChange={(e) => {
|
||||
if (e === TeamTabEnum.operationLog && !planContent?.permissionTeamOperationLog) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: t('common:not_permission')
|
||||
});
|
||||
return;
|
||||
}
|
||||
router.replace({
|
||||
query: {
|
||||
...router.query,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { PgClient } from '@fastgpt/service/common/vectorStore/pg';
|
||||
import { PgClient } from '@fastgpt/service/common/vectorDB/pg/controller';
|
||||
|
||||
/* pg 中的数据搬到 mongo dataset.datas 中,并做映射 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
|
||||
@@ -11,7 +11,7 @@ import { MongoDatasetDataText } from '@fastgpt/service/core/dataset/data/dataTex
|
||||
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
|
||||
import { DatasetCollectionSchemaType } from '@fastgpt/global/core/dataset/type';
|
||||
import { MongoDatasetTraining } from '@fastgpt/service/core/dataset/training/schema';
|
||||
import { deleteDatasetDataVector } from '@fastgpt/service/common/vectorStore/controller';
|
||||
import { deleteDatasetDataVector } from '@fastgpt/service/common/vectorDB/controller';
|
||||
|
||||
// 删了库,没删集合
|
||||
const checkInvalidCollection = async () => {
|
||||
|
||||
@@ -5,8 +5,8 @@ import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection
|
||||
import { DatasetCollectionDataProcessModeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
|
||||
import { DatasetDataIndexTypeEnum } from '@fastgpt/global/core/dataset/data/constants';
|
||||
import { PgClient } from '@fastgpt/service/common/vectorStore/pg';
|
||||
import { PG_ADDRESS } from '@fastgpt/service/common/vectorStore/constants';
|
||||
import { PgClient } from '@fastgpt/service/common/vectorDB/pg/controller';
|
||||
import { PG_ADDRESS } from '@fastgpt/service/common/vectorDB/constants';
|
||||
|
||||
// 所有 trainingType=auto 的 collection,都改成 trainingType=chunk
|
||||
const updateCollections = async () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import { MilvusCtrl } from '@fastgpt/service/common/vectorStore/milvus/class';
|
||||
import { DatasetVectorTableName } from '@fastgpt/service/common/vectorStore/constants';
|
||||
import { MilvusCtrl } from '@fastgpt/service/common/vectorDB/milvus/index';
|
||||
import { DatasetVectorTableName } from '@fastgpt/service/common/vectorDB/constants';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
|
||||
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
||||
|
||||
@@ -63,14 +63,6 @@ export type Props = {
|
||||
};
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
res.on('close', () => {
|
||||
res.end();
|
||||
});
|
||||
res.on('error', () => {
|
||||
console.log('error: ', 'request error');
|
||||
res.end();
|
||||
});
|
||||
|
||||
let {
|
||||
nodes = [],
|
||||
edges = [],
|
||||
@@ -170,39 +162,40 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
});
|
||||
|
||||
/* start process */
|
||||
const { flowResponses, assistantResponses, newVariables, flowUsages } = await dispatchWorkFlow({
|
||||
res,
|
||||
requestOrigin: req.headers.origin,
|
||||
mode: 'test',
|
||||
timezone,
|
||||
externalProvider,
|
||||
uid: tmbId,
|
||||
const { flowResponses, assistantResponses, newVariables, flowUsages, durationSeconds } =
|
||||
await dispatchWorkFlow({
|
||||
res,
|
||||
requestOrigin: req.headers.origin,
|
||||
mode: 'test',
|
||||
timezone,
|
||||
externalProvider,
|
||||
uid: tmbId,
|
||||
|
||||
runningAppInfo: {
|
||||
id: appId,
|
||||
teamId: app.teamId,
|
||||
tmbId: app.tmbId
|
||||
},
|
||||
runningUserInfo: {
|
||||
teamId,
|
||||
tmbId
|
||||
},
|
||||
runningAppInfo: {
|
||||
id: appId,
|
||||
teamId: app.teamId,
|
||||
tmbId: app.tmbId
|
||||
},
|
||||
runningUserInfo: {
|
||||
teamId,
|
||||
tmbId
|
||||
},
|
||||
|
||||
chatId,
|
||||
responseChatItemId,
|
||||
runtimeNodes,
|
||||
runtimeEdges: storeEdges2RuntimeEdges(edges, interactive),
|
||||
variables,
|
||||
query: removeEmptyUserInput(userQuestion.value),
|
||||
lastInteractive: interactive,
|
||||
chatConfig,
|
||||
histories: newHistories,
|
||||
stream: true,
|
||||
maxRunTimes: WORKFLOW_MAX_RUN_TIMES,
|
||||
workflowStreamResponse: workflowResponseWrite,
|
||||
version: 'v2',
|
||||
responseDetail: true
|
||||
});
|
||||
chatId,
|
||||
responseChatItemId,
|
||||
runtimeNodes,
|
||||
runtimeEdges: storeEdges2RuntimeEdges(edges, interactive),
|
||||
variables,
|
||||
query: removeEmptyUserInput(userQuestion.value),
|
||||
lastInteractive: interactive,
|
||||
chatConfig,
|
||||
histories: newHistories,
|
||||
stream: true,
|
||||
maxRunTimes: WORKFLOW_MAX_RUN_TIMES,
|
||||
workflowStreamResponse: workflowResponseWrite,
|
||||
version: 'v2',
|
||||
responseDetail: true
|
||||
});
|
||||
|
||||
workflowResponseWrite({
|
||||
event: SseResponseEventEnum.answer,
|
||||
@@ -238,7 +231,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
appId: app._id,
|
||||
userInteractiveVal,
|
||||
aiResponse,
|
||||
newVariables
|
||||
newVariables,
|
||||
durationSeconds
|
||||
});
|
||||
} else {
|
||||
await saveChat({
|
||||
@@ -252,7 +246,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
isUpdateUseTime: false, // owner update use time
|
||||
newTitle,
|
||||
source: ChatSourceEnum.test,
|
||||
content: [userQuestion, aiResponse]
|
||||
content: [userQuestion, aiResponse],
|
||||
durationSeconds
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -53,11 +53,11 @@ async function handler(
|
||||
const isOutLink = authType === GetChatTypeEnum.outLink;
|
||||
|
||||
const fieldMap = {
|
||||
[GetChatTypeEnum.normal]: `dataId obj value adminFeedback userBadFeedback userGoodFeedback time hideInUI ${
|
||||
[GetChatTypeEnum.normal]: `dataId obj value adminFeedback userBadFeedback userGoodFeedback time hideInUI durationSeconds ${
|
||||
DispatchNodeResponseKeyEnum.nodeResponse
|
||||
} ${loadCustomFeedbacks ? 'customFeedbacks' : ''}`,
|
||||
[GetChatTypeEnum.outLink]: `dataId obj value userGoodFeedback userBadFeedback adminFeedback time hideInUI ${DispatchNodeResponseKeyEnum.nodeResponse}`,
|
||||
[GetChatTypeEnum.team]: `dataId obj value userGoodFeedback userBadFeedback adminFeedback time hideInUI ${DispatchNodeResponseKeyEnum.nodeResponse}`
|
||||
[GetChatTypeEnum.outLink]: `dataId obj value userGoodFeedback userBadFeedback adminFeedback time hideInUI durationSeconds ${DispatchNodeResponseKeyEnum.nodeResponse}`,
|
||||
[GetChatTypeEnum.team]: `dataId obj value userGoodFeedback userBadFeedback adminFeedback time hideInUI durationSeconds ${DispatchNodeResponseKeyEnum.nodeResponse}`
|
||||
};
|
||||
|
||||
const { total, histories } = await getChatItems({
|
||||
|
||||
@@ -11,9 +11,8 @@ import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { DatasetCollectionItemType } from '@fastgpt/global/core/dataset/type';
|
||||
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||
import { collectionTagsToTagLabel } from '@fastgpt/service/core/dataset/collection/utils';
|
||||
import { getVectorCountByCollectionId } from '@fastgpt/service/common/vectorStore/controller';
|
||||
import { getVectorCountByCollectionId } from '@fastgpt/service/common/vectorDB/controller';
|
||||
import { MongoDatasetTraining } from '@fastgpt/service/core/dataset/training/schema';
|
||||
import { Types } from 'mongoose';
|
||||
import { readFromSecondary } from '@fastgpt/service/common/mongo/utils';
|
||||
|
||||
async function handler(req: NextApiRequest): Promise<DatasetCollectionItemType> {
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
import type { SearchTestProps, SearchTestResponse } from '@/global/core/dataset/api.d';
|
||||
import { authDataset } from '@fastgpt/service/support/permission/dataset/auth';
|
||||
import { pushGenerateVectorUsage } from '@/service/support/wallet/usage/push';
|
||||
import { pushGenerateVectorUsage, pushRerankUsage } from '@/service/support/wallet/usage/push';
|
||||
import {
|
||||
deepRagSearch,
|
||||
defaultSearchDatasetData
|
||||
} from '@fastgpt/service/core/dataset/search/controller';
|
||||
import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools';
|
||||
import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants';
|
||||
import {
|
||||
checkTeamAIPoints,
|
||||
checkTeamReRankPermission
|
||||
} from '@fastgpt/service/support/permission/teamLimit';
|
||||
import { checkTeamAIPoints } from '@fastgpt/service/support/permission/teamLimit';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||
@@ -58,6 +55,8 @@ async function handler(req: ApiRequestProps<SearchTestProps>): Promise<SearchTes
|
||||
// auth balance
|
||||
await checkTeamAIPoints(teamId);
|
||||
|
||||
const rerankModelData = getRerankModel(rerankModel);
|
||||
|
||||
const searchData = {
|
||||
histories: [],
|
||||
teamId,
|
||||
@@ -69,11 +68,19 @@ async function handler(req: ApiRequestProps<SearchTestProps>): Promise<SearchTes
|
||||
datasetIds: [datasetId],
|
||||
searchMode,
|
||||
embeddingWeight,
|
||||
usingReRank: usingReRank && (await checkTeamReRankPermission(teamId)),
|
||||
rerankModel: getRerankModel(rerankModel),
|
||||
usingReRank,
|
||||
rerankModel: rerankModelData,
|
||||
rerankWeight
|
||||
};
|
||||
const { searchRes, tokens, queryExtensionResult, deepSearchResult, ...result } = datasetDeepSearch
|
||||
const {
|
||||
searchRes,
|
||||
embeddingTokens,
|
||||
reRankInputTokens,
|
||||
usingReRank: searchUsingReRank,
|
||||
queryExtensionResult,
|
||||
deepSearchResult,
|
||||
...result
|
||||
} = datasetDeepSearch
|
||||
? await deepRagSearch({
|
||||
...searchData,
|
||||
datasetDeepSearchModel,
|
||||
@@ -88,10 +95,10 @@ async function handler(req: ApiRequestProps<SearchTestProps>): Promise<SearchTes
|
||||
});
|
||||
|
||||
// push bill
|
||||
const { totalPoints } = pushGenerateVectorUsage({
|
||||
const { totalPoints: embeddingTotalPoints } = pushGenerateVectorUsage({
|
||||
teamId,
|
||||
tmbId,
|
||||
inputTokens: tokens,
|
||||
inputTokens: reRankInputTokens,
|
||||
model: dataset.vectorModel,
|
||||
source: apikey ? UsageSourceEnum.api : UsageSourceEnum.fastgpt,
|
||||
|
||||
@@ -106,10 +113,19 @@ async function handler(req: ApiRequestProps<SearchTestProps>): Promise<SearchTes
|
||||
deepSearchOutputTokens: deepSearchResult.outputTokens
|
||||
})
|
||||
});
|
||||
const { totalPoints: reRankTotalPoints } = searchUsingReRank
|
||||
? pushRerankUsage({
|
||||
teamId,
|
||||
tmbId,
|
||||
inputTokens: reRankInputTokens,
|
||||
model: rerankModelData.model
|
||||
})
|
||||
: { totalPoints: 0 };
|
||||
|
||||
if (apikey) {
|
||||
updateApiKeyUsage({
|
||||
apikey,
|
||||
totalPoints: totalPoints
|
||||
totalPoints: embeddingTotalPoints + reRankTotalPoints
|
||||
});
|
||||
}
|
||||
|
||||
@@ -117,6 +133,7 @@ async function handler(req: ApiRequestProps<SearchTestProps>): Promise<SearchTes
|
||||
list: searchRes,
|
||||
duration: `${((Date.now() - start) / 1000).toFixed(3)}s`,
|
||||
queryExtensionModel: queryExtensionResult?.model,
|
||||
usingReRank: searchUsingReRank,
|
||||
...result
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import { ToolType } from '@fastgpt/global/core/app/type';
|
||||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||
import getMCPClient from '@fastgpt/service/core/app/mcp';
|
||||
import { MCPClient } from '@fastgpt/service/core/app/mcp';
|
||||
|
||||
export type getMCPToolsQuery = {};
|
||||
|
||||
@@ -15,14 +15,9 @@ async function handler(
|
||||
): Promise<getMCPToolsResponse> {
|
||||
const { url } = req.body;
|
||||
|
||||
const mcpClient = getMCPClient({ url });
|
||||
const mcpClient = new MCPClient({ url });
|
||||
|
||||
try {
|
||||
const tools = await mcpClient.getTools();
|
||||
return tools;
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
return mcpClient.getTools();
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||
import getMCPClient from '@fastgpt/service/core/app/mcp';
|
||||
import { MCPClient } from '@fastgpt/service/core/app/mcp';
|
||||
|
||||
export type RunMCPToolQuery = {};
|
||||
|
||||
@@ -18,14 +18,9 @@ async function handler(
|
||||
): Promise<RunMCPToolResponse> {
|
||||
const { url, toolName, params } = req.body;
|
||||
|
||||
const mcpClient = getMCPClient({ url });
|
||||
const mcpClient = new MCPClient({ url });
|
||||
|
||||
try {
|
||||
const result = await mcpClient.toolCall(toolName, params);
|
||||
return result;
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
return mcpClient.toolCall(toolName, params);
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
||||
@@ -85,30 +85,31 @@ const dispatchApp = async (app: AppSchema, variables: Record<string, any>) => {
|
||||
|
||||
const chatId = getNanoid();
|
||||
|
||||
const { flowUsages, assistantResponses, newVariables, flowResponses } = await dispatchWorkFlow({
|
||||
chatId,
|
||||
timezone,
|
||||
externalProvider,
|
||||
mode: 'chat',
|
||||
runningAppInfo: {
|
||||
id: String(app._id),
|
||||
teamId: String(app.teamId),
|
||||
tmbId: String(app.tmbId)
|
||||
},
|
||||
runningUserInfo: {
|
||||
teamId: String(app.teamId),
|
||||
tmbId: String(app.tmbId)
|
||||
},
|
||||
uid: String(app.tmbId),
|
||||
runtimeNodes,
|
||||
runtimeEdges: storeEdges2RuntimeEdges(edges),
|
||||
variables,
|
||||
query: removeEmptyUserInput(userQuestion.value),
|
||||
chatConfig,
|
||||
histories: [],
|
||||
stream: false,
|
||||
maxRunTimes: WORKFLOW_MAX_RUN_TIMES
|
||||
});
|
||||
const { flowUsages, assistantResponses, newVariables, flowResponses, durationSeconds } =
|
||||
await dispatchWorkFlow({
|
||||
chatId,
|
||||
timezone,
|
||||
externalProvider,
|
||||
mode: 'chat',
|
||||
runningAppInfo: {
|
||||
id: String(app._id),
|
||||
teamId: String(app.teamId),
|
||||
tmbId: String(app.tmbId)
|
||||
},
|
||||
runningUserInfo: {
|
||||
teamId: String(app.teamId),
|
||||
tmbId: String(app.tmbId)
|
||||
},
|
||||
uid: String(app.tmbId),
|
||||
runtimeNodes,
|
||||
runtimeEdges: storeEdges2RuntimeEdges(edges),
|
||||
variables,
|
||||
query: removeEmptyUserInput(userQuestion.value),
|
||||
chatConfig,
|
||||
histories: [],
|
||||
stream: false,
|
||||
maxRunTimes: WORKFLOW_MAX_RUN_TIMES
|
||||
});
|
||||
|
||||
// Save chat
|
||||
const aiResponse: AIChatItemType & { dataId?: string } = {
|
||||
@@ -128,7 +129,8 @@ const dispatchApp = async (app: AppSchema, variables: Record<string, any>) => {
|
||||
isUpdateUseTime: false, // owner update use time
|
||||
newTitle,
|
||||
source: ChatSourceEnum.mcp,
|
||||
content: [userQuestion, aiResponse]
|
||||
content: [userQuestion, aiResponse],
|
||||
durationSeconds
|
||||
});
|
||||
|
||||
// Push usage
|
||||
@@ -184,7 +186,7 @@ async function handler(
|
||||
const app = appList.find((app) => {
|
||||
const mcpApp = mcp.apps.find((mcpApp) => String(mcpApp.appId) === String(app._id))!;
|
||||
|
||||
return toolName === mcpApp.toolAlias || toolName === mcpApp.toolName;
|
||||
return toolName === mcpApp.toolName;
|
||||
});
|
||||
|
||||
if (!app) {
|
||||
|
||||
@@ -8,10 +8,10 @@ import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { getAppLatestVersion } from '@fastgpt/service/core/app/version/controller';
|
||||
import { Tool } from '@modelcontextprotocol/sdk/types';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
||||
import { toolValueTypeList } from '@fastgpt/global/core/workflow/constants';
|
||||
import { AppChatConfigType } from '@fastgpt/global/core/app/type';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io';
|
||||
|
||||
export type listToolsQuery = { key: string };
|
||||
|
||||
@@ -19,7 +19,9 @@ export type listToolsBody = {};
|
||||
|
||||
export type listToolsResponse = {};
|
||||
|
||||
const pluginNodes2InputSchema = (nodes: StoreNodeItemType[]) => {
|
||||
export const pluginNodes2InputSchema = (
|
||||
nodes: { flowNodeType: FlowNodeTypeEnum; inputs: FlowNodeInputItemType[] }[]
|
||||
) => {
|
||||
const pluginInput = nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput);
|
||||
|
||||
const schema: Tool['inputSchema'] = {
|
||||
@@ -47,7 +49,10 @@ const pluginNodes2InputSchema = (nodes: StoreNodeItemType[]) => {
|
||||
|
||||
return schema;
|
||||
};
|
||||
const workflow2InputSchema = (chatConfig?: AppChatConfigType) => {
|
||||
export const workflow2InputSchema = (chatConfig?: {
|
||||
fileSelectConfig?: AppChatConfigType['fileSelectConfig'];
|
||||
variables?: AppChatConfigType['variables'];
|
||||
}) => {
|
||||
const schema: Tool['inputSchema'] = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
@@ -138,7 +143,7 @@ async function handler(
|
||||
);
|
||||
|
||||
return {
|
||||
name: mcpApp.toolAlias || mcpApp.toolName,
|
||||
name: mcpApp.toolName,
|
||||
description: mcpApp.description,
|
||||
inputSchema: isPlugin
|
||||
? pluginNodes2InputSchema(version.nodes)
|
||||
@@ -150,5 +155,3 @@ async function handler(
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
||||
export { pluginNodes2InputSchema, workflow2InputSchema, handler };
|
||||
|
||||
@@ -56,15 +56,6 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
...req.body
|
||||
});
|
||||
|
||||
// auth app
|
||||
// const app = await MongoApp.findById(appId, 'modules').lean();
|
||||
// if (!app) {
|
||||
// throw new Error('app not found');
|
||||
// }
|
||||
// if (!whisperConfig?.open) {
|
||||
// throw new Error('Whisper is not open in the app');
|
||||
// }
|
||||
|
||||
const result = await aiTranscriptions({
|
||||
model: getDefaultSTTModel(),
|
||||
fileStream: fs.createReadStream(file.path)
|
||||
@@ -73,7 +64,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
pushWhisperUsage({
|
||||
teamId,
|
||||
tmbId,
|
||||
duration
|
||||
duration: result?.usage?.total_tokens || duration
|
||||
});
|
||||
|
||||
jsonRes(res, {
|
||||
|
||||
@@ -93,14 +93,6 @@ type AuthResponseType = {
|
||||
};
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
res.on('close', () => {
|
||||
res.end();
|
||||
});
|
||||
res.on('error', () => {
|
||||
console.log('error: ', 'request error');
|
||||
res.end();
|
||||
});
|
||||
|
||||
let {
|
||||
chatId,
|
||||
appId,
|
||||
@@ -266,42 +258,43 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
});
|
||||
|
||||
/* start flow controller */
|
||||
const { flowResponses, flowUsages, assistantResponses, newVariables } = await (async () => {
|
||||
if (app.version === 'v2') {
|
||||
return dispatchWorkFlow({
|
||||
res,
|
||||
requestOrigin: req.headers.origin,
|
||||
mode: 'chat',
|
||||
timezone,
|
||||
externalProvider,
|
||||
const { flowResponses, flowUsages, assistantResponses, newVariables, durationSeconds } =
|
||||
await (async () => {
|
||||
if (app.version === 'v2') {
|
||||
return dispatchWorkFlow({
|
||||
res,
|
||||
requestOrigin: req.headers.origin,
|
||||
mode: 'chat',
|
||||
timezone,
|
||||
externalProvider,
|
||||
|
||||
runningAppInfo: {
|
||||
id: String(app._id),
|
||||
teamId: String(app.teamId),
|
||||
tmbId: String(app.tmbId)
|
||||
},
|
||||
runningUserInfo: {
|
||||
teamId,
|
||||
tmbId
|
||||
},
|
||||
uid: String(outLinkUserId || tmbId),
|
||||
runningAppInfo: {
|
||||
id: String(app._id),
|
||||
teamId: String(app.teamId),
|
||||
tmbId: String(app.tmbId)
|
||||
},
|
||||
runningUserInfo: {
|
||||
teamId,
|
||||
tmbId
|
||||
},
|
||||
uid: String(outLinkUserId || tmbId),
|
||||
|
||||
chatId,
|
||||
responseChatItemId,
|
||||
runtimeNodes,
|
||||
runtimeEdges: storeEdges2RuntimeEdges(edges, interactive),
|
||||
variables,
|
||||
query: removeEmptyUserInput(userQuestion.value),
|
||||
lastInteractive: interactive,
|
||||
chatConfig,
|
||||
histories: newHistories,
|
||||
stream,
|
||||
maxRunTimes: WORKFLOW_MAX_RUN_TIMES,
|
||||
workflowStreamResponse: workflowResponseWrite
|
||||
});
|
||||
}
|
||||
return Promise.reject('您的工作流版本过低,请重新发布一次');
|
||||
})();
|
||||
chatId,
|
||||
responseChatItemId,
|
||||
runtimeNodes,
|
||||
runtimeEdges: storeEdges2RuntimeEdges(edges, interactive),
|
||||
variables,
|
||||
query: removeEmptyUserInput(userQuestion.value),
|
||||
lastInteractive: interactive,
|
||||
chatConfig,
|
||||
histories: newHistories,
|
||||
stream,
|
||||
maxRunTimes: WORKFLOW_MAX_RUN_TIMES,
|
||||
workflowStreamResponse: workflowResponseWrite
|
||||
});
|
||||
}
|
||||
return Promise.reject('您的工作流版本过低,请重新发布一次');
|
||||
})();
|
||||
|
||||
// save chat
|
||||
const isOwnerUse = !shareId && !spaceTeamId && String(tmbId) === String(app.tmbId);
|
||||
@@ -339,7 +332,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
appId: app._id,
|
||||
userInteractiveVal,
|
||||
aiResponse,
|
||||
newVariables
|
||||
newVariables,
|
||||
durationSeconds
|
||||
});
|
||||
} else {
|
||||
await saveChat({
|
||||
@@ -360,7 +354,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
metadata: {
|
||||
originIp,
|
||||
...metadata
|
||||
}
|
||||
},
|
||||
durationSeconds
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -93,14 +93,6 @@ type AuthResponseType = {
|
||||
};
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
res.on('close', () => {
|
||||
res.end();
|
||||
});
|
||||
res.on('error', () => {
|
||||
console.log('error: ', 'request error');
|
||||
res.end();
|
||||
});
|
||||
|
||||
let {
|
||||
chatId,
|
||||
appId,
|
||||
@@ -265,44 +257,46 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
});
|
||||
|
||||
/* start flow controller */
|
||||
const { flowResponses, flowUsages, assistantResponses, newVariables } = await (async () => {
|
||||
if (app.version === 'v2') {
|
||||
return dispatchWorkFlow({
|
||||
res,
|
||||
requestOrigin: req.headers.origin,
|
||||
mode: 'chat',
|
||||
timezone,
|
||||
externalProvider,
|
||||
const { flowResponses, flowUsages, assistantResponses, newVariables, durationSeconds } =
|
||||
await (async () => {
|
||||
if (app.version === 'v2') {
|
||||
return dispatchWorkFlow({
|
||||
res,
|
||||
requestOrigin: req.headers.origin,
|
||||
mode: 'chat',
|
||||
timezone,
|
||||
externalProvider,
|
||||
|
||||
runningAppInfo: {
|
||||
id: String(app._id),
|
||||
teamId: String(app.teamId),
|
||||
tmbId: String(app.tmbId)
|
||||
},
|
||||
runningUserInfo: {
|
||||
teamId,
|
||||
tmbId
|
||||
},
|
||||
uid: String(outLinkUserId || tmbId),
|
||||
runningAppInfo: {
|
||||
id: String(app._id),
|
||||
teamId: String(app.teamId),
|
||||
tmbId: String(app.tmbId)
|
||||
},
|
||||
runningUserInfo: {
|
||||
teamId,
|
||||
tmbId
|
||||
},
|
||||
uid: String(outLinkUserId || tmbId),
|
||||
|
||||
chatId,
|
||||
responseChatItemId,
|
||||
runtimeNodes,
|
||||
runtimeEdges: storeEdges2RuntimeEdges(edges, interactive),
|
||||
variables,
|
||||
query: removeEmptyUserInput(userQuestion.value),
|
||||
lastInteractive: interactive,
|
||||
chatConfig,
|
||||
histories: newHistories,
|
||||
stream,
|
||||
maxRunTimes: WORKFLOW_MAX_RUN_TIMES,
|
||||
workflowStreamResponse: workflowResponseWrite,
|
||||
version: 'v2',
|
||||
responseDetail
|
||||
});
|
||||
}
|
||||
return Promise.reject('您的工作流版本过低,请重新发布一次');
|
||||
})();
|
||||
chatId,
|
||||
responseChatItemId,
|
||||
runtimeNodes,
|
||||
runtimeEdges: storeEdges2RuntimeEdges(edges, interactive),
|
||||
variables,
|
||||
query: removeEmptyUserInput(userQuestion.value),
|
||||
lastInteractive: interactive,
|
||||
chatConfig,
|
||||
histories: newHistories,
|
||||
stream,
|
||||
maxRunTimes: WORKFLOW_MAX_RUN_TIMES,
|
||||
workflowStreamResponse: workflowResponseWrite,
|
||||
version: 'v2',
|
||||
responseAllData,
|
||||
responseDetail
|
||||
});
|
||||
}
|
||||
return Promise.reject('您的工作流版本过低,请重新发布一次');
|
||||
})();
|
||||
|
||||
// save chat
|
||||
const isOwnerUse = !shareId && !spaceTeamId && String(tmbId) === String(app.tmbId);
|
||||
@@ -340,7 +334,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
appId: app._id,
|
||||
userInteractiveVal,
|
||||
aiResponse,
|
||||
newVariables
|
||||
newVariables,
|
||||
durationSeconds
|
||||
});
|
||||
} else {
|
||||
await saveChat({
|
||||
@@ -361,7 +356,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
metadata: {
|
||||
originIp,
|
||||
...metadata
|
||||
}
|
||||
},
|
||||
durationSeconds
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user