V4.8.14 dev (#3234)

* feat: rewrite chat context (#3176)

* feat: add app auto execute (#3115)

* feat: add app auto execute

* auto exec configtion

* chatting animation

* change icon

* fix

* fix

* fix link

* feat: add chat context to all chatbox

* perf: loading ui

---------

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

* app auto exec (#3179)

* add chat records loaded state (#3184)

* perf: chat store reset storage (#3186)

* perf: chat store reset storage

* perf: auto exec code

* chore: workflow ui (#3175)

* chore: workflow ui

* fix

* change icon color config

* change popover to mymenu

* 4.8.14 test (#3189)

* update doc

* fix: token check

* perf: icon button

* update doc

* feat: share page support configuration Whether to allow the original view (#3194)

* update doc

* perf: fix index (#3206)

* perf: i18n

* perf: Add service entry (#3226)

* 4.8.14 test (#3228)

* fix: ai log

* fix: text splitter

* fix: reference unselect & user form description & simple to advance (#3229)

* fix: reference unselect & user form description & simple to advance

* change abort position

* perf

* perf: code (#3232)

* perf: code

* update doc

* fix: create btn permission (#3233)

* update doc

* fix: refresh chatbox listener

* perf: check invalid reference

* perf: check invalid reference

* update doc

* fix: ui props

---------

Co-authored-by: heheer <heheer@sealos.io>
This commit is contained in:
Archer
2024-11-26 12:02:58 +08:00
committed by GitHub
parent 7e1d31b5a9
commit 8aa6b53760
221 changed files with 3831 additions and 2737 deletions

View File

@@ -1,15 +1,25 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import type { NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import type { CreateQuestionGuideParams } from '@/global/core/ai/api.d';
import { pushQuestionGuideUsage } from '@/service/support/wallet/usage/push';
import { createQuestionGuide } from '@fastgpt/service/core/ai/functions/createQuestionGuide';
import { authChatCert } from '@/service/support/permission/auth/chat';
import { ApiRequestProps } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry';
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
import { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
async function handler(
req: ApiRequestProps<
OutLinkChatAuthProps & {
messages: ChatCompletionMessageParam[];
}
>,
res: NextApiResponse<any>
) {
try {
await connectToDatabase();
const { messages } = req.body as CreateQuestionGuideParams;
const { messages } = req.body;
const { tmbId, teamId } = await authChatCert({
req,
@@ -40,3 +50,5 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
});
}
}
export default NextAPI(handler);

View File

@@ -0,0 +1,47 @@
import type { NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import type { CreateQuestionGuideParams } from '@/global/core/ai/api.d';
import { pushQuestionGuideUsage } from '@/service/support/wallet/usage/push';
import { createQuestionGuide } from '@fastgpt/service/core/ai/functions/createQuestionGuide';
import { authChatCrud } from '@/service/support/permission/auth/chat';
import { ApiRequestProps } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry';
async function handler(req: ApiRequestProps<CreateQuestionGuideParams>, res: NextApiResponse<any>) {
try {
await connectToDatabase();
const { messages } = req.body;
const { tmbId, teamId } = await authChatCrud({
req,
authToken: true,
authApiKey: true,
...req.body
});
const qgModel = global.llmModels[0];
const { result, tokens } = await createQuestionGuide({
messages,
model: qgModel.model
});
jsonRes(res, {
data: result
});
pushQuestionGuideUsage({
tokens,
teamId,
tmbId
});
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
}
}
export default NextAPI(handler);

View File

@@ -43,10 +43,10 @@ async function handler(
});
return { id: appId };
} else {
await MongoApp.findByIdAndUpdate(appId, { type: AppTypeEnum.workflow });
}
await MongoApp.findByIdAndUpdate(appId, { type: AppTypeEnum.workflow });
return {};
}

View File

@@ -8,17 +8,14 @@ import { beforeUpdateAppFormat } from '@fastgpt/service/core/app/controller';
import { getNextTimeByCronStringAndTimezone } from '@fastgpt/global/common/string/time';
import { PostPublishAppProps } from '@/global/core/app/api';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { ApiRequestProps } from '@fastgpt/service/type/next';
async function handler(req: NextApiRequest, res: NextApiResponse<any>): Promise<{}> {
async function handler(
req: ApiRequestProps<PostPublishAppProps>,
res: NextApiResponse<any>
): Promise<{}> {
const { appId } = req.query as { appId: string };
const {
nodes = [],
edges = [],
chatConfig,
type,
isPublish,
versionName
} = req.body as PostPublishAppProps;
const { nodes = [], edges = [], chatConfig, isPublish, versionName } = req.body;
const { tmbId } = await authApp({ appId, req, per: WritePermissionVal, authToken: true });
@@ -50,11 +47,13 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>): Promise<
chatConfig,
updateTime: new Date(),
version: 'v2',
type,
scheduledTriggerConfig: chatConfig?.scheduledTriggerConfig,
scheduledTriggerNextTime: chatConfig?.scheduledTriggerConfig?.cronString
? getNextTimeByCronStringAndTimezone(chatConfig.scheduledTriggerConfig)
: null,
// 只有发布才会更新定时器
...(isPublish && {
scheduledTriggerConfig: chatConfig?.scheduledTriggerConfig,
scheduledTriggerNextTime: chatConfig?.scheduledTriggerConfig?.cronString
? getNextTimeByCronStringAndTimezone(chatConfig.scheduledTriggerConfig)
: null
}),
'pluginData.nodeVersion': _id
},
{

View File

@@ -1,16 +1,22 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { sseErrRes } from '@fastgpt/service/common/response';
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
import {
DispatchNodeResponseKeyEnum,
SseResponseEventEnum
} from '@fastgpt/global/core/workflow/runtime/constants';
import { responseWrite } from '@fastgpt/service/common/response';
import { pushChatUsage } from '@/service/support/wallet/usage/push';
import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants';
import type { UserChatItemType } from '@fastgpt/global/core/chat/type';
import type { AIChatItemType, UserChatItemType } from '@fastgpt/global/core/chat/type';
import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { dispatchWorkFlow } from '@fastgpt/service/core/workflow/dispatch';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { getUserChatInfoAndAuthTeamPoints } from '@/service/support/permission/auth/team';
import { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
import { removeEmptyUserInput } from '@fastgpt/global/core/chat/utils';
import {
concatHistories,
getChatTitleFromChatMessage,
removeEmptyUserInput
} from '@fastgpt/global/core/chat/utils';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import {
@@ -18,27 +24,37 @@ import {
updatePluginInputByVariables
} from '@fastgpt/global/core/workflow/utils';
import { NextAPI } from '@/service/middleware/entry';
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
import { chatValue2RuntimePrompt, GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
import { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type';
import { AppChatConfigType } from '@fastgpt/global/core/app/type';
import {
getLastInteractiveValue,
getMaxHistoryLimitFromNodes,
getWorkflowEntryNodeIds,
initWorkflowEdgeStatus,
rewriteNodeOutputByHistories,
storeNodes2RuntimeNodes
storeNodes2RuntimeNodes,
textAdaptGptResponse
} from '@fastgpt/global/core/workflow/runtime/utils';
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
import { getWorkflowResponseWrite } from '@fastgpt/service/core/workflow/dispatch/utils';
import { WORKFLOW_MAX_RUN_TIMES } from '@fastgpt/service/core/workflow/constants';
import { getPluginInputsFromStoreNodes } from '@fastgpt/global/core/app/plugin/utils';
import { getChatItems } from '@fastgpt/service/core/chat/controller';
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
import { getSystemTime } from '@fastgpt/global/common/time/timezone';
import { ChatRoleEnum, ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
import { saveChat, updateInteractiveChat } from '@fastgpt/service/core/chat/saveChat';
export type Props = {
messages: ChatCompletionMessageParam[];
responseChatItemId: string;
nodes: StoreNodeItemType[];
edges: StoreEdgeItemType[];
variables: Record<string, any>;
appId: string;
appName: string;
chatId: string;
chatConfig: AppChatConfigType;
};
@@ -55,10 +71,12 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
nodes = [],
edges = [],
messages = [],
responseChatItemId,
variables = {},
appName,
appId,
chatConfig
chatConfig,
chatId
} = req.body as Props;
try {
if (!Array.isArray(nodes)) {
@@ -71,15 +89,12 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
// console.log(JSON.stringify(chatMessages, null, 2), '====', chatMessages.length);
/* user auth */
const [{ app }, { teamId, tmbId }] = await Promise.all([
authApp({ req, authToken: true, appId, per: ReadPermissionVal }),
authCert({
req,
authToken: true
})
]);
// auth balance
const { user } = await getUserChatInfoAndAuthTeamPoints(tmbId);
const { app, teamId, tmbId } = await authApp({
req,
authToken: true,
appId,
per: ReadPermissionVal
});
const isPlugin = app.type === AppTypeEnum.plugin;
@@ -99,48 +114,79 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
return latestHumanChat;
})();
let runtimeNodes = storeNodes2RuntimeNodes(nodes, getWorkflowEntryNodeIds(nodes, chatMessages));
const limit = getMaxHistoryLimitFromNodes(nodes);
const [{ histories }, chatDetail, { user }] = await Promise.all([
getChatItems({
appId,
chatId,
offset: 0,
limit,
field: `dataId obj value nodeOutputs`
}),
MongoChat.findOne({ appId: app._id, chatId }, 'source variableList variables'),
// auth balance
getUserChatInfoAndAuthTeamPoints(tmbId)
]);
// Plugin need to replace inputs
if (chatDetail?.variables) {
variables = {
...chatDetail.variables,
...variables
};
}
const newHistories = concatHistories(histories, chatMessages);
// Get runtimeNodes
let runtimeNodes = storeNodes2RuntimeNodes(nodes, getWorkflowEntryNodeIds(nodes, newHistories));
if (isPlugin) {
runtimeNodes = updatePluginInputByVariables(runtimeNodes, variables);
variables = {};
} else {
if (!userQuestion.value) {
throw new Error('Params Error');
}
}
runtimeNodes = rewriteNodeOutputByHistories(newHistories, runtimeNodes);
runtimeNodes = rewriteNodeOutputByHistories(chatMessages, runtimeNodes);
const workflowResponseWrite = getWorkflowResponseWrite({
res,
detail: true,
streamResponse: true
streamResponse: true,
id: chatId,
showNodeStatus: true
});
/* start process */
const { flowResponses, flowUsages } = await dispatchWorkFlow({
const { flowResponses, assistantResponses, newVariables, flowUsages } = await dispatchWorkFlow({
res,
requestOrigin: req.headers.origin,
mode: 'test',
user,
uid: tmbId,
runningAppInfo: {
id: appId,
teamId,
tmbId
},
uid: tmbId,
user,
chatId,
responseChatItemId,
runtimeNodes,
runtimeEdges: initWorkflowEdgeStatus(edges, chatMessages),
runtimeEdges: initWorkflowEdgeStatus(edges, newHistories),
variables,
query: removeEmptyUserInput(userQuestion.value),
chatConfig,
histories: chatMessages,
histories: newHistories,
stream: true,
maxRunTimes: WORKFLOW_MAX_RUN_TIMES,
workflowStreamResponse: workflowResponseWrite
});
workflowResponseWrite({
event: SseResponseEventEnum.answer,
data: textAdaptGptResponse({
text: null,
finish_reason: 'stop'
})
});
responseWrite({
res,
event: SseResponseEventEnum.answer,
@@ -152,7 +198,44 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
data: JSON.stringify(flowResponses)
});
res.end();
// save chat
const isInteractiveRequest = !!getLastInteractiveValue(histories);
const { text: userInteractiveVal } = chatValue2RuntimePrompt(userQuestion.value);
const newTitle = isPlugin
? variables.cTime ?? getSystemTime(user.timezone)
: getChatTitleFromChatMessage(userQuestion);
const aiResponse: AIChatItemType & { dataId?: string } = {
dataId: responseChatItemId,
obj: ChatRoleEnum.AI,
value: assistantResponses,
[DispatchNodeResponseKeyEnum.nodeResponse]: flowResponses
};
if (isInteractiveRequest) {
await updateInteractiveChat({
chatId,
appId: app._id,
userInteractiveVal,
aiResponse,
newVariables
});
} else {
await saveChat({
chatId,
appId: app._id,
teamId,
tmbId: tmbId,
nodes,
appChatConfig: chatConfig,
variables: newVariables,
isUpdateUseTime: false, // owner update use time
newTitle,
source: ChatSourceEnum.test,
content: [userQuestion, aiResponse]
});
}
pushChatUsage({
appName,
@@ -165,8 +248,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
} catch (err: any) {
res.status(500);
sseErrRes(res, err);
res.end();
}
res.end();
}
export default NextAPI(handler);

View File

@@ -1,44 +1,48 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import type { NextApiResponse } from 'next';
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
import { ClearHistoriesProps } from '@/global/core/chat/api';
import { authOutLink } from '@/service/support/permission/auth/outLink';
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
import { authTeamSpaceToken } from '@/service/support/permission/auth/team';
import { NextAPI } from '@/service/middleware/entry';
import { deleteChatFiles } from '@fastgpt/service/core/chat/controller';
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
import { ApiRequestProps } from '@fastgpt/service/type/next';
import { authChatCrud } from '@/service/support/permission/auth/chat';
/* clear chat history */
async function handler(req: NextApiRequest, res: NextApiResponse) {
const { appId, shareId, outLinkUid, teamId, teamToken } = req.query as ClearHistoriesProps;
async function handler(req: ApiRequestProps<{}, ClearHistoriesProps>, res: NextApiResponse) {
const { appId, shareId, outLinkUid, teamId, teamToken } = req.query;
let chatAppId = appId!;
const {
teamId: chatTeamId,
tmbId,
uid,
authType
} = await authChatCrud({
req,
authToken: true,
authApiKey: true,
...req.query
});
const match = await (async () => {
if (shareId && outLinkUid) {
const { appId, uid } = await authOutLink({ shareId, outLinkUid });
chatAppId = appId;
if (shareId && outLinkUid && authType === 'outLink') {
return {
shareId,
outLinkUid: uid
};
}
if (teamId && teamToken) {
const { uid } = await authTeamSpaceToken({ teamId, teamToken });
return {
teamId,
teamId: chatTeamId,
appId,
outLinkUid: uid
};
}
if (appId) {
const { tmbId } = await authCert({ req, authToken: true, authApiKey: true });
if (teamId && teamToken && authType === 'teamDomain') {
return {
teamId: chatTeamId,
appId,
outLinkUid: uid
};
}
if (authType === 'token') {
return {
teamId: chatTeamId,
tmbId,
appId,
source: ChatSourceEnum.online
@@ -54,24 +58,22 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
await deleteChatFiles({ chatIdList: idList });
await mongoSessionRun(async (session) => {
return mongoSessionRun(async (session) => {
await MongoChatItem.deleteMany(
{
appId: chatAppId,
appId,
chatId: { $in: idList }
},
{ session }
);
await MongoChat.deleteMany(
{
appId: chatAppId,
appId,
chatId: { $in: idList }
},
{ session }
);
});
jsonRes(res);
}
export default NextAPI(handler);

View File

@@ -7,7 +7,6 @@ import { authChatCrud } from '@/service/support/permission/auth/chat';
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
import { NextAPI } from '@/service/middleware/entry';
import { ApiRequestProps } from '@fastgpt/service/type/next';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { deleteChatFiles } from '@fastgpt/service/core/chat/controller';
/* clear chat history */
@@ -18,8 +17,7 @@ async function handler(req: ApiRequestProps<{}, DelHistoryProps>, res: NextApiRe
req,
authToken: true,
authApiKey: true,
...req.query,
per: WritePermissionVal
...req.query
});
await deleteChatFiles({ chatIdList: [chatId] });

View File

@@ -4,7 +4,6 @@ import { connectToDatabase } from '@/service/mongo';
import type { AdminUpdateFeedbackParams } from '@/global/core/chat/api.d';
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
import { authChatCrud } from '@/service/support/permission/auth/chat';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
/* 初始化我的聊天框,需要身份验证 */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
@@ -20,9 +19,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
await authChatCrud({
req,
authToken: true,
authApiKey: true,
appId,
chatId,
per: ReadPermissionVal
chatId
});
await MongoChatItem.findOneAndUpdate(

View File

@@ -5,7 +5,6 @@ import { authCert } from '@fastgpt/service/support/permission/auth/common';
import type { CloseCustomFeedbackParams } from '@/global/core/chat/api.d';
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
import { authChatCrud } from '@/service/support/permission/auth/chat';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
/* remove custom feedback */
@@ -21,9 +20,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
await authChatCrud({
req,
authToken: true,
authApiKey: true,
appId,
chatId,
per: ReadPermissionVal
chatId
});
await authCert({ req, authToken: true });

View File

@@ -1,71 +1,42 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import type { NextApiResponse } from 'next';
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
import { UpdateChatFeedbackProps } from '@fastgpt/global/core/chat/api';
import { authChatCrud } from '@/service/support/permission/auth/chat';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { NextAPI } from '@/service/middleware/entry';
import { ApiRequestProps } from '@fastgpt/service/type/next';
/* 初始化我的聊天框,需要身份验证 */
async function handler(req: NextApiRequest, res: NextApiResponse) {
const {
appId,
chatId,
dataId,
shareId,
teamId,
teamToken,
outLinkUid,
userBadFeedback,
userGoodFeedback
} = req.body as UpdateChatFeedbackProps;
async function handler(req: ApiRequestProps<UpdateChatFeedbackProps>, res: NextApiResponse) {
const { appId, chatId, dataId, userBadFeedback, userGoodFeedback } = req.body;
try {
await connectToDatabase();
await authChatCrud({
req,
authToken: true,
authApiKey: true,
appId,
teamId,
teamToken,
chatId,
shareId,
outLinkUid,
per: ReadPermissionVal
});
if (!dataId) {
throw new Error('dataId is required');
}
await MongoChatItem.findOneAndUpdate(
{
appId,
chatId,
dataId
},
{
$unset: {
...(userBadFeedback === undefined && { userBadFeedback: '' }),
...(userGoodFeedback === undefined && { userGoodFeedback: '' })
},
$set: {
...(userBadFeedback !== undefined && { userBadFeedback }),
...(userGoodFeedback !== undefined && { userGoodFeedback })
}
}
);
jsonRes(res);
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
if (!chatId || !dataId) {
return Promise.reject('chatId or dataId is empty');
}
await authChatCrud({
req,
authToken: true,
authApiKey: true,
...req.body
});
await MongoChatItem.findOneAndUpdate(
{
appId,
chatId,
dataId
},
{
$unset: {
...(userBadFeedback === undefined && { userBadFeedback: '' }),
...(userGoodFeedback === undefined && { userGoodFeedback: '' })
},
$set: {
...(userBadFeedback !== undefined && { userBadFeedback }),
...(userGoodFeedback !== undefined && { userGoodFeedback })
}
}
);
}
export default NextAPI(handler);

View File

@@ -7,6 +7,8 @@ import { NextAPI } from '@/service/middleware/entry';
import { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type';
import { GetHistoriesProps } from '@/global/core/chat/api';
import { addMonths } from 'date-fns';
export type getHistoriesQuery = {};
export type getHistoriesBody = PaginationProps<GetHistoriesProps>;
@@ -17,8 +19,7 @@ async function handler(
req: ApiRequestProps<getHistoriesBody, getHistoriesQuery>,
res: ApiResponseType<any>
): Promise<PaginationResponse<getHistoriesResponse>> {
const { appId, shareId, outLinkUid, teamId, teamToken, offset, pageSize, source } =
req.body as getHistoriesBody;
const { appId, shareId, outLinkUid, teamId, teamToken, offset, pageSize, source } = req.body;
const match = await (async () => {
if (shareId && outLinkUid) {
@@ -28,7 +29,7 @@ async function handler(
shareId,
outLinkUid: uid,
updateTime: {
$gte: new Date(new Date().setDate(new Date().getDate() - 30))
$gte: addMonths(new Date(), -1)
}
};
}
@@ -62,7 +63,8 @@ async function handler(
await MongoChat.find(match, 'chatId title top customTitle appId updateTime')
.sort({ top: -1, updateTime: -1 })
.skip(offset)
.limit(pageSize),
.limit(pageSize)
.lean(),
MongoChat.countDocuments(match)
]);

View File

@@ -1,7 +1,6 @@
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry';
import { GetChatRecordsProps } from '@/global/core/chat/api';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
import { transformPreviewHistories } from '@/global/core/chat/utils';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
@@ -11,7 +10,6 @@ import { MongoApp } from '@fastgpt/service/core/app/schema';
import { AppErrEnum } from '@fastgpt/global/common/error/code/app';
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import { filterPublicNodeResponseData } from '@fastgpt/global/core/chat/utils';
import { authOutLink } from '@/service/support/permission/auth/outLink';
import { GetChatTypeEnum } from '@/global/core/chat/constants';
import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type';
import { ChatItemType } from '@fastgpt/global/core/chat/type';
@@ -42,14 +40,13 @@ async function handler(
};
}
const [app] = await Promise.all([
const [app, { responseDetail, showNodeStatus, authType }] = await Promise.all([
MongoApp.findById(appId, 'type').lean(),
authChatCrud({
req,
authToken: true,
authApiKey: true,
...req.body,
per: ReadPermissionVal
...req.body
})
]);
@@ -57,21 +54,14 @@ async function handler(
return Promise.reject(AppErrEnum.unExist);
}
const isPlugin = app.type === AppTypeEnum.plugin;
const shareChat = await (async () => {
if (type === GetChatTypeEnum.outLink)
return await authOutLink({
shareId: req.body.shareId,
outLinkUid: req.body.outLinkUid
}).then((result) => result.shareChat);
})();
const isOutLink = authType === GetChatTypeEnum.outLink;
const fieldMap = {
[GetChatTypeEnum.normal]: `dataId obj value adminFeedback userBadFeedback userGoodFeedback time ${
[GetChatTypeEnum.normal]: `dataId obj value adminFeedback userBadFeedback userGoodFeedback time hideInUI ${
DispatchNodeResponseKeyEnum.nodeResponse
} ${loadCustomFeedbacks ? 'customFeedbacks' : ''}`,
[GetChatTypeEnum.outLink]: `dataId obj value userGoodFeedback userBadFeedback adminFeedback time ${DispatchNodeResponseKeyEnum.nodeResponse}`,
[GetChatTypeEnum.team]: `dataId obj value userGoodFeedback userBadFeedback adminFeedback time ${DispatchNodeResponseKeyEnum.nodeResponse}`
[GetChatTypeEnum.outLink]: `dataId obj value userGoodFeedback userBadFeedback adminFeedback time hideInUI ${DispatchNodeResponseKeyEnum.nodeResponse}`,
[GetChatTypeEnum.team]: `dataId obj value userGoodFeedback userBadFeedback adminFeedback time hideInUI ${DispatchNodeResponseKeyEnum.nodeResponse}`
};
const { total, histories } = await getChatItems({
@@ -82,10 +72,8 @@ async function handler(
limit: pageSize
});
const responseDetail = !shareChat || shareChat.responseDetail;
// Remove important information
if (shareChat && app.type !== AppTypeEnum.plugin) {
if (isOutLink && app.type !== AppTypeEnum.plugin) {
histories.forEach((item) => {
if (item.obj === ChatRoleEnum.AI) {
item.responseData = filterPublicNodeResponseData({
@@ -93,7 +81,7 @@ async function handler(
responseDetail
});
if (shareChat.showNodeStatus === false) {
if (showNodeStatus === false) {
item.value = item.value.filter((v) => v.type !== ChatItemValueTypeEnum.tool);
}
}

View File

@@ -1,17 +1,11 @@
import { authChatCrud } from '@/service/support/permission/auth/chat';
import {
ManagePermissionVal,
ReadPermissionVal
} from '@fastgpt/global/support/permission/constant';
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry';
import { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type';
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { filterPublicNodeResponseData } from '@fastgpt/global/core/chat/utils';
import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
export type getResDataQuery = OutLinkChatAuthProps & {
chatId?: string;
@@ -32,29 +26,13 @@ async function handler(
return {};
}
// 1. Un login api: share chat, team chat
// 2. Login api: account chat, chat log
const authData = await (() => {
try {
return authChatCrud({
req,
authToken: true,
authApiKey: true,
...req.query,
per: ReadPermissionVal
});
} catch (error) {
return authApp({
req,
authToken: true,
authApiKey: true,
appId,
per: ManagePermissionVal
});
}
})();
const [chatData] = await Promise.all([
const [{ responseDetail }, chatData] = await Promise.all([
authChatCrud({
req,
authToken: true,
authApiKey: true,
...req.query
}),
MongoChatItem.findOne(
{
appId,
@@ -62,8 +40,7 @@ async function handler(
dataId
},
'obj responseData'
).lean(),
shareId ? MongoOutLink.findOne({ shareId }).lean() : Promise.resolve(null)
).lean()
]);
if (chatData?.obj !== ChatRoleEnum.AI) {
@@ -73,8 +50,7 @@ async function handler(
const flowResponses = chatData.responseData ?? {};
return req.query.shareId
? filterPublicNodeResponseData({
// @ts-ignore
responseDetail: authData.responseDetail,
responseDetail,
flowResponses: chatData.responseData
})
: flowResponses;

View File

@@ -3,7 +3,7 @@ import { MongoChatInputGuide } from '@fastgpt/service/core/chat/inputGuide/schem
import { NextAPI } from '@/service/middleware/entry';
import { ApiRequestProps } from '@fastgpt/service/type/next';
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
import { authChatCert } from '@/service/support/permission/auth/chat';
import { authChatCrud } from '@/service/support/permission/auth/chat';
import { MongoApp } from '@fastgpt/service/core/app/schema';
import { AppErrEnum } from '@fastgpt/global/common/error/code/app';
import { replaceRegChars } from '@fastgpt/global/common/string/tools';
@@ -21,7 +21,7 @@ async function handler(
const { appId, searchKey } = req.body;
// tmp auth
const { teamId } = await authChatCert({ req, authToken: true });
const { teamId } = await authChatCrud({ req, authToken: true, ...req.body });
const app = await MongoApp.findOne({ _id: appId, teamId });
if (!app) {
return Promise.reject(AppErrEnum.unAuthApp);

View File

@@ -5,21 +5,19 @@ import { authChatCrud } from '@/service/support/permission/auth/chat';
import type { DeleteChatItemProps } from '@/global/core/chat/api.d';
import { NextAPI } from '@/service/middleware/entry';
import { ApiRequestProps } from '@fastgpt/service/type/next';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
async function handler(req: ApiRequestProps<{}, DeleteChatItemProps>, res: NextApiResponse) {
const { appId, chatId, contentId } = req.query;
if (!contentId || !chatId) {
return jsonRes(res);
return Promise.reject('contentId or chatId is empty');
}
await authChatCrud({
req,
authToken: true,
authApiKey: true,
...req.query,
per: WritePermissionVal
...req.query
});
await MongoChatItem.deleteOne({
@@ -28,7 +26,7 @@ async function handler(req: ApiRequestProps<{}, DeleteChatItemProps>, res: NextA
dataId: contentId
});
jsonRes(res);
return;
}
export default NextAPI(handler);

View File

@@ -1,33 +1,35 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import type { NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import { GetChatSpeechProps } from '@/global/core/chat/api.d';
import { text2Speech } from '@fastgpt/service/core/ai/audio/speech';
import { pushAudioSpeechUsage } from '@/service/support/wallet/usage/push';
import { authChatCert } from '@/service/support/permission/auth/chat';
import { authChatCrud } from '@/service/support/permission/auth/chat';
import { authType2UsageSource } from '@/service/support/wallet/usage/utils';
import { getAudioSpeechModel } from '@fastgpt/service/core/ai/model';
import { MongoTTSBuffer } from '@fastgpt/service/common/buffer/tts/schema';
import { NextAPI } from '@/service/middleware/entry';
import { ApiRequestProps } from '@fastgpt/service/type/next';
/*
1. get tts from chatItem store
2. get tts from ai
4. push bill
*/
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
async function handler(req: ApiRequestProps<GetChatSpeechProps>, res: NextApiResponse) {
try {
await connectToDatabase();
const { ttsConfig, input } = req.body as GetChatSpeechProps;
const { ttsConfig, input } = req.body;
if (!ttsConfig.model || !ttsConfig.voice) {
throw new Error('model or voice not found');
}
const { teamId, tmbId, authType } = await authChatCert({
const { teamId, tmbId, authType } = await authChatCrud({
req,
authToken: true,
authApiKey: true
authApiKey: true,
...req.body
});
const ttsModel = getAudioSpeechModel(ttsConfig.model);
@@ -90,3 +92,5 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
});
}
}
export default NextAPI(handler);

View File

@@ -16,11 +16,11 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
let { chatId, shareId, outLinkUid } = req.query as InitOutLinkChatProps;
// auth link permission
const { shareChat, uid, appId } = await authOutLink({ shareId, outLinkUid });
const { outLinkConfig, uid, appId } = await authOutLink({ shareId, outLinkUid });
// auth app permission
const [tmb, chat, app] = await Promise.all([
MongoTeamMember.findById(shareChat.tmbId, '_id userId').populate('userId', 'avatar').lean(),
MongoTeamMember.findById(outLinkConfig.tmbId, '_id userId').populate('userId', 'avatar').lean(),
MongoChat.findOne({ appId, chatId, shareId }).lean(),
MongoApp.findById(appId).lean()
]);
@@ -35,7 +35,6 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
}
const { nodes, chatConfig } = await getAppLatestVersion(app._id, app);
// pick share response field
jsonRes<InitChatResponse>(res, {
data: {
@@ -66,9 +65,3 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
}
export default NextAPI(handler);
export const config = {
api: {
responseLimit: '10mb'
}
};

View File

@@ -1,4 +1,4 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import type { NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { getGuideModule, getAppChatConfig } from '@fastgpt/global/core/workflow/utils';
import { getChatModelNameListByModules } from '@/service/core/app/workflow';
@@ -12,12 +12,13 @@ import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
import { getAppLatestVersion } from '@fastgpt/service/core/app/version/controller';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { NextAPI } from '@/service/middleware/entry';
import { ApiRequestProps } from '@fastgpt/service/type/next';
async function handler(req: NextApiRequest, res: NextApiResponse) {
let { teamId, appId, chatId, teamToken } = req.query as InitTeamChatProps;
async function handler(req: ApiRequestProps<InitTeamChatProps>, res: NextApiResponse) {
let { teamId, appId, chatId, teamToken } = req.query;
if (!teamId || !appId || !teamToken) {
throw new Error('teamId, appId, teamToken are required');
return Promise.reject('teamId, appId, teamToken are required');
}
const { uid } = await authTeamSpaceToken({
@@ -32,7 +33,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
]);
if (!app) {
throw new Error(AppErrEnum.unExist);
return Promise.reject(AppErrEnum.unExist);
}
// auth chat permission
@@ -43,8 +44,6 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
// get app and history
const { nodes, chatConfig } = await getAppLatestVersion(app._id, app);
// pick share response field
jsonRes<InitChatResponse>(res, {
data: {
chatId,
@@ -74,9 +73,3 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
}
export default NextAPI(handler);
export const config = {
api: {
responseLimit: '10mb'
}
};

View File

@@ -77,7 +77,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>): CreateCo
// 2. upload file
const fileId = await uploadFile({
teamId,
tmbId,
uid: tmbId,
bucketName,
path: file.path,
filename: file.originalname,

View File

@@ -5,40 +5,142 @@ import { DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constant
import { createFileToken } from '@fastgpt/service/support/permission/controller';
import { BucketNameEnum, ReadFileBaseUrl } from '@fastgpt/global/common/file/constants';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { ShareChatAuthProps } from '@fastgpt/global/support/permission/chat';
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset';
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
import { AIChatItemType, ChatHistoryItemResType } from '@fastgpt/global/core/chat/type';
import { authChatCrud } from '@/service/support/permission/auth/chat';
import { getCollectionWithDataset } from '@fastgpt/service/core/dataset/controller';
export type readCollectionSourceQuery = {};
export type readCollectionSourceBody = {
collectionId: string;
} & ShareChatAuthProps;
appId?: string;
chatId?: string;
chatItemId?: string;
} & OutLinkChatAuthProps;
export type readCollectionSourceResponse = {
type: 'url';
value: string;
};
const authCollectionInChat = async ({
collectionId,
appId,
chatId,
chatItemId
}: {
collectionId: string;
appId: string;
chatId: string;
chatItemId: string;
}) => {
try {
const chatItem = (await MongoChatItem.findOne(
{
appId,
chatId,
dataId: chatItemId
},
'responseData'
).lean()) as AIChatItemType;
if (!chatItem) return Promise.reject(DatasetErrEnum.unAuthDatasetFile);
// 找 responseData 里,是否有该文档 id
const responseData = chatItem.responseData || [];
const flatResData: ChatHistoryItemResType[] =
responseData
?.map((item) => {
return [
item,
...(item.pluginDetail || []),
...(item.toolDetail || []),
...(item.loopDetail || [])
];
})
.flat() || [];
if (
flatResData.some((item) => {
if (item.quoteList) {
return item.quoteList.some((quote) => quote.collectionId === collectionId);
}
return false;
})
) {
return true;
}
} catch (error) {}
return Promise.reject(DatasetErrEnum.unAuthDatasetFile);
};
async function handler(
req: ApiRequestProps<readCollectionSourceBody, readCollectionSourceQuery>
): Promise<readCollectionSourceResponse> {
const { collection, teamId, tmbId } = await authDatasetCollection({
req,
authToken: true,
authApiKey: true,
collectionId: req.body.collectionId,
per: ReadPermissionVal
});
const { collectionId, appId, chatId, chatItemId, shareId, outLinkUid, teamId, teamToken } =
req.body;
const {
collection,
teamId: userTeamId,
tmbId: uid,
authType
} = await (async () => {
if (!appId || !chatId || !chatItemId) {
return authDatasetCollection({
req,
authToken: true,
authApiKey: true,
collectionId: req.body.collectionId,
per: ReadPermissionVal
});
}
/*
1. auth chat read permission
2. auth collection quote in chat
3. auth outlink open show quote
*/
const [authRes, collection] = await Promise.all([
authChatCrud({
req,
authToken: true,
appId,
chatId,
shareId,
outLinkUid,
teamId,
teamToken
}),
getCollectionWithDataset(collectionId),
authCollectionInChat({ appId, chatId, chatItemId, collectionId })
]);
if (!authRes.showRawSource) {
return Promise.reject(DatasetErrEnum.unAuthDatasetFile);
}
return {
...authRes,
collection
};
})();
const sourceUrl = await (async () => {
if (collection.type === DatasetCollectionTypeEnum.file && collection.fileId) {
const token = await createFileToken({
bucketName: BucketNameEnum.dataset,
teamId,
tmbId,
fileId: collection.fileId
teamId: userTeamId,
uid,
fileId: collection.fileId,
customExpireMinutes: authType === 'outLink' ? 5 : undefined
});
return `${ReadFileBaseUrl}?token=${token}`;
return `${ReadFileBaseUrl}/${collection.name}?token=${token}`;
}
if (collection.type === DatasetCollectionTypeEnum.link && collection.rawLink) {
return collection.rawLink;

View File

@@ -4,7 +4,7 @@ import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { NextAPI } from '@/service/middleware/entry';
import { ApiRequestProps } from '@fastgpt/service/type/next';
import { OwnerPermissionVal } from '@fastgpt/global/support/permission/constant';
import { authFile } from '@fastgpt/service/support/permission/auth/file';
import { authCollectionFile } from '@fastgpt/service/support/permission/auth/file';
export type PostPreviewFilesChunksProps = {
type: DatasetSourceReadTypeEnum;
@@ -35,7 +35,7 @@ async function handler(
const { teamId } = await (async () => {
if (type === DatasetSourceReadTypeEnum.fileLocal) {
return authFile({
return authCollectionFile({
req,
authToken: true,
authApiKey: true,