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:
@@ -2,7 +2,7 @@
|
||||
Read db file content and response 3000 words
|
||||
*/
|
||||
import type { NextApiResponse } from 'next';
|
||||
import { authFile } from '@fastgpt/service/support/permission/auth/file';
|
||||
import { authCollectionFile } from '@fastgpt/service/support/permission/auth/file';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import { DatasetSourceReadTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { readDatasetSourceRawText } from '@fastgpt/service/core/dataset/read';
|
||||
@@ -26,7 +26,7 @@ async function handler(req: ApiRequestProps<PreviewContextProps>, res: NextApiRe
|
||||
|
||||
const { teamId } = await (async () => {
|
||||
if (type === DatasetSourceReadTypeEnum.fileLocal) {
|
||||
return authFile({
|
||||
return authCollectionFile({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true,
|
||||
|
||||
@@ -9,7 +9,17 @@ import { ReadFileBaseUrl } from '@fastgpt/global/common/file/constants';
|
||||
import { addLog } from '@fastgpt/service/common/system/log';
|
||||
import { authFrequencyLimit } from '@/service/common/frequencyLimit/api';
|
||||
import { addSeconds } from 'date-fns';
|
||||
import { authChatCert } from '@/service/support/permission/auth/chat';
|
||||
import { authChatCrud } from '@/service/support/permission/auth/chat';
|
||||
import { authDataset } from '@fastgpt/service/support/permission/dataset/auth';
|
||||
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
||||
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
|
||||
export type UploadChatFileProps = {
|
||||
appId: string;
|
||||
} & OutLinkChatAuthProps;
|
||||
export type UploadDatasetFileProps = {
|
||||
datasetId: string;
|
||||
};
|
||||
|
||||
const authUploadLimit = (tmbId: string) => {
|
||||
if (!global.feConfigs.uploadFileMaxAmount) return;
|
||||
@@ -28,15 +38,43 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
const upload = getUploadModel({
|
||||
maxSize: global.feConfigs?.uploadFileMaxSize
|
||||
});
|
||||
const { file, bucketName, metadata } = await upload.doUpload(req, res);
|
||||
const { file, bucketName, metadata, data } = await upload.doUpload<
|
||||
UploadChatFileProps | UploadDatasetFileProps
|
||||
>(req, res);
|
||||
filePaths.push(file.path);
|
||||
const { teamId, tmbId, outLinkUid } = await authChatCert({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true
|
||||
});
|
||||
|
||||
await authUploadLimit(outLinkUid || tmbId);
|
||||
const { teamId, uid } = await (async () => {
|
||||
if (bucketName === 'chat') {
|
||||
const chatData = data as UploadChatFileProps;
|
||||
const authData = await authChatCrud({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true,
|
||||
...chatData
|
||||
});
|
||||
return {
|
||||
teamId: authData.teamId,
|
||||
uid: authData.uid
|
||||
};
|
||||
}
|
||||
if (bucketName === 'dataset') {
|
||||
const chatData = data as UploadDatasetFileProps;
|
||||
const authData = await authDataset({
|
||||
datasetId: chatData.datasetId,
|
||||
per: WritePermissionVal,
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true
|
||||
});
|
||||
return {
|
||||
teamId: authData.teamId,
|
||||
uid: authData.tmbId
|
||||
};
|
||||
}
|
||||
return Promise.reject('bucketName is empty');
|
||||
})();
|
||||
|
||||
await authUploadLimit(uid);
|
||||
|
||||
addLog.info(`Upload file success ${file.originalname}, cost ${Date.now() - start}ms`);
|
||||
|
||||
@@ -46,7 +84,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
|
||||
const fileId = await uploadFile({
|
||||
teamId,
|
||||
tmbId,
|
||||
uid,
|
||||
bucketName,
|
||||
path: file.path,
|
||||
filename: file.originalname,
|
||||
@@ -61,7 +99,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
previewUrl: `${ReadFileBaseUrl}/${file.originalname}?token=${await createFileToken({
|
||||
bucketName,
|
||||
teamId,
|
||||
tmbId,
|
||||
uid,
|
||||
fileId
|
||||
})}`
|
||||
}
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authChatCert } from '@/service/support/permission/auth/chat';
|
||||
import { uploadMongoImg } from '@fastgpt/service/common/file/image/controller';
|
||||
import { UploadImgProps } from '@fastgpt/global/common/file/api';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
|
||||
/*
|
||||
Upload avatar image
|
||||
*/
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const body = req.body as UploadImgProps;
|
||||
|
||||
const { teamId } = await authChatCert({ req, authToken: true });
|
||||
const { teamId } = await authCert({ req, authToken: true });
|
||||
|
||||
const imgId = await uploadMongoImg({
|
||||
teamId,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
@@ -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 {};
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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] });
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 });
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
]);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
|
||||
import type { OutLinkEditType } from '@fastgpt/global/support/outLink/type.d';
|
||||
import { authOutLinkCrud } from '@fastgpt/service/support/permission/publish/authLink';
|
||||
import { OwnerPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { ManagePermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import type { ApiRequestProps } from '@fastgpt/service/type/next';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||
@@ -30,7 +30,7 @@ async function handler(
|
||||
return Promise.reject(CommonErrEnum.missingParams);
|
||||
}
|
||||
|
||||
await authOutLinkCrud({ req, outLinkId: _id, authToken: true, per: OwnerPermissionVal });
|
||||
await authOutLinkCrud({ req, outLinkId: _id, authToken: true, per: ManagePermissionVal });
|
||||
|
||||
await MongoOutLink.findByIdAndUpdate(_id, {
|
||||
name,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { getUploadModel } from '@fastgpt/service/common/file/multer';
|
||||
import { removeFilesByPaths } from '@fastgpt/service/common/file/utils';
|
||||
import fs from 'fs';
|
||||
import { pushWhisperUsage } from '@/service/support/wallet/usage/push';
|
||||
import { authChatCert } from '@/service/support/permission/auth/chat';
|
||||
import { authChatCrud } from '@/service/support/permission/auth/chat';
|
||||
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import { aiTranscriptions } from '@fastgpt/service/core/ai/audio/transcriptions';
|
||||
@@ -27,6 +27,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
}
|
||||
>(req, res);
|
||||
|
||||
req.body.appId = appId;
|
||||
req.body.shareId = shareId;
|
||||
req.body.outLinkUid = outLinkUid;
|
||||
req.body.teamId = spaceTeamId;
|
||||
@@ -43,7 +44,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
}
|
||||
|
||||
// auth role
|
||||
const { teamId, tmbId } = await authChatCert({ req, authToken: true });
|
||||
const { teamId, tmbId } = await authChatCrud({ req, authToken: true, ...req.body });
|
||||
|
||||
// auth app
|
||||
// const app = await MongoApp.findById(appId, 'modules').lean();
|
||||
|
||||
@@ -86,7 +86,7 @@ type AuthResponseType = {
|
||||
showNodeStatus?: boolean;
|
||||
authType: `${AuthUserTypeEnum}`;
|
||||
apikey?: string;
|
||||
canWrite: boolean;
|
||||
responseAllData: boolean;
|
||||
outLinkUserId?: string;
|
||||
sourceName?: string;
|
||||
};
|
||||
@@ -160,7 +160,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
authType,
|
||||
sourceName,
|
||||
apikey,
|
||||
canWrite,
|
||||
responseAllData,
|
||||
outLinkUserId = customUid,
|
||||
showNodeStatus
|
||||
} = await (async () => {
|
||||
@@ -328,12 +328,9 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
await updateInteractiveChat({
|
||||
chatId,
|
||||
appId: app._id,
|
||||
teamId,
|
||||
tmbId: tmbId,
|
||||
userInteractiveVal,
|
||||
aiResponse,
|
||||
newVariables,
|
||||
newTitle
|
||||
newVariables
|
||||
});
|
||||
} else {
|
||||
await saveChat({
|
||||
@@ -361,7 +358,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
addLog.info(`completions running time: ${(Date.now() - startTime) / 1000}s`);
|
||||
|
||||
/* select fe response field */
|
||||
const feResponseData = canWrite
|
||||
const feResponseData = responseAllData
|
||||
? flowResponses
|
||||
: filterPublicNodeResponseData({ flowResponses, responseDetail });
|
||||
|
||||
@@ -482,10 +479,10 @@ const authShareChat = async ({
|
||||
tmbId,
|
||||
user,
|
||||
app,
|
||||
responseDetail,
|
||||
apikey: '',
|
||||
authType,
|
||||
canWrite: false,
|
||||
responseAllData: false,
|
||||
responseDetail,
|
||||
outLinkUserId: uid,
|
||||
showNodeStatus
|
||||
};
|
||||
@@ -525,10 +522,10 @@ const authTeamSpaceChat = async ({
|
||||
tmbId: app.tmbId,
|
||||
user,
|
||||
app,
|
||||
responseDetail: true,
|
||||
authType: AuthUserTypeEnum.outLink,
|
||||
apikey: '',
|
||||
canWrite: false,
|
||||
responseAllData: false,
|
||||
responseDetail: true,
|
||||
outLinkUserId: uid
|
||||
};
|
||||
};
|
||||
@@ -610,11 +607,11 @@ const authHeaderRequest = async ({
|
||||
tmbId,
|
||||
user,
|
||||
app,
|
||||
responseDetail: true,
|
||||
apikey,
|
||||
authType,
|
||||
sourceName,
|
||||
canWrite: true
|
||||
responseAllData: true,
|
||||
responseDetail: true
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { Flex, Box, useTheme } from '@chakra-ui/react';
|
||||
import { Flex, Box } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { HUMAN_ICON } from '@fastgpt/global/common/system/constants';
|
||||
import { getInitChatInfo } from '@/web/core/chat/api';
|
||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import { useChat } from '@/components/core/chat/ChatContainer/useChat';
|
||||
|
||||
import dynamic from 'next/dynamic';
|
||||
import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
|
||||
@@ -14,53 +12,52 @@ import { PluginRunBoxTabEnum } from '@/components/core/chat/ChatContainer/Plugin
|
||||
import CloseIcon from '@fastgpt/web/components/common/Icon/close';
|
||||
import ChatBox from '@/components/core/chat/ChatContainer/ChatBox';
|
||||
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { PcHeader } from '@/pages/chat/components/ChatHeader';
|
||||
import { GetChatTypeEnum } from '@/global/core/chat/constants';
|
||||
import ChatItemContextProvider, { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
|
||||
import ChatRecordContextProvider, {
|
||||
ChatRecordContext
|
||||
} from '@/web/core/chat/context/chatRecordContext';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
|
||||
const PluginRunBox = dynamic(() => import('@/components/core/chat/ChatContainer/PluginRunBox'));
|
||||
|
||||
const DetailLogsModal = ({
|
||||
appId,
|
||||
chatId,
|
||||
onClose
|
||||
}: {
|
||||
type Props = {
|
||||
appId: string;
|
||||
chatId: string;
|
||||
onClose: () => void;
|
||||
}) => {
|
||||
};
|
||||
|
||||
const DetailLogsModal = ({ appId, chatId, onClose }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const { isPc } = useSystem();
|
||||
const theme = useTheme();
|
||||
|
||||
const params = useMemo(() => {
|
||||
return {
|
||||
chatId,
|
||||
appId,
|
||||
loadCustomFeedbacks: true,
|
||||
type: GetChatTypeEnum.normal
|
||||
};
|
||||
}, [appId, chatId]);
|
||||
const {
|
||||
ChatBoxRef,
|
||||
variablesForm,
|
||||
pluginRunTab,
|
||||
setPluginRunTab,
|
||||
resetVariables,
|
||||
chatRecords,
|
||||
ScrollData,
|
||||
setChatRecords,
|
||||
totalRecordsCount
|
||||
} = useChat(params);
|
||||
const resetVariables = useContextSelector(ChatItemContext, (v) => v.resetVariables);
|
||||
const setChatBoxData = useContextSelector(ChatItemContext, (v) => v.setChatBoxData);
|
||||
const pluginRunTab = useContextSelector(ChatItemContext, (v) => v.pluginRunTab);
|
||||
const setPluginRunTab = useContextSelector(ChatItemContext, (v) => v.setPluginRunTab);
|
||||
|
||||
const { data: chat, isFetching } = useQuery(
|
||||
['getChatDetail', chatId],
|
||||
() => getInitChatInfo({ appId, chatId, loadCustomFeedbacks: true }),
|
||||
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
|
||||
const totalRecordsCount = useContextSelector(ChatRecordContext, (v) => v.totalRecordsCount);
|
||||
|
||||
const { data: chat, loading: isFetching } = useRequest2(
|
||||
async () => {
|
||||
const res = await getInitChatInfo({ appId, chatId, loadCustomFeedbacks: true });
|
||||
res.userAvatar = HUMAN_ICON;
|
||||
|
||||
setChatBoxData(res);
|
||||
resetVariables({
|
||||
variables: res.variables
|
||||
});
|
||||
|
||||
return res;
|
||||
},
|
||||
{
|
||||
onSuccess(res) {
|
||||
resetVariables({
|
||||
variables: res.variables
|
||||
});
|
||||
manual: false,
|
||||
refreshDeps: [chatId],
|
||||
onError(e) {
|
||||
onClose();
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -68,12 +65,11 @@ const DetailLogsModal = ({
|
||||
const title = chat?.title;
|
||||
const chatModels = chat?.app?.chatModels;
|
||||
const isPlugin = chat?.app.type === AppTypeEnum.plugin;
|
||||
const loading = isFetching;
|
||||
|
||||
return (
|
||||
<>
|
||||
<MyBox
|
||||
isLoading={loading}
|
||||
isLoading={isFetching}
|
||||
display={'flex'}
|
||||
flexDirection={'column'}
|
||||
zIndex={3}
|
||||
@@ -124,7 +120,7 @@ const DetailLogsModal = ({
|
||||
alignItems={'center'}
|
||||
px={[3, 5]}
|
||||
h={['46px', '60px']}
|
||||
borderBottom={theme.borders.base}
|
||||
borderBottom={'base'}
|
||||
borderBottomColor={'gray.200'}
|
||||
color={'myGray.900'}
|
||||
>
|
||||
@@ -154,32 +150,15 @@ const DetailLogsModal = ({
|
||||
<Box pt={2} flex={'1 0 0'}>
|
||||
{isPlugin ? (
|
||||
<Box px={5} pt={2} h={'100%'}>
|
||||
<PluginRunBox
|
||||
chatConfig={chat?.app?.chatConfig}
|
||||
pluginInputs={chat?.app.pluginInputs}
|
||||
variablesForm={variablesForm}
|
||||
histories={chatRecords}
|
||||
setHistories={setChatRecords}
|
||||
appId={chat.appId}
|
||||
tab={pluginRunTab}
|
||||
setTab={setPluginRunTab}
|
||||
/>
|
||||
<PluginRunBox appId={appId} chatId={chatId} />
|
||||
</Box>
|
||||
) : (
|
||||
<ChatBox
|
||||
ScrollData={ScrollData}
|
||||
ref={ChatBoxRef}
|
||||
chatHistories={chatRecords}
|
||||
setChatHistories={setChatRecords}
|
||||
variablesForm={variablesForm}
|
||||
appAvatar={chat?.app.avatar}
|
||||
userAvatar={HUMAN_ICON}
|
||||
appId={appId}
|
||||
chatId={chatId}
|
||||
feedbackType={'admin'}
|
||||
showMarkIcon
|
||||
showVoiceIcon={false}
|
||||
chatConfig={chat?.app?.chatConfig}
|
||||
appId={appId}
|
||||
chatId={chatId}
|
||||
chatType="log"
|
||||
showRawSource
|
||||
showNodeStatus
|
||||
@@ -191,4 +170,24 @@ const DetailLogsModal = ({
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default DetailLogsModal;
|
||||
|
||||
const Render = (props: Props) => {
|
||||
const { appId, chatId } = props;
|
||||
const params = useMemo(() => {
|
||||
return {
|
||||
chatId,
|
||||
appId,
|
||||
loadCustomFeedbacks: true,
|
||||
type: GetChatTypeEnum.normal
|
||||
};
|
||||
}, [appId, chatId]);
|
||||
|
||||
return (
|
||||
<ChatItemContextProvider>
|
||||
<ChatRecordContextProvider params={params}>
|
||||
<DetailLogsModal {...props} />
|
||||
</ChatRecordContextProvider>
|
||||
</ChatItemContextProvider>
|
||||
);
|
||||
};
|
||||
export default Render;
|
||||
|
||||
@@ -11,6 +11,7 @@ import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
||||
import { fileToBase64 } from '@/web/common/file/utils';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import MyImage from '@fastgpt/web/components/common/Image/MyImage';
|
||||
import { subRoute } from '@fastgpt/web/common/system/utils';
|
||||
|
||||
enum UsingWayEnum {
|
||||
link = 'link',
|
||||
@@ -74,7 +75,7 @@ const SelectUsingWayModal = ({ share, onClose }: { share: OutLinkSchema; onClose
|
||||
});
|
||||
|
||||
const baseUrl = feConfigs?.customSharePageDomain || location?.origin;
|
||||
const linkUrl = `${baseUrl}/chat/share?shareId=${share?.shareId}${
|
||||
const linkUrl = `${baseUrl}${subRoute ? `${subRoute}/` : '/'}chat/share?shareId=${share?.shareId}${
|
||||
getValues('showHistory') ? '' : '&showHistory=0'
|
||||
}`;
|
||||
|
||||
|
||||
@@ -423,7 +423,7 @@ function EditLinkModal({
|
||||
</Flex>
|
||||
<Switch {...register('responseDetail')} isChecked={responseDetail} />
|
||||
</Flex>
|
||||
{/* <Flex alignItems={'center'} mt={4} justify={'space-between'} height={'36px'}>
|
||||
<Flex alignItems={'center'} mt={4} justify={'space-between'} height={'36px'}>
|
||||
<Flex alignItems={'center'}>
|
||||
<FormLabel>{t('common:support.outlink.share.show_complete_quote')}</FormLabel>
|
||||
<QuestionTip
|
||||
@@ -431,8 +431,17 @@ function EditLinkModal({
|
||||
label={t('common:support.outlink.share.show_complete_quote_tips' || '')}
|
||||
></QuestionTip>
|
||||
</Flex>
|
||||
<Switch {...register('showRawSource')} isChecked={showRawSource} />
|
||||
</Flex> */}
|
||||
<Switch
|
||||
{...register('showRawSource', {
|
||||
onChange(e) {
|
||||
if (e.target.checked) {
|
||||
setValue('responseDetail', true);
|
||||
}
|
||||
}
|
||||
})}
|
||||
isChecked={showRawSource}
|
||||
/>
|
||||
</Flex>
|
||||
</Box>
|
||||
</ModalBody>
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
ModalFooter
|
||||
} from '@chakra-ui/react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { AppSchema } from '@fastgpt/global/core/app/type.d';
|
||||
import { AppSchema, AppSimpleEditFormType } from '@fastgpt/global/core/app/type.d';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
@@ -19,21 +19,25 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { AppContext } from '@/pages/app/detail/components/context';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import MyMenu from '@fastgpt/web/components/common/MyMenu';
|
||||
import { useI18n } from '@/web/context/I18n';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { postTransition2Workflow } from '@/web/core/app/api/app';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import { form2AppWorkflow } from '@/web/core/app/utils';
|
||||
import { SimpleAppSnapshotType } from './useSnapshots';
|
||||
|
||||
const AppCard = () => {
|
||||
const AppCard = ({
|
||||
appForm,
|
||||
setPast
|
||||
}: {
|
||||
appForm: AppSimpleEditFormType;
|
||||
setPast: (value: React.SetStateAction<SimpleAppSnapshotType[]>) => void;
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
const { t } = useTranslation();
|
||||
const { appT } = useI18n();
|
||||
|
||||
const { appDetail, setAppDetail, onOpenInfoEdit, onDelApp } = useContextSelector(
|
||||
AppContext,
|
||||
(v) => v
|
||||
);
|
||||
const onSaveApp = useContextSelector(AppContext, (v) => v.onSaveApp);
|
||||
const appDetail = useContextSelector(AppContext, (v) => v.appDetail);
|
||||
const onOpenInfoEdit = useContextSelector(AppContext, (v) => v.onOpenInfoEdit);
|
||||
const onDelApp = useContextSelector(AppContext, (v) => v.onDelApp);
|
||||
|
||||
const appId = appDetail._id;
|
||||
const { feConfigs } = useSystemStore();
|
||||
@@ -42,7 +46,18 @@ const AppCard = () => {
|
||||
// transition to workflow
|
||||
const [transitionCreateNew, setTransitionCreateNew] = useState<boolean>();
|
||||
const { runAsync: onTransition, loading: transiting } = useRequest2(
|
||||
() => postTransition2Workflow({ appId, createNew: transitionCreateNew }),
|
||||
async () => {
|
||||
const { nodes, edges } = form2AppWorkflow(appForm, t);
|
||||
await onSaveApp({
|
||||
nodes,
|
||||
edges,
|
||||
chatConfig: appForm.chatConfig,
|
||||
isPublish: false,
|
||||
versionName: t('app:transition_to_workflow')
|
||||
});
|
||||
|
||||
return postTransition2Workflow({ appId, createNew: transitionCreateNew });
|
||||
},
|
||||
{
|
||||
onSuccess: ({ id }) => {
|
||||
if (id) {
|
||||
@@ -52,10 +67,8 @@ const AppCard = () => {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
setAppDetail((state) => ({
|
||||
...state,
|
||||
type: AppTypeEnum.workflow
|
||||
}));
|
||||
setPast([]);
|
||||
router.reload();
|
||||
}
|
||||
},
|
||||
successToast: t('common:common.Success')
|
||||
@@ -118,7 +131,7 @@ const AppCard = () => {
|
||||
children: [
|
||||
{
|
||||
icon: 'core/app/type/workflow',
|
||||
label: appT('transition_to_workflow'),
|
||||
label: t('app:transition_to_workflow'),
|
||||
onClick: () => setTransitionCreateNew(true)
|
||||
},
|
||||
...(appDetail.permission.hasWritePer && feConfigs?.show_team_chat
|
||||
@@ -159,15 +172,15 @@ const AppCard = () => {
|
||||
</Box>
|
||||
{TeamTagsSet && <TagsEditModal onClose={() => setTeamTagsSet(undefined)} />}
|
||||
{transitionCreateNew !== undefined && (
|
||||
<MyModal isOpen title={appT('transition_to_workflow')} iconSrc="core/app/type/workflow">
|
||||
<MyModal isOpen title={t('app:transition_to_workflow')} iconSrc="core/app/type/workflow">
|
||||
<ModalBody>
|
||||
<Box mb={3}>{appT('transition_to_workflow_create_new_tip')}</Box>
|
||||
<Box mb={3}>{t('app:transition_to_workflow_create_new_tip')}</Box>
|
||||
<HStack cursor={'pointer'} onClick={() => setTransitionCreateNew((state) => !state)}>
|
||||
<Checkbox
|
||||
isChecked={transitionCreateNew}
|
||||
icon={<MyIcon name={'common/check'} w={'12px'} />}
|
||||
/>
|
||||
<Box>{appT('transition_to_workflow_create_new_placeholder')}</Box>
|
||||
<Box>{t('app:transition_to_workflow_create_new_placeholder')}</Box>
|
||||
</HStack>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Box, Flex, IconButton } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
|
||||
@@ -12,8 +12,13 @@ import { useContextSelector } from 'use-context-selector';
|
||||
import { AppContext } from '../context';
|
||||
import { useChatTest } from '../useChatTest';
|
||||
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
|
||||
import ChatItemContextProvider from '@/web/core/chat/context/chatItemContext';
|
||||
import ChatRecordContextProvider from '@/web/core/chat/context/chatRecordContext';
|
||||
import { useChatStore } from '@/web/core/chat/context/useChatStore';
|
||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||
|
||||
const ChatTest = ({ appForm }: { appForm: AppSimpleEditFormType }) => {
|
||||
type Props = { appForm: AppSimpleEditFormType };
|
||||
const ChatTest = ({ appForm }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const { appT } = useI18n();
|
||||
|
||||
@@ -32,13 +37,21 @@ const ChatTest = ({ appForm }: { appForm: AppSimpleEditFormType }) => {
|
||||
setWorkflowData({ nodes, edges });
|
||||
}, [appForm, setWorkflowData, allDatasets, t]);
|
||||
|
||||
const { restartChat, ChatContainer } = useChatTest({
|
||||
const { ChatContainer, restartChat, loading } = useChatTest({
|
||||
...workflowData,
|
||||
chatConfig: appForm.chatConfig
|
||||
chatConfig: appForm.chatConfig,
|
||||
isReady: true
|
||||
});
|
||||
|
||||
return (
|
||||
<Flex position={'relative'} flexDirection={'column'} h={'100%'} py={4}>
|
||||
<MyBox
|
||||
isLoading={loading}
|
||||
display={'flex'}
|
||||
position={'relative'}
|
||||
flexDirection={'column'}
|
||||
h={'100%'}
|
||||
py={4}
|
||||
>
|
||||
<Flex px={[2, 5]}>
|
||||
<Box fontSize={['md', 'lg']} fontWeight={'bold'} flex={1} color={'myGray.900'}>
|
||||
{appT('chat_debug')}
|
||||
@@ -61,8 +74,29 @@ const ChatTest = ({ appForm }: { appForm: AppSimpleEditFormType }) => {
|
||||
<Box flex={1}>
|
||||
<ChatContainer />
|
||||
</Box>
|
||||
</Flex>
|
||||
</MyBox>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(ChatTest);
|
||||
const Render = ({ appForm }: Props) => {
|
||||
const { chatId } = useChatStore();
|
||||
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
||||
|
||||
const chatRecordProviderParams = useMemo(
|
||||
() => ({
|
||||
chatId: chatId,
|
||||
appId: appDetail._id
|
||||
}),
|
||||
[appDetail._id, chatId]
|
||||
);
|
||||
|
||||
return (
|
||||
<ChatItemContextProvider>
|
||||
<ChatRecordContextProvider params={chatRecordProviderParams}>
|
||||
<ChatTest appForm={appForm} />
|
||||
</ChatRecordContextProvider>
|
||||
</ChatItemContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(Render);
|
||||
|
||||
@@ -132,7 +132,7 @@ const Edit = ({
|
||||
flex={'1'}
|
||||
>
|
||||
<Box {...cardStyles} boxShadow={'2'}>
|
||||
<AppCard />
|
||||
<AppCard appForm={appForm} setPast={setPast} />
|
||||
</Box>
|
||||
|
||||
<Box mt={4} {...cardStyles} boxShadow={'3.5'}>
|
||||
|
||||
@@ -87,7 +87,6 @@ const Header = ({
|
||||
nodes,
|
||||
edges,
|
||||
chatConfig: appForm.chatConfig,
|
||||
type: AppTypeEnum.simple,
|
||||
isPublish,
|
||||
versionName
|
||||
});
|
||||
|
||||
@@ -37,7 +37,8 @@ export const compareSimpleAppSnapshot = (
|
||||
scheduledTriggerConfig: appForm1.chatConfig?.scheduledTriggerConfig || undefined,
|
||||
chatInputGuide: appForm1.chatConfig?.chatInputGuide || undefined,
|
||||
fileSelectConfig: appForm1.chatConfig?.fileSelectConfig || undefined,
|
||||
instruction: appForm1.chatConfig?.instruction || ''
|
||||
instruction: appForm1.chatConfig?.instruction || '',
|
||||
autoExecute: appForm1.chatConfig?.autoExecute || undefined
|
||||
},
|
||||
{
|
||||
welcomeText: appForm2.chatConfig?.welcomeText || '',
|
||||
@@ -48,7 +49,8 @@ export const compareSimpleAppSnapshot = (
|
||||
scheduledTriggerConfig: appForm2.chatConfig?.scheduledTriggerConfig || undefined,
|
||||
chatInputGuide: appForm2.chatConfig?.chatInputGuide || undefined,
|
||||
fileSelectConfig: appForm2.chatConfig?.fileSelectConfig || undefined,
|
||||
instruction: appForm2.chatConfig?.instruction || ''
|
||||
instruction: appForm2.chatConfig?.instruction || '',
|
||||
autoExecute: appForm2.chatConfig?.autoExecute || undefined
|
||||
}
|
||||
)
|
||||
) {
|
||||
|
||||
@@ -147,7 +147,6 @@ const AppCard = ({
|
||||
appDetail.name,
|
||||
appDetail.permission.hasWritePer,
|
||||
appDetail.permission.isOwner,
|
||||
currentTab,
|
||||
feConfigs?.show_team_chat,
|
||||
onDelApp,
|
||||
onOpenImport,
|
||||
@@ -252,10 +251,6 @@ function ExportPopover({
|
||||
trigger={'hover'}
|
||||
w={'8.6rem'}
|
||||
Trigger={
|
||||
// <Flex align={'center'} w={'100%'} py={2} px={3}>
|
||||
// <Avatar src={'export'} borderRadius={'sm'} w={'1rem'} mr={3} />
|
||||
// {t('app:export_configs')}
|
||||
// </Flex>
|
||||
<MyBox display={'flex'} size={'md'} rounded={'4px'} cursor={'pointer'}>
|
||||
<MyIcon name={'export'} w={'16px'} mr={2} />
|
||||
<Box fontSize={'sm'}>{t('app:export_configs')}</Box>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node.d';
|
||||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { SmallCloseIcon } from '@chakra-ui/icons';
|
||||
import { Box, Flex, IconButton } from '@chakra-ui/react';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
@@ -14,27 +14,34 @@ import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
|
||||
import { PluginRunBoxTabEnum } from '@/components/core/chat/ChatContainer/PluginRunBox/constants';
|
||||
import CloseIcon from '@fastgpt/web/components/common/Icon/close';
|
||||
import ChatItemContextProvider, { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
|
||||
import ChatRecordContextProvider, {
|
||||
ChatRecordContext
|
||||
} from '@/web/core/chat/context/chatRecordContext';
|
||||
import { useChatStore } from '@/web/core/chat/context/useChatStore';
|
||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||
|
||||
const ChatTest = ({
|
||||
isOpen,
|
||||
nodes = [],
|
||||
edges = [],
|
||||
onClose
|
||||
}: {
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
nodes?: StoreNodeItemType[];
|
||||
edges?: StoreEdgeItemType[];
|
||||
onClose: () => void;
|
||||
}) => {
|
||||
};
|
||||
|
||||
const ChatTest = ({ isOpen, nodes = [], edges = [], onClose }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
||||
const isPlugin = appDetail.type === AppTypeEnum.plugin;
|
||||
|
||||
const { restartChat, ChatContainer, pluginRunTab, setPluginRunTab, chatRecords } = useChatTest({
|
||||
const { restartChat, ChatContainer, loading } = useChatTest({
|
||||
nodes,
|
||||
edges,
|
||||
chatConfig: appDetail.chatConfig
|
||||
chatConfig: appDetail.chatConfig,
|
||||
isReady: isOpen
|
||||
});
|
||||
const pluginRunTab = useContextSelector(ChatItemContext, (v) => v.pluginRunTab);
|
||||
const setPluginRunTab = useContextSelector(ChatItemContext, (v) => v.setPluginRunTab);
|
||||
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -48,8 +55,10 @@ const ChatTest = ({
|
||||
right={0}
|
||||
onClick={onClose}
|
||||
/>
|
||||
<Flex
|
||||
<MyBox
|
||||
isLoading={loading}
|
||||
zIndex={300}
|
||||
display={'flex'}
|
||||
flexDirection={'column'}
|
||||
position={'absolute'}
|
||||
top={5}
|
||||
@@ -131,9 +140,30 @@ const ChatTest = ({
|
||||
<Box flex={'1 0 0'} overflow={'auto'}>
|
||||
<ChatContainer />
|
||||
</Box>
|
||||
</Flex>
|
||||
</MyBox>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(ChatTest);
|
||||
const Render = (Props: Props) => {
|
||||
const { chatId } = useChatStore();
|
||||
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
||||
|
||||
const chatRecordProviderParams = useMemo(
|
||||
() => ({
|
||||
chatId: chatId,
|
||||
appId: appDetail._id
|
||||
}),
|
||||
[appDetail._id, chatId]
|
||||
);
|
||||
|
||||
return (
|
||||
<ChatItemContextProvider>
|
||||
<ChatRecordContextProvider params={chatRecordProviderParams}>
|
||||
<ChatTest {...Props} />
|
||||
</ChatRecordContextProvider>
|
||||
</ChatItemContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(Render);
|
||||
|
||||
@@ -240,7 +240,7 @@ const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => {
|
||||
position={'absolute'}
|
||||
top={'10px'}
|
||||
left={0}
|
||||
pt={'20px'}
|
||||
pt={5}
|
||||
pb={4}
|
||||
h={isOpen ? 'calc(100% - 20px)' : '0'}
|
||||
w={isOpen ? ['100%', `${sliderWidth}px`] : '0'}
|
||||
@@ -254,7 +254,7 @@ const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => {
|
||||
{/* Header */}
|
||||
<Box px={'5'} mb={3} whiteSpace={'nowrap'} overflow={'hidden'}>
|
||||
{/* Tabs */}
|
||||
<Flex flex={'1 0 0'} alignItems={'center'} gap={3}>
|
||||
<Flex flex={'1 0 0'} alignItems={'center'} gap={2}>
|
||||
<Box flex={'1 0 0'}>
|
||||
<FillRowTabs
|
||||
list={[
|
||||
@@ -288,8 +288,14 @@ const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => {
|
||||
{/* close icon */}
|
||||
<IconButton
|
||||
size={'sm'}
|
||||
icon={<MyIcon name={'common/backFill'} w={'14px'} color={'myGray.700'} />}
|
||||
borderColor={'myGray.300'}
|
||||
icon={<MyIcon name={'common/backFill'} w={'14px'} color={'myGray.600'} />}
|
||||
bg={'myGray.100'}
|
||||
_hover={{
|
||||
bg: 'myGray.200',
|
||||
'& svg': {
|
||||
color: 'primary.600'
|
||||
}
|
||||
}}
|
||||
variant={'grayBase'}
|
||||
aria-label={''}
|
||||
onClick={onClose}
|
||||
|
||||
@@ -177,20 +177,15 @@ const ButtonEdge = (props: EdgeProps) => {
|
||||
position={'absolute'}
|
||||
transform={arrowTransform}
|
||||
pointerEvents={'all'}
|
||||
w={highlightEdge ? '14px' : '10px'}
|
||||
h={highlightEdge ? '14px' : '10px'}
|
||||
w={highlightEdge ? '12px' : '10px'}
|
||||
h={highlightEdge ? '12px' : '10px'}
|
||||
zIndex={highlightEdge ? defaultZIndex + 1000 : defaultZIndex}
|
||||
>
|
||||
<MyIcon
|
||||
name={'core/workflow/edgeArrow'}
|
||||
name={highlightEdge ? 'core/workflow/edgeArrowBold' : 'core/workflow/edgeArrow'}
|
||||
w={'100%'}
|
||||
color={edgeColor}
|
||||
{...(highlightEdge
|
||||
? {
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
: {})}
|
||||
></MyIcon>
|
||||
/>
|
||||
</Flex>
|
||||
)}
|
||||
</Box>
|
||||
@@ -223,7 +218,7 @@ const ButtonEdge = (props: EdgeProps) => {
|
||||
...style,
|
||||
...(highlightEdge
|
||||
? {
|
||||
strokeWidth: 5
|
||||
strokeWidth: 4
|
||||
}
|
||||
: { strokeWidth: 3, zIndex: 2 })
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { nodeTemplate2FlowNode } from '@/web/core/workflow/utils';
|
||||
import { CommentNode } from '@fastgpt/global/core/workflow/template/system/comment';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
|
||||
@@ -333,7 +333,7 @@ export const useDebug = () => {
|
||||
<Flex alignItems={'center'} mb={1}>
|
||||
<Box position={'relative'}>
|
||||
{required && (
|
||||
<Box position={'absolute'} right={-2} top={'-1px'} color={'red.600'}>
|
||||
<Box position={'absolute'} left={'-8px'} top={'-2px'} color={'red.600'}>
|
||||
*
|
||||
</Box>
|
||||
)}
|
||||
|
||||
@@ -36,8 +36,8 @@ const NodeCode = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
return {
|
||||
[NodeInputKeyEnum.code]: (item: FlowNodeInputItemType) => {
|
||||
return (
|
||||
<Box>
|
||||
<Flex mb={1} alignItems={'flex-end'}>
|
||||
<Box mt={-3}>
|
||||
<Flex mb={2} alignItems={'flex-end'}>
|
||||
<Box flex={'1'}>{'Javascript ' + workflowT('Code')}</Box>
|
||||
<Box
|
||||
cursor={'pointer'}
|
||||
@@ -88,7 +88,7 @@ const NodeCode = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
</>
|
||||
)}
|
||||
<Container>
|
||||
<IOTitle text={t('common:common.Input')} />
|
||||
<IOTitle text={t('common:common.Input')} mb={-1} />
|
||||
<RenderInput
|
||||
nodeId={nodeId}
|
||||
flowInputList={commonInputs}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowContext } from '../../context';
|
||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
const NodeComment = ({ data }: NodeProps<FlowNodeItemType>) => {
|
||||
const { nodeId, inputs } = data;
|
||||
|
||||
@@ -34,6 +34,7 @@ import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
import IOTitle from '../../components/IOTitle';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowContext } from '../../../context';
|
||||
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
|
||||
|
||||
const NodeExtract = ({ data }: NodeProps<FlowNodeItemType>) => {
|
||||
const { inputs, outputs, nodeId } = data;
|
||||
@@ -53,14 +54,15 @@ const NodeExtract = ({ data }: NodeProps<FlowNodeItemType>) => {
|
||||
}: Omit<FlowNodeInputItemType, 'value'> & {
|
||||
value?: ContextExtractAgentItemType[];
|
||||
}) => (
|
||||
<Box>
|
||||
<Box mt={-2}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Box flex={'1 0 0'} fontWeight={'medium'} color={'myGray.600'}>
|
||||
<Box flex={'1 0 0'} fontSize={'sm'} fontWeight={'medium'} color={'myGray.600'}>
|
||||
{t('common:core.module.extract.Target field')}
|
||||
</Box>
|
||||
<Button
|
||||
size={'sm'}
|
||||
variant={'ghost'}
|
||||
variant={'grayGhost'}
|
||||
px={2}
|
||||
color={'myGray.600'}
|
||||
leftIcon={<AddIcon fontSize={'10px'} />}
|
||||
onClick={() => setEditExtractField(defaultField)}
|
||||
@@ -68,48 +70,47 @@ const NodeExtract = ({ data }: NodeProps<FlowNodeItemType>) => {
|
||||
{t('common:core.module.extract.Add field')}
|
||||
</Button>
|
||||
</Flex>
|
||||
<Box
|
||||
mt={2}
|
||||
borderRadius={'md'}
|
||||
overflow={'hidden'}
|
||||
borderWidth={'1px'}
|
||||
borderBottom="none"
|
||||
>
|
||||
<TableContainer>
|
||||
<Table bg={'white'}>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th borderRadius={'none !important'}>{t('common:item_name')}</Th>
|
||||
<Th>{t('common:item_description')}</Th>
|
||||
<Th>{t('common:required')}</Th>
|
||||
<Th borderRadius={'none !important'}></Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{extractKeys.map((item, index) => (
|
||||
<Tr
|
||||
key={index}
|
||||
position={'relative'}
|
||||
whiteSpace={'pre-wrap'}
|
||||
wordBreak={'break-all'}
|
||||
>
|
||||
<Td>{item.key}</Td>
|
||||
<Td>{item.desc}</Td>
|
||||
<Td>{item.required ? '✔' : ''}</Td>
|
||||
<Td whiteSpace={'nowrap'}>
|
||||
<MyIcon
|
||||
mr={3}
|
||||
name={'common/settingLight'}
|
||||
w={'16px'}
|
||||
cursor={'pointer'}
|
||||
|
||||
<TableContainer borderRadius={'md'} overflow={'hidden'} borderWidth={'1px'} mt={2}>
|
||||
<Table variant={'workflow'}>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>{t('common:item_name')}</Th>
|
||||
<Th>{t('common:item_description')}</Th>
|
||||
<Th>{t('common:required')}</Th>
|
||||
<Th></Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{extractKeys.map((item, index) => (
|
||||
<Tr key={index}>
|
||||
<Td>
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon name={'checkCircle'} w={'14px'} mr={1} color={'myGray.600'} />
|
||||
{item.key}
|
||||
</Flex>
|
||||
</Td>
|
||||
<Td>{item.desc}</Td>
|
||||
<Td>
|
||||
{item.required ? (
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon name={'check'} w={'16px'} color={'myGray.900'} mr={2} />
|
||||
</Flex>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</Td>
|
||||
<Td>
|
||||
<Flex>
|
||||
<MyIconButton
|
||||
icon={'common/settingLight'}
|
||||
onClick={() => {
|
||||
setEditExtractField(item);
|
||||
}}
|
||||
/>
|
||||
<MyIcon
|
||||
name={'delete'}
|
||||
w={'16px'}
|
||||
cursor={'pointer'}
|
||||
<MyIconButton
|
||||
icon={'delete'}
|
||||
hoverColor={'red.500'}
|
||||
onClick={() => {
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
@@ -128,13 +129,13 @@ const NodeExtract = ({ data }: NodeProps<FlowNodeItemType>) => {
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Box>
|
||||
</Flex>
|
||||
</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Box>
|
||||
)
|
||||
}),
|
||||
|
||||
@@ -3,7 +3,7 @@ import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants
|
||||
import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import React, { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { UserInputFormItemType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||
import { useForm } from 'react-hook-form';
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
Box,
|
||||
Button,
|
||||
Flex,
|
||||
FormLabel,
|
||||
HStack,
|
||||
Table,
|
||||
TableContainer,
|
||||
@@ -24,7 +23,7 @@ import {
|
||||
Tr
|
||||
} from '@chakra-ui/react';
|
||||
import { UserInputFormItemType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import {
|
||||
FlowNodeInputMap,
|
||||
FlowNodeInputTypeEnum,
|
||||
@@ -37,6 +36,8 @@ import { useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowContext } from '../../../context';
|
||||
import InputFormEditModal, { defaultFormInput } from './InputFormEditModal';
|
||||
import RenderOutput from '../render/RenderOutput';
|
||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
|
||||
|
||||
const NodeFormInput = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
const { nodeId, inputs, outputs } = data;
|
||||
@@ -120,10 +121,14 @@ const NodeFormInput = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
return (
|
||||
<Box>
|
||||
<HStack className="nodrag" cursor={'default'} mb={3}>
|
||||
<FormLabel>{t('workflow:user_form_input_config')}</FormLabel>
|
||||
<FormLabel fontSize={'sm'} color={'myGray.600'}>
|
||||
{t('workflow:user_form_input_config')}
|
||||
</FormLabel>
|
||||
<Box flex={'1 0 0'} />
|
||||
<Button
|
||||
variant={'ghost'}
|
||||
variant={'grayGhost'}
|
||||
px={2}
|
||||
color={'myGray.600'}
|
||||
leftIcon={<SmallAddIcon />}
|
||||
iconSpacing={1}
|
||||
size={'sm'}
|
||||
@@ -144,17 +149,14 @@ const NodeFormInput = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
/>
|
||||
)}
|
||||
</HStack>
|
||||
|
||||
<TableContainer borderWidth={'1px'} borderRadius={'md'} borderBottom="none">
|
||||
<Table bg={'white'}>
|
||||
<TableContainer borderWidth={'1px'} borderRadius={'md'}>
|
||||
<Table variant={'workflow'}>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th borderBottomLeftRadius={'none !important'}>
|
||||
{t('workflow:user_form_input_name')}
|
||||
</Th>
|
||||
<Th>{t('workflow:user_form_input_name')}</Th>
|
||||
<Th>{t('workflow:user_form_input_description')}</Th>
|
||||
<Th>{t('common:common.Require Input')}</Th>
|
||||
<Th borderBottomRightRadius={'none !important'}>{t('user:operations')}</Th>
|
||||
<Th>{t('user:operations')}</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
@@ -163,35 +165,35 @@ const NodeFormInput = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
return (
|
||||
<Tr key={index}>
|
||||
<Td>
|
||||
<Flex alignItems={'center'}>
|
||||
<Flex alignItems={'center'} fontSize={'mini'} fontWeight={'medium'}>
|
||||
{!!icon && (
|
||||
<MyIcon name={icon as any} w={'14px'} mr={1} color={'primary.600'} />
|
||||
<MyIcon name={icon as any} w={'14px'} mr={1} color={'myGray.400'} />
|
||||
)}
|
||||
{item.label}
|
||||
</Flex>
|
||||
</Td>
|
||||
<Td>{item.description}</Td>
|
||||
<Td>{item.required ? '✅' : ''}</Td>
|
||||
<Td>{item.description || '-'}</Td>
|
||||
<Td>
|
||||
<MyIcon
|
||||
mr={3}
|
||||
name={'common/settingLight'}
|
||||
w={'16px'}
|
||||
cursor={'pointer'}
|
||||
_hover={{ color: 'primary.600' }}
|
||||
onClick={() => setEditField(item)}
|
||||
/>
|
||||
<MyIcon
|
||||
className="delete"
|
||||
name={'delete'}
|
||||
w={'16px'}
|
||||
color={'myGray.600'}
|
||||
cursor={'pointer'}
|
||||
_hover={{ color: 'red.500' }}
|
||||
onClick={() => {
|
||||
onDelete(item.key);
|
||||
}}
|
||||
/>
|
||||
{item.required ? (
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon name={'check'} w={'16px'} color={'myGray.900'} mr={2} />
|
||||
</Flex>
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
</Td>
|
||||
<Td>
|
||||
<Flex>
|
||||
<MyIconButton
|
||||
icon={'common/settingLight'}
|
||||
onClick={() => setEditField(item)}
|
||||
/>
|
||||
<MyIconButton
|
||||
icon={'delete'}
|
||||
hoverColor={'red.500'}
|
||||
onClick={() => onDelete(item.key)}
|
||||
/>
|
||||
</Flex>
|
||||
</Td>
|
||||
</Tr>
|
||||
);
|
||||
|
||||
@@ -5,11 +5,6 @@ import {
|
||||
FormControl,
|
||||
HStack,
|
||||
Input,
|
||||
NumberDecrementStepper,
|
||||
NumberIncrementStepper,
|
||||
NumberInput,
|
||||
NumberInputField,
|
||||
NumberInputStepper,
|
||||
Stack,
|
||||
Switch,
|
||||
Textarea
|
||||
@@ -28,7 +23,7 @@ import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||
import JsonEditor from '@fastgpt/web/components/common/Textarea/JsonEditor';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useFieldArray, UseFormReturn } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import DndDrag, { Draggable } from '@fastgpt/web/components/common/DndDrag';
|
||||
import MyTextarea from '@/components/common/Textarea/MyTextarea';
|
||||
|
||||
@@ -100,6 +100,7 @@ const PluginOutputEditModal = ({
|
||||
|
||||
data.key = data?.key?.trim();
|
||||
data.label = data.key;
|
||||
data.required = true;
|
||||
|
||||
onSubmit({
|
||||
data,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React from 'react';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { Box, Table, Thead, Tbody, Tr, Th, Td, TableContainer, Flex } from '@chakra-ui/react';
|
||||
import { Table, Thead, Tbody, Tr, Th, Td, TableContainer, Flex } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
|
||||
|
||||
const VariableTable = ({
|
||||
variables = [],
|
||||
@@ -16,58 +17,61 @@ const VariableTable = ({
|
||||
const showToolColumn = variables.some((item) => item.isTool);
|
||||
|
||||
return (
|
||||
<Box bg={'white'} borderRadius={'md'} overflow={'hidden'} border={'base'}>
|
||||
<TableContainer>
|
||||
<Table bg={'white'}>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th borderBottomLeftRadius={'none !important'}>{t('workflow:Variable_name')}</Th>
|
||||
<Th>{t('common:core.workflow.Value type')}</Th>
|
||||
{showToolColumn && <Th>{t('workflow:tool_input')}</Th>}
|
||||
<Th borderBottomRightRadius={'none !important'}></Th>
|
||||
<TableContainer
|
||||
borderRadius={'md'}
|
||||
overflow={'hidden'}
|
||||
border={'1px solid'}
|
||||
borderColor={'myGray.200'}
|
||||
>
|
||||
<Table variant={'workflow'}>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>{t('workflow:Variable_name')}</Th>
|
||||
<Th>{t('common:core.workflow.Value type')}</Th>
|
||||
{showToolColumn && <Th>{t('workflow:tool_input')}</Th>}
|
||||
<Th>{t('user:operations')}</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{variables.map((item, index) => (
|
||||
<Tr key={item.key}>
|
||||
<Td>
|
||||
<Flex alignItems={'center'} fontSize={'xs'}>
|
||||
{!!item.icon ? (
|
||||
<MyIcon name={item.icon as any} w={'14px'} mr={1} color={'myGray.600'} />
|
||||
) : (
|
||||
<MyIcon name={'checkCircle'} w={'14px'} mr={1} color={'myGray.600'} />
|
||||
)}
|
||||
{item.label || item.key}
|
||||
</Flex>
|
||||
</Td>
|
||||
<Td>{item.type}</Td>
|
||||
{showToolColumn && (
|
||||
<Td>
|
||||
{item.isTool ? (
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon name={'check'} w={'16px'} color={'myGray.900'} mr={2} />
|
||||
</Flex>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</Td>
|
||||
)}
|
||||
<Td>
|
||||
<Flex>
|
||||
<MyIconButton icon={'common/settingLight'} onClick={() => onEdit(item.key)} />
|
||||
<MyIconButton
|
||||
icon={'delete'}
|
||||
hoverColor={'red.500'}
|
||||
onClick={() => onDelete(item.key)}
|
||||
/>
|
||||
</Flex>
|
||||
</Td>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{variables.map((item) => (
|
||||
<Tr key={item.key}>
|
||||
<Td>
|
||||
<Flex alignItems={'center'}>
|
||||
{!!item.icon && (
|
||||
<MyIcon name={item.icon as any} w={'14px'} mr={1} color={'primary.600'} />
|
||||
)}
|
||||
{item.label || item.key}
|
||||
</Flex>
|
||||
</Td>
|
||||
<Td>{item.type}</Td>
|
||||
{showToolColumn && <Th>{item.isTool ? '✅' : '-'}</Th>}
|
||||
<Td>
|
||||
<MyIcon
|
||||
mr={3}
|
||||
name={'common/settingLight'}
|
||||
w={'16px'}
|
||||
cursor={'pointer'}
|
||||
_hover={{ color: 'primary.600' }}
|
||||
onClick={() => onEdit(item.key)}
|
||||
/>
|
||||
<MyIcon
|
||||
className="delete"
|
||||
name={'delete'}
|
||||
w={'16px'}
|
||||
color={'myGray.600'}
|
||||
cursor={'pointer'}
|
||||
ml={2}
|
||||
_hover={{ color: 'red.500' }}
|
||||
onClick={() => {
|
||||
onDelete(item.key);
|
||||
}}
|
||||
/>
|
||||
</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Box>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import FileSelect from '@/components/core/app/FileSelect';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { userFilesInput } from '@fastgpt/global/core/workflow/template/system/workflowStart';
|
||||
import Container from '../components/Container';
|
||||
import AutoExecConfig from '@/components/core/app/AutoExecConfig';
|
||||
|
||||
type ComponentProps = {
|
||||
chatConfig: AppChatConfigType;
|
||||
@@ -80,6 +81,9 @@ const NodeUserGuide = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
<Box mt={4} pt={3} borderTop={'base'} borderColor={'myGray.200'}>
|
||||
<ScheduledTrigger {...componentsProps} />
|
||||
</Box>
|
||||
<Box mt={3} pt={3} borderTop={'base'} borderColor={'myGray.200'}>
|
||||
<AutoExecute {...componentsProps} />
|
||||
</Box>
|
||||
<Box mt={3} pt={3} borderTop={'base'} borderColor={'myGray.200'}>
|
||||
<QuestionInputGuide {...componentsProps} />
|
||||
</Box>
|
||||
@@ -128,6 +132,23 @@ function ChatStartVariable({ chatConfig: { variables = [] }, setAppDetail }: Com
|
||||
return <VariableEdit variables={variables} onChange={(e) => updateVariables(e)} />;
|
||||
}
|
||||
|
||||
function AutoExecute({ chatConfig: { autoExecute }, setAppDetail }: ComponentProps) {
|
||||
return (
|
||||
<AutoExecConfig
|
||||
value={autoExecute}
|
||||
onChange={(e) =>
|
||||
setAppDetail((state) => ({
|
||||
...state,
|
||||
chatConfig: {
|
||||
...state.chatConfig,
|
||||
autoExecute: e
|
||||
}
|
||||
}))
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function QuestionGuide({ chatConfig: { questionGuide = false }, setAppDetail }: ComponentProps) {
|
||||
return (
|
||||
<QGSwitch
|
||||
|
||||
@@ -5,7 +5,7 @@ import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import MySelect from '@fastgpt/web/components/common/MySelect';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { defaultEditFormData } from '../render/RenderToolInput/EditFieldModal';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
Thead,
|
||||
Tr
|
||||
} from '@chakra-ui/react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { SmallAddIcon } from '@chakra-ui/icons';
|
||||
import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io';
|
||||
import { defaultEditFormData } from '../render/RenderToolInput/EditFieldModal';
|
||||
@@ -36,7 +36,7 @@ const NodeToolParams = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
<NodeCard selected={selected} {...data}>
|
||||
<Container>
|
||||
<Flex alignItems={'center'} justifyContent={'space-between'} mb={1.5}>
|
||||
<FormLabel>{t('workflow:tool_custom_field')}</FormLabel>
|
||||
<FormLabel fontSize={'sm'}>{t('workflow:tool_custom_field')}</FormLabel>
|
||||
<Button
|
||||
variant={'whiteBase'}
|
||||
leftIcon={<SmallAddIcon />}
|
||||
@@ -54,38 +54,89 @@ const NodeToolParams = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
<Box borderRadius={'md'} overflow={'hidden'} border={'base'}>
|
||||
<TableContainer>
|
||||
<Table bg={'white'}>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>{t('workflow:tool_params.params_name')}</Th>
|
||||
<Th>{t('workflow:tool_params.params_description')}</Th>
|
||||
<Th>{t('common:common.Operation')}</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{inputs.map((item, index) => (
|
||||
<Tr
|
||||
key={index}
|
||||
position={'relative'}
|
||||
whiteSpace={'pre-wrap'}
|
||||
wordBreak={'break-all'}
|
||||
<TableContainer
|
||||
borderRadius={'md'}
|
||||
overflow={'hidden'}
|
||||
border={'1px solid'}
|
||||
borderColor={'myGray.200'}
|
||||
>
|
||||
<Table bg={'white'}>
|
||||
<Thead>
|
||||
<Tr h={8}>
|
||||
<Th p={0} px={4} bg={'myGray.50'} borderBottomLeftRadius={'none !important'}>
|
||||
{t('workflow:tool_params.params_name')}
|
||||
</Th>
|
||||
<Th p={0} px={4} bg={'myGray.50'}>
|
||||
{t('workflow:tool_params.params_description')}
|
||||
</Th>
|
||||
<Th p={0} px={4} bg={'myGray.50'} borderBottomRightRadius={'none !important'}>
|
||||
{t('common:common.Operation')}
|
||||
</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{inputs.map((item, index) => (
|
||||
<Tr
|
||||
key={index}
|
||||
position={'relative'}
|
||||
whiteSpace={'pre-wrap'}
|
||||
wordBreak={'break-all'}
|
||||
h={10}
|
||||
>
|
||||
<Td
|
||||
p={0}
|
||||
px={4}
|
||||
borderBottom={index === inputs.length - 1 ? 'none' : undefined}
|
||||
>
|
||||
<Td>{item.key}</Td>
|
||||
<Td>{item.toolDescription}</Td>
|
||||
<Td whiteSpace={'nowrap'}>
|
||||
<MyIcon
|
||||
<Flex alignItems={'center'} fontSize={'xs'}>
|
||||
<MyIcon name={'checkCircle'} w={'14px'} mr={1} color={'myGray.600'} />
|
||||
{item.key}
|
||||
</Flex>
|
||||
</Td>
|
||||
<Td
|
||||
p={0}
|
||||
px={4}
|
||||
borderBottom={index === inputs.length - 1 ? 'none' : undefined}
|
||||
fontSize={'xs'}
|
||||
>
|
||||
{item.toolDescription}
|
||||
</Td>
|
||||
<Td
|
||||
p={0}
|
||||
px={4}
|
||||
borderBottom={index === inputs.length - 1 ? 'none' : undefined}
|
||||
whiteSpace={'nowrap'}
|
||||
>
|
||||
<Flex alignItems={'center'}>
|
||||
<Flex
|
||||
mr={3}
|
||||
name={'common/settingLight'}
|
||||
w={'16px'}
|
||||
p={1}
|
||||
color={'myGray.500'}
|
||||
rounded={'sm'}
|
||||
alignItems={'center'}
|
||||
bg={'transparent'}
|
||||
transition={'background 0.1s'}
|
||||
cursor={'pointer'}
|
||||
_hover={{
|
||||
bg: 'myGray.05',
|
||||
color: 'primary.600'
|
||||
}}
|
||||
onClick={() => setEditField(item)}
|
||||
/>
|
||||
<MyIcon
|
||||
name={'delete'}
|
||||
w={'16px'}
|
||||
>
|
||||
<MyIcon name={'common/settingLight'} w={'16px'} />
|
||||
</Flex>
|
||||
<Flex
|
||||
p={1}
|
||||
color={'myGray.500'}
|
||||
rounded={'sm'}
|
||||
alignItems={'center'}
|
||||
bg={'transparent'}
|
||||
transition={'background 0.1s'}
|
||||
cursor={'pointer'}
|
||||
_hover={{
|
||||
bg: 'myGray.05',
|
||||
color: 'red.500'
|
||||
}}
|
||||
onClick={() => {
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
@@ -98,14 +149,16 @@ const NodeToolParams = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
key: item.key
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Box>
|
||||
>
|
||||
<MyIcon name={'delete'} w={'16px'} />
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Container>
|
||||
</NodeCard>
|
||||
);
|
||||
|
||||
@@ -26,7 +26,7 @@ const NodeTools = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
<RenderOutput nodeId={nodeId} flowOutputList={outputs} />
|
||||
</Container>
|
||||
<Box position={'relative'}>
|
||||
<Box mb={-4} borderBottomLeftRadius={'md'} borderBottomRadius={'md'} overflow={'hidden'}>
|
||||
<Box mb={-3} borderBottomRadius={'lg'} overflow={'hidden'}>
|
||||
<Divider
|
||||
showBorderBottom={false}
|
||||
icon={<MyIcon name="phoneTabbar/tool" w={'16px'} h={'16px'} />}
|
||||
|
||||
@@ -123,7 +123,7 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
|
||||
return (
|
||||
<Container key={index} w={'full'} mx={0}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Flex w={'60px'}>{t('common:core.workflow.variable')}</Flex>
|
||||
<Flex w={'80px'}>{t('common:core.workflow.variable')}</Flex>
|
||||
<VariableSelector
|
||||
nodeId={nodeId}
|
||||
variable={updateItem.variable}
|
||||
@@ -162,7 +162,7 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
|
||||
)}
|
||||
</Flex>
|
||||
<Flex mt={2} w={'full'} alignItems={'center'} className="nodrag">
|
||||
<Flex w={'60px'}>
|
||||
<Flex w={'80px'}>
|
||||
<Box>{t('common:core.workflow.value')}</Box>
|
||||
<MyTooltip
|
||||
label={
|
||||
|
||||
@@ -53,6 +53,8 @@ export const ToolTargetHandle = ({ show, nodeId }: ToolHandleProps) => {
|
||||
w={handleSize}
|
||||
h={handleSize}
|
||||
border={'4px solid #8774EE'}
|
||||
rounded={'xs'}
|
||||
bg={'white'}
|
||||
transform={'translate(0,0) rotate(45deg)'}
|
||||
pointerEvents={'none'}
|
||||
/>
|
||||
@@ -105,6 +107,8 @@ export const ToolSourceHandle = () => {
|
||||
w={handleSize}
|
||||
h={handleSize}
|
||||
border={'4px solid #8774EE'}
|
||||
rounded={'xs'}
|
||||
bg={'white'}
|
||||
transform={'translate(0,0) rotate(45deg)'}
|
||||
pointerEvents={'none'}
|
||||
/>
|
||||
|
||||
@@ -20,7 +20,6 @@ import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowContext } from '../../../context';
|
||||
import { moduleTemplatesFlat } from '@fastgpt/global/core/workflow/template/constants';
|
||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { useWorkflowUtils } from '../../hooks/useUtils';
|
||||
@@ -29,6 +28,7 @@ import { getDocPath } from '@/web/common/system/doc';
|
||||
import { WorkflowNodeEdgeContext } from '../../../context/workflowInitContext';
|
||||
import { WorkflowEventContext } from '../../../context/workflowEventContext';
|
||||
import MyImage from '@fastgpt/web/components/common/Image/MyImage';
|
||||
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
|
||||
|
||||
type Props = FlowNodeItemType & {
|
||||
children?: React.ReactNode | React.ReactNode[] | string;
|
||||
@@ -166,7 +166,7 @@ const NodeCard = (props: Props) => {
|
||||
<ToolTargetHandle show={showToolHandle} nodeId={nodeId} />
|
||||
|
||||
{/* avatar and name */}
|
||||
<Flex alignItems={'center'}>
|
||||
<Flex alignItems={'center'} mb={intro ? 1 : 0}>
|
||||
{node?.flowNodeType !== FlowNodeTypeEnum.stopTool && (
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
@@ -202,15 +202,13 @@ const NodeCard = (props: Props) => {
|
||||
<Box ml={2} fontSize={'18px'} fontWeight={'medium'} color={'myGray.900'}>
|
||||
{t(name as any)}
|
||||
</Box>
|
||||
<MyIcon
|
||||
className="controller-rename"
|
||||
<Button
|
||||
display={'none'}
|
||||
name={'edit'}
|
||||
w={'14px'}
|
||||
variant={'grayGhost'}
|
||||
size={'xs'}
|
||||
ml={0.5}
|
||||
className="controller-rename"
|
||||
cursor={'pointer'}
|
||||
ml={1}
|
||||
color={'myGray.500'}
|
||||
_hover={{ color: 'primary.600' }}
|
||||
onClick={() => {
|
||||
onOpenCustomTitleModal({
|
||||
defaultVal: name,
|
||||
@@ -230,7 +228,9 @@ const NodeCard = (props: Props) => {
|
||||
}
|
||||
});
|
||||
}}
|
||||
/>
|
||||
>
|
||||
<MyIcon name={'edit'} w={'14px'} />
|
||||
</Button>
|
||||
<Box flex={1} />
|
||||
{hasNewVersion && (
|
||||
<MyTooltip label={t('app:app.modules.click to update')}>
|
||||
@@ -248,7 +248,7 @@ const NodeCard = (props: Props) => {
|
||||
onClick={onOpenConfirmSync(onClickSyncVersion)}
|
||||
>
|
||||
<Box>{t('app:app.modules.has new version')}</Box>
|
||||
<QuestionOutlineIcon ml={1} />
|
||||
<MyIcon name={'help'} w={'14px'} ml={1} />
|
||||
</Button>
|
||||
</MyTooltip>
|
||||
)}
|
||||
@@ -263,32 +263,20 @@ const NodeCard = (props: Props) => {
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Box
|
||||
fontSize={'sm'}
|
||||
color={'primary.700'}
|
||||
p={1}
|
||||
rounded={'sm'}
|
||||
cursor={'default'}
|
||||
_hover={{ bg: 'rgba(17, 24, 36, 0.05)' }}
|
||||
>
|
||||
<Button variant={'grayGhost'} size={'xs'} color={'primary.600'} px={1}>
|
||||
{t('common:core.module.Diagram')}
|
||||
</Box>
|
||||
</Button>
|
||||
</MyTooltip>
|
||||
)}
|
||||
{!!nodeTemplate?.diagram && node?.courseUrl && (
|
||||
<Box bg={'myGray.300'} w={'1px'} h={'12px'} mx={1} />
|
||||
<Box bg={'myGray.300'} w={'1px'} h={'12px'} ml={1} mr={0.5} />
|
||||
)}
|
||||
{node?.courseUrl && !hasNewVersion && (
|
||||
<MyTooltip label={t('workflow:Node.Open_Node_Course')}>
|
||||
<MyIcon
|
||||
cursor={'pointer'}
|
||||
name="book"
|
||||
color={'primary.600'}
|
||||
w={'18px'}
|
||||
<MyIconButton
|
||||
ml={1}
|
||||
_hover={{
|
||||
color: 'primary.800'
|
||||
}}
|
||||
icon="book"
|
||||
color={'primary.600'}
|
||||
onClick={() => window.open(getDocPath(node.courseUrl || ''), '_blank')}
|
||||
/>
|
||||
</MyTooltip>
|
||||
@@ -378,7 +366,7 @@ const NodeCard = (props: Props) => {
|
||||
>
|
||||
<NodeDebugResponse nodeId={nodeId} debugResult={debugResult} />
|
||||
{Header}
|
||||
<Flex flexDirection={'column'} flex={1} my={!isFolded ? 4 : 0} gap={2}>
|
||||
<Flex flexDirection={'column'} flex={1} my={!isFolded ? 3 : 0} gap={2}>
|
||||
{!isFolded ? children : <Box h={4} />}
|
||||
</Flex>
|
||||
{RenderHandle}
|
||||
@@ -521,7 +509,7 @@ const MenuRender = React.memo(function MenuRender({
|
||||
className="nodrag controller-menu"
|
||||
display={'none'}
|
||||
flexDirection={'column'}
|
||||
gap={3}
|
||||
gap={2}
|
||||
position={'absolute'}
|
||||
top={'-20px'}
|
||||
right={0}
|
||||
@@ -532,16 +520,18 @@ const MenuRender = React.memo(function MenuRender({
|
||||
pt={'20px'}
|
||||
>
|
||||
{menuList.map((item) => (
|
||||
<Box key={item.icon}>
|
||||
<Button
|
||||
size={'xs'}
|
||||
variant={item.variant}
|
||||
leftIcon={<MyIcon name={item.icon as any} w={'13px'} />}
|
||||
onClick={item.onClick}
|
||||
>
|
||||
{t(item.label as any)}
|
||||
</Button>
|
||||
</Box>
|
||||
<Button
|
||||
key={item.icon}
|
||||
h={8}
|
||||
fontSize={'sm'}
|
||||
pl={2}
|
||||
pr={6}
|
||||
variant={item.variant}
|
||||
leftIcon={<MyIcon name={item.icon as any} w={'16px'} mr={-1} />}
|
||||
onClick={item.onClick}
|
||||
>
|
||||
{t(item.label as any)}
|
||||
</Button>
|
||||
))}
|
||||
</Box>
|
||||
<DebugInputModal />
|
||||
@@ -592,31 +582,32 @@ const NodeIntro = React.memo(function NodeIntro({
|
||||
<Box fontSize={'sm'} color={'myGray.500'} flex={'1 0 0'}>
|
||||
{t(intro as any)}
|
||||
</Box>
|
||||
<Flex
|
||||
p={'7px'}
|
||||
rounded={'sm'}
|
||||
alignItems={'center'}
|
||||
_hover={{
|
||||
bg: NodeIsTool ? 'myGray.100' : 'transparent'
|
||||
}}
|
||||
cursor={NodeIsTool ? 'pointer' : 'default'}
|
||||
onClick={() => {
|
||||
if (!NodeIsTool) return;
|
||||
onOpenIntroModal({
|
||||
defaultVal: intro,
|
||||
onSuccess(e) {
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'attr',
|
||||
key: 'intro',
|
||||
value: e
|
||||
});
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
<MyIcon name={'edit'} w={'18px'} opacity={NodeIsTool ? 1 : 0} />
|
||||
</Flex>
|
||||
{NodeIsTool && (
|
||||
<Flex
|
||||
p={'7px'}
|
||||
rounded={'sm'}
|
||||
alignItems={'center'}
|
||||
_hover={{
|
||||
bg: 'myGray.100'
|
||||
}}
|
||||
cursor={'pointer'}
|
||||
onClick={() => {
|
||||
onOpenIntroModal({
|
||||
defaultVal: intro,
|
||||
onSuccess(e) {
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'attr',
|
||||
key: 'intro',
|
||||
value: e
|
||||
});
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
<MyIcon name={'edit'} w={'18px'} />
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
<EditIntroModal maxLength={500} />
|
||||
</>
|
||||
|
||||
@@ -115,7 +115,6 @@ function Reference({
|
||||
content: workflowT('confirm_delete_field_tip')
|
||||
});
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||
|
||||
const keys = useMemo(() => {
|
||||
return inputs.map((input) => input.key);
|
||||
@@ -199,7 +198,7 @@ function Reference({
|
||||
/>
|
||||
|
||||
<MyIcon
|
||||
className="delete"
|
||||
className={'delete'}
|
||||
name={'delete'}
|
||||
w={'14px'}
|
||||
color={'myGray.500'}
|
||||
|
||||
@@ -69,7 +69,7 @@ const SelectDatasetRender = ({ inputs = [], item, nodeId }: RenderInputProps) =>
|
||||
w={'100%'}
|
||||
>
|
||||
<Button
|
||||
h={'36px'}
|
||||
h={10}
|
||||
leftIcon={<MyIcon name={'common/selectLight'} w={'14px'} />}
|
||||
onClick={onOpenDatasetSelect}
|
||||
>
|
||||
@@ -79,19 +79,20 @@ const SelectDatasetRender = ({ inputs = [], item, nodeId }: RenderInputProps) =>
|
||||
<Flex
|
||||
key={item._id}
|
||||
alignItems={'center'}
|
||||
h={'36px'}
|
||||
h={10}
|
||||
border={theme.borders.base}
|
||||
borderColor={'myGray.200'}
|
||||
px={2}
|
||||
borderRadius={'md'}
|
||||
>
|
||||
<Avatar src={item.avatar} w={'24px'}></Avatar>
|
||||
<Avatar src={item.avatar} w={'18px'} borderRadius={'xs'} />
|
||||
<Box
|
||||
ml={3}
|
||||
ml={1.5}
|
||||
flex={'1 0 0'}
|
||||
w={0}
|
||||
className="textEllipsis"
|
||||
fontWeight={'bold'}
|
||||
fontSize={['md', 'lg']}
|
||||
fontSize={['sm', 'sm']}
|
||||
>
|
||||
{item.name}
|
||||
</Box>
|
||||
|
||||
@@ -4,7 +4,6 @@ import { Box, Button, Flex } from '@chakra-ui/react';
|
||||
import { FlowNodeOutputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import OutputLabel from './Label';
|
||||
import { RenderOutputProps } from './type';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { SmallAddIcon } from '@chakra-ui/icons';
|
||||
import VariableTable from '../../NodePluginIO/VariableTable';
|
||||
@@ -18,11 +17,6 @@ import { defaultOutput } from './FieldEditModal';
|
||||
|
||||
const FieldEditModal = dynamic(() => import('./FieldEditModal'));
|
||||
|
||||
const RenderList: {
|
||||
types: FlowNodeOutputTypeEnum[];
|
||||
Component: React.ComponentType<RenderOutputProps>;
|
||||
}[] = [];
|
||||
|
||||
const RenderOutput = ({
|
||||
nodeId,
|
||||
flowOutputList
|
||||
@@ -77,7 +71,7 @@ const RenderOutput = ({
|
||||
alignItems={'center'}
|
||||
position={'relative'}
|
||||
>
|
||||
<Box position={'relative'} fontWeight={'medium'}>
|
||||
<Box position={'relative'} fontWeight={'medium'} fontSize={'sm'}>
|
||||
{t((addOutput.label || 'common:core.workflow.Custom outputs') as any)}
|
||||
</Box>
|
||||
{addOutput.description && <QuestionTip ml={1} label={addOutput.description} />}
|
||||
|
||||
@@ -21,7 +21,8 @@ const ValueTypeLabel = ({
|
||||
<Box
|
||||
bg={'myGray.100'}
|
||||
color={'myGray.500'}
|
||||
border={'base'}
|
||||
border={'1px solid'}
|
||||
borderColor={'myGray.200'}
|
||||
borderRadius={'sm'}
|
||||
ml={2}
|
||||
px={1}
|
||||
|
||||
@@ -6,7 +6,7 @@ export const minZoom = 0.1;
|
||||
export const maxZoom = 1.5;
|
||||
|
||||
export const connectionLineStyle: React.CSSProperties = {
|
||||
strokeWidth: 2,
|
||||
strokeWidth: 3,
|
||||
stroke: '#487FFF'
|
||||
};
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo } from 'react';
|
||||
import type { StartChatFnProps } from '@/components/core/chat/ChatContainer/type';
|
||||
import { streamFetch } from '@/web/common/api/fetch';
|
||||
import { getMaxHistoryLimitFromNodes } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { AppContext } from './context';
|
||||
@@ -11,43 +10,56 @@ import { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useChat } from '@/components/core/chat/ChatContainer/useChat';
|
||||
import { Box, BoxProps } from '@chakra-ui/react';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { AppChatConfigType } from '@fastgpt/global/core/app/type';
|
||||
import ChatBox from '@/components/core/chat/ChatContainer/ChatBox';
|
||||
import { ChatSiteItemType } from '@fastgpt/global/core/chat/type';
|
||||
import { useChatStore } from '@/web/core/chat/context/useChatStore';
|
||||
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { getInitChatInfo } from '@/web/core/chat/api';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
const PluginRunBox = dynamic(() => import('@/components/core/chat/ChatContainer/PluginRunBox'));
|
||||
|
||||
export const useChatTest = ({
|
||||
nodes,
|
||||
edges,
|
||||
chatConfig
|
||||
chatConfig,
|
||||
isReady
|
||||
}: {
|
||||
nodes: StoreNodeItemType[];
|
||||
edges: StoreEdgeItemType[];
|
||||
chatConfig: AppChatConfigType;
|
||||
isReady: boolean;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { userInfo } = useUserStore();
|
||||
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
||||
const [chatRecords, setChatRecords] = useState<ChatSiteItemType[]>([]);
|
||||
const { setChatId, chatId, appId } = useChatStore();
|
||||
const appDetail = useContextSelector(AppContext, (v) => v.appDetail);
|
||||
|
||||
const startChat = useMemoizedFn(
|
||||
async ({ messages, controller, generatingMessage, variables }: StartChatFnProps) => {
|
||||
/* get histories */
|
||||
const historyMaxLen = getMaxHistoryLimitFromNodes(nodes);
|
||||
async ({
|
||||
messages,
|
||||
responseChatItemId,
|
||||
controller,
|
||||
generatingMessage,
|
||||
variables
|
||||
}: StartChatFnProps) => {
|
||||
const histories = messages.slice(-1);
|
||||
|
||||
// 流请求,获取数据
|
||||
const { responseText, responseData } = await streamFetch({
|
||||
url: '/api/core/chat/chatTest',
|
||||
data: {
|
||||
// Send histories and user messages
|
||||
messages: messages.slice(-historyMaxLen - 2),
|
||||
messages: histories,
|
||||
nodes,
|
||||
edges,
|
||||
variables,
|
||||
appId: appDetail._id,
|
||||
appName: `调试-${appDetail.name}`,
|
||||
responseChatItemId,
|
||||
appId,
|
||||
appName: t('chat:chat_test_app', { name: appDetail.name }),
|
||||
chatId,
|
||||
chatConfig
|
||||
},
|
||||
onMessage: generatingMessage,
|
||||
@@ -58,77 +70,84 @@ export const useChatTest = ({
|
||||
}
|
||||
);
|
||||
|
||||
const setChatBoxData = useContextSelector(ChatItemContext, (v) => v.setChatBoxData);
|
||||
const resetVariables = useContextSelector(ChatItemContext, (v) => v.resetVariables);
|
||||
const clearChatRecords = useContextSelector(ChatItemContext, (v) => v.clearChatRecords);
|
||||
|
||||
const pluginInputs = useMemo(() => {
|
||||
return nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput)?.inputs || [];
|
||||
}, [nodes]);
|
||||
|
||||
const { ChatBoxRef, variablesForm, pluginRunTab, setPluginRunTab, clearChatRecords } = useChat();
|
||||
// Set chat box data
|
||||
useEffect(() => {
|
||||
setChatBoxData({
|
||||
userAvatar: userInfo?.avatar,
|
||||
app: {
|
||||
chatConfig,
|
||||
name: appDetail.name,
|
||||
avatar: appDetail.avatar,
|
||||
type: appDetail.type,
|
||||
pluginInputs
|
||||
}
|
||||
});
|
||||
}, [
|
||||
appDetail.avatar,
|
||||
appDetail.name,
|
||||
appDetail.type,
|
||||
chatConfig,
|
||||
pluginInputs,
|
||||
setChatBoxData,
|
||||
userInfo?.avatar
|
||||
]);
|
||||
|
||||
// Mock ScrollData
|
||||
const ScrollData = useCallback(
|
||||
({
|
||||
children,
|
||||
ScrollContainerRef,
|
||||
...props
|
||||
}: {
|
||||
ScrollContainerRef?: React.RefObject<HTMLDivElement>;
|
||||
children: React.ReactNode;
|
||||
} & BoxProps) => {
|
||||
return (
|
||||
<Box ref={ScrollContainerRef} {...props} overflow={'overlay'}>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
// init chat data
|
||||
const { loading } = useRequest2(
|
||||
async () => {
|
||||
if (!appId || !chatId) return;
|
||||
const res = await getInitChatInfo({ appId, chatId });
|
||||
resetVariables({
|
||||
variables: res.variables
|
||||
});
|
||||
},
|
||||
[]
|
||||
{
|
||||
manual: false,
|
||||
refreshDeps: [appId, chatId]
|
||||
}
|
||||
);
|
||||
|
||||
const restartChat = useCallback(() => {
|
||||
clearChatRecords();
|
||||
setChatId();
|
||||
}, [clearChatRecords, setChatId]);
|
||||
|
||||
const CustomChatContainer = useMemoizedFn(() =>
|
||||
appDetail.type === AppTypeEnum.plugin ? (
|
||||
<Box p={5}>
|
||||
<PluginRunBox
|
||||
pluginInputs={pluginInputs}
|
||||
variablesForm={variablesForm}
|
||||
histories={chatRecords}
|
||||
setHistories={setChatRecords}
|
||||
appId={appDetail._id}
|
||||
chatConfig={appDetail.chatConfig}
|
||||
tab={pluginRunTab}
|
||||
setTab={setPluginRunTab}
|
||||
onNewChat={() => {
|
||||
clearChatRecords();
|
||||
setChatRecords([]);
|
||||
}}
|
||||
appId={appId}
|
||||
chatId={chatId}
|
||||
onNewChat={restartChat}
|
||||
onStartChat={startChat}
|
||||
/>
|
||||
</Box>
|
||||
) : (
|
||||
<ChatBox
|
||||
ref={ChatBoxRef}
|
||||
ScrollData={ScrollData}
|
||||
chatHistories={chatRecords}
|
||||
setChatHistories={setChatRecords}
|
||||
variablesForm={variablesForm}
|
||||
appId={appDetail._id}
|
||||
appAvatar={appDetail.avatar}
|
||||
userAvatar={userInfo?.avatar}
|
||||
isReady={isReady}
|
||||
appId={appId}
|
||||
chatId={chatId}
|
||||
showMarkIcon
|
||||
chatType="chat"
|
||||
showRawSource
|
||||
showNodeStatus
|
||||
chatConfig={chatConfig}
|
||||
onStartChat={startChat}
|
||||
onDelMessage={() => {}}
|
||||
/>
|
||||
)
|
||||
);
|
||||
|
||||
return {
|
||||
restartChat: clearChatRecords,
|
||||
ChatContainer: CustomChatContainer,
|
||||
chatRecords,
|
||||
pluginRunTab,
|
||||
setPluginRunTab
|
||||
restartChat,
|
||||
loading
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import Loading from '@fastgpt/web/components/common/MyLoading';
|
||||
@@ -7,6 +7,7 @@ import NextHead from '@/components/common/NextHead';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import AppContextProvider, { AppContext } from './components/context';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import { useChatStore } from '@/web/core/chat/context/useChatStore';
|
||||
|
||||
const SimpleEdit = dynamic(() => import('./components/SimpleApp'), {
|
||||
ssr: false,
|
||||
@@ -22,7 +23,13 @@ const Plugin = dynamic(() => import('./components/Plugin'), {
|
||||
});
|
||||
|
||||
const AppDetail = () => {
|
||||
const { appDetail } = useContextSelector(AppContext, (e) => e);
|
||||
const { setAppId, setSource } = useChatStore();
|
||||
const appDetail = useContextSelector(AppContext, (e) => e.appDetail);
|
||||
|
||||
useEffect(() => {
|
||||
setSource('test');
|
||||
appDetail._id && setAppId(appDetail._id);
|
||||
}, [appDetail._id, setSource, setAppId]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -16,7 +16,7 @@ import { AppFolderTypeList, AppTypeEnum } from '@fastgpt/global/core/app/constan
|
||||
import { useFolderDrag } from '@/components/common/folder/useFolderDrag';
|
||||
import dynamic from 'next/dynamic';
|
||||
import type { EditResourceInfoFormType } from '@/components/common/Modal/EditResourceModal';
|
||||
import MyMenu from '@fastgpt/web/components/common/MyMenu';
|
||||
import MyMenu, { MenuItemType } from '@fastgpt/web/components/common/MyMenu';
|
||||
import { AppPermissionList } from '@fastgpt/global/support/permission/app/constant';
|
||||
import {
|
||||
deleteAppCollaborators,
|
||||
@@ -33,7 +33,7 @@ import type { EditHttpPluginProps } from './HttpPluginEditModal';
|
||||
import { postCopyApp } from '@/web/core/app/api/app';
|
||||
import { formatTimeToChatTime } from '@fastgpt/global/common/string/time';
|
||||
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
||||
import { useChatStore } from '@/web/core/chat/context/storeChat';
|
||||
import { useChatStore } from '@/web/core/chat/context/useChatStore';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
|
||||
const HttpEditModal = dynamic(() => import('./HttpPluginEditModal'));
|
||||
@@ -45,7 +45,6 @@ const ListItem = () => {
|
||||
const { isPc } = useSystem();
|
||||
|
||||
const { loadAndGetTeamMembers } = useUserStore();
|
||||
const { lastChatAppId, setLastChatAppId } = useChatStore();
|
||||
|
||||
const { openConfirm: openMoveConfirm, ConfirmModal: MoveConfirmModal } = useConfirm({
|
||||
type: 'common',
|
||||
@@ -87,6 +86,8 @@ const ListItem = () => {
|
||||
const { openConfirm: openConfirmDel, ConfirmModal: DelConfirmModal } = useConfirm({
|
||||
type: 'delete'
|
||||
});
|
||||
|
||||
const { lastChatAppId, setLastChatAppId } = useChatStore();
|
||||
const { runAsync: onclickDelApp } = useRequest2(
|
||||
(id: string) => {
|
||||
if (id === lastChatAppId) {
|
||||
@@ -259,6 +260,7 @@ const ListItem = () => {
|
||||
: app.permission.hasWritePer) && (
|
||||
<Box className="more" display={['', 'none']}>
|
||||
<MyMenu
|
||||
size={'xs'}
|
||||
Button={
|
||||
<IconButton
|
||||
size={'xsSquare'}
|
||||
@@ -274,6 +276,7 @@ const ListItem = () => {
|
||||
children: [
|
||||
{
|
||||
icon: 'core/chat/chatLight',
|
||||
type: 'grayBg' as MenuItemType,
|
||||
label: t('app:go_to_chat'),
|
||||
onClick: () => {
|
||||
router.push(`/chat?appId=${app._id}`);
|
||||
@@ -289,6 +292,7 @@ const ListItem = () => {
|
||||
children: [
|
||||
{
|
||||
icon: 'core/chat/chatLight',
|
||||
type: 'grayBg' as MenuItemType,
|
||||
label: t('app:go_to_run'),
|
||||
onClick: () => {
|
||||
router.push(`/chat?appId=${app._id}`);
|
||||
@@ -304,6 +308,7 @@ const ListItem = () => {
|
||||
children: [
|
||||
{
|
||||
icon: 'edit',
|
||||
type: 'grayBg' as MenuItemType,
|
||||
label: t('common:dataset.Edit Info'),
|
||||
onClick: () => {
|
||||
if (app.type === AppTypeEnum.httpPlugin) {
|
||||
@@ -331,6 +336,7 @@ const ListItem = () => {
|
||||
: [
|
||||
{
|
||||
icon: 'common/file/move',
|
||||
type: 'grayBg' as MenuItemType,
|
||||
label: t('common:common.folder.Move to'),
|
||||
onClick: () => setMoveAppId(app._id)
|
||||
}
|
||||
@@ -339,6 +345,7 @@ const ListItem = () => {
|
||||
? [
|
||||
{
|
||||
icon: 'support/team/key',
|
||||
type: 'grayBg' as MenuItemType,
|
||||
label: t('common:permission.Permission'),
|
||||
onClick: () => setEditPerAppIndex(index)
|
||||
}
|
||||
@@ -355,6 +362,7 @@ const ListItem = () => {
|
||||
children: [
|
||||
{
|
||||
icon: 'copy',
|
||||
type: 'grayBg' as MenuItemType,
|
||||
label: t('app:copy_one_app'),
|
||||
onClick: () =>
|
||||
openConfirmCopy(() => onclickCopy({ appId: app._id }))()
|
||||
@@ -394,9 +402,7 @@ const ListItem = () => {
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
|
||||
{myApps.length === 0 && <EmptyTip text={t('common:core.app.no_app')} pt={'30vh'} />}
|
||||
|
||||
<DelConfirmModal />
|
||||
<ConfirmCopyModal />
|
||||
{!!editedApp && (
|
||||
@@ -463,5 +469,4 @@ const ListItem = () => {
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListItem;
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Flex,
|
||||
Button,
|
||||
useDisclosure,
|
||||
Input,
|
||||
InputGroup,
|
||||
InputLeftElement
|
||||
} from '@chakra-ui/react';
|
||||
import { Box, Flex, Button, useDisclosure, Input, InputGroup } from '@chakra-ui/react';
|
||||
import { AddIcon } from '@chakra-ui/icons';
|
||||
import { serviceSideProps } from '@/web/common/utils/i18n';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
@@ -95,15 +87,23 @@ const MyApps = () => {
|
||||
|
||||
const RenderSearchInput = useMemo(
|
||||
() => (
|
||||
<InputGroup maxW={['auto', '250px']}>
|
||||
<InputLeftElement h={'full'} alignItems={'center'} display={'flex'}>
|
||||
<MyIcon name={'common/searchLight'} w={'1rem'} />
|
||||
</InputLeftElement>
|
||||
<InputGroup maxW={['auto', '250px']} position={'relative'}>
|
||||
<MyIcon
|
||||
position={'absolute'}
|
||||
zIndex={10}
|
||||
name={'common/searchLight'}
|
||||
w={'1rem'}
|
||||
color={'myGray.600'}
|
||||
left={2.5}
|
||||
top={'50%'}
|
||||
transform={'translateY(-50%)'}
|
||||
/>
|
||||
<Input
|
||||
value={searchKey}
|
||||
onChange={(e) => setSearchKey(e.target.value)}
|
||||
placeholder={appT('search_app')}
|
||||
maxLength={30}
|
||||
pl={8}
|
||||
bg={'white'}
|
||||
/>
|
||||
</InputGroup>
|
||||
@@ -179,66 +179,67 @@ const MyApps = () => {
|
||||
|
||||
{isPc && RenderSearchInput}
|
||||
|
||||
{userInfo?.team.permission.hasWritePer &&
|
||||
folderDetail?.type !== AppTypeEnum.httpPlugin && (
|
||||
<MyMenu
|
||||
iconSize="2rem"
|
||||
Button={
|
||||
<Button variant={'primary'} leftIcon={<AddIcon />}>
|
||||
<Box>{t('common:common.Create New')}</Box>
|
||||
</Button>
|
||||
{(folderDetail
|
||||
? folderDetail.permission.hasWritePer && folderDetail?.type !== AppTypeEnum.httpPlugin
|
||||
: userInfo?.team.permission.hasWritePer) && (
|
||||
<MyMenu
|
||||
size="md"
|
||||
Button={
|
||||
<Button variant={'primary'} leftIcon={<AddIcon />}>
|
||||
<Box>{t('common:common.Create New')}</Box>
|
||||
</Button>
|
||||
}
|
||||
menuList={[
|
||||
{
|
||||
children: [
|
||||
{
|
||||
icon: 'core/app/simpleBot',
|
||||
label: t('app:type.Simple bot'),
|
||||
description: t('app:type.Create simple bot tip'),
|
||||
onClick: () => setCreateAppType(AppTypeEnum.simple)
|
||||
},
|
||||
{
|
||||
icon: 'core/app/type/workflowFill',
|
||||
label: t('app:type.Workflow bot'),
|
||||
description: t('app:type.Create workflow tip'),
|
||||
onClick: () => setCreateAppType(AppTypeEnum.workflow)
|
||||
},
|
||||
{
|
||||
icon: 'core/app/type/pluginFill',
|
||||
label: t('app:type.Plugin'),
|
||||
description: t('app:type.Create one plugin tip'),
|
||||
onClick: () => setCreateAppType(AppTypeEnum.plugin)
|
||||
},
|
||||
{
|
||||
icon: 'core/app/type/httpPluginFill',
|
||||
label: t('app:type.Http plugin'),
|
||||
description: t('app:type.Create http plugin tip'),
|
||||
onClick: onOpenCreateHttpPlugin
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
icon: '/imgs/app/templateFill.svg',
|
||||
label: t('app:template_market'),
|
||||
description: t('app:template_market_description'),
|
||||
onClick: () => setTemplateModalType('all')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
icon: FolderIcon,
|
||||
label: t('common:Folder'),
|
||||
onClick: () => setEditFolder({})
|
||||
}
|
||||
]
|
||||
}
|
||||
menuList={[
|
||||
{
|
||||
children: [
|
||||
{
|
||||
icon: 'core/app/simpleBot',
|
||||
label: t('app:type.Simple bot'),
|
||||
description: t('app:type.Create simple bot tip'),
|
||||
onClick: () => setCreateAppType(AppTypeEnum.simple)
|
||||
},
|
||||
{
|
||||
icon: 'core/app/type/workflowFill',
|
||||
label: t('app:type.Workflow bot'),
|
||||
description: t('app:type.Create workflow tip'),
|
||||
onClick: () => setCreateAppType(AppTypeEnum.workflow)
|
||||
},
|
||||
{
|
||||
icon: 'core/app/type/pluginFill',
|
||||
label: t('app:type.Plugin'),
|
||||
description: t('app:type.Create one plugin tip'),
|
||||
onClick: () => setCreateAppType(AppTypeEnum.plugin)
|
||||
},
|
||||
{
|
||||
icon: 'core/app/type/httpPluginFill',
|
||||
label: t('app:type.Http plugin'),
|
||||
description: t('app:type.Create http plugin tip'),
|
||||
onClick: onOpenCreateHttpPlugin
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
icon: '/imgs/app/templateFill.svg',
|
||||
label: t('app:template_market'),
|
||||
description: t('app:template_market_description'),
|
||||
onClick: () => setTemplateModalType('all')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
icon: FolderIcon,
|
||||
label: t('common:Folder'),
|
||||
onClick: () => setEditFolder({})
|
||||
}
|
||||
]
|
||||
}
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
{!isPc && <Box mt={2}>{RenderSearchInput}</Box>}
|
||||
|
||||
@@ -14,6 +14,8 @@ import { useContextSelector } from 'use-context-selector';
|
||||
import { ChatContext } from '@/web/core/chat/context/chatContext';
|
||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||
import { formatTimeToChatTime } from '@fastgpt/global/common/string/time';
|
||||
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
|
||||
import { useChatStore } from '@/web/core/chat/context/useChatStore';
|
||||
|
||||
type HistoryItemType = {
|
||||
id: string;
|
||||
@@ -23,25 +25,7 @@ type HistoryItemType = {
|
||||
updateTime: Date;
|
||||
};
|
||||
|
||||
const ChatHistorySlider = ({
|
||||
appId,
|
||||
appName,
|
||||
appAvatar,
|
||||
confirmClearText,
|
||||
onDelHistory,
|
||||
onClearHistory,
|
||||
onSetHistoryTop,
|
||||
onSetCustomTitle
|
||||
}: {
|
||||
appId?: string;
|
||||
appName: string;
|
||||
appAvatar: string;
|
||||
confirmClearText: string;
|
||||
onDelHistory: (e: { chatId: string }) => void;
|
||||
onClearHistory: () => void;
|
||||
onSetHistoryTop?: (e: { chatId: string; top: boolean }) => void;
|
||||
onSetCustomTitle?: (e: { chatId: string; title: string }) => void;
|
||||
}) => {
|
||||
const ChatHistorySlider = ({ confirmClearText }: { confirmClearText: string }) => {
|
||||
const theme = useTheme();
|
||||
const router = useRouter();
|
||||
const isUserChatPage = router.pathname === '/chat';
|
||||
@@ -51,13 +35,17 @@ const ChatHistorySlider = ({
|
||||
const { isPc } = useSystem();
|
||||
const { userInfo } = useUserStore();
|
||||
|
||||
const {
|
||||
onChangeChatId,
|
||||
chatId: activeChatId,
|
||||
isLoading,
|
||||
ScrollData,
|
||||
histories
|
||||
} = useContextSelector(ChatContext, (v) => v);
|
||||
const { appId, chatId: activeChatId } = useChatStore();
|
||||
const onChangeChatId = useContextSelector(ChatContext, (v) => v.onChangeChatId);
|
||||
const isLoading = useContextSelector(ChatContext, (v) => v.isLoading);
|
||||
const ScrollData = useContextSelector(ChatContext, (v) => v.ScrollData);
|
||||
const histories = useContextSelector(ChatContext, (v) => v.histories);
|
||||
const onDelHistory = useContextSelector(ChatContext, (v) => v.onDelHistory);
|
||||
const onClearHistory = useContextSelector(ChatContext, (v) => v.onClearHistories);
|
||||
const onUpdateHistory = useContextSelector(ChatContext, (v) => v.onUpdateHistory);
|
||||
|
||||
const appName = useContextSelector(ChatItemContext, (v) => v.chatBoxData?.app.name);
|
||||
const appAvatar = useContextSelector(ChatItemContext, (v) => v.chatBoxData?.app.avatar);
|
||||
|
||||
const concatHistory = useMemo(() => {
|
||||
const formatHistories: HistoryItemType[] = histories.map((item) => {
|
||||
@@ -169,14 +157,13 @@ const ChatHistorySlider = ({
|
||||
size={'mdSquare'}
|
||||
aria-label={''}
|
||||
borderRadius={'50%'}
|
||||
icon={<MyIcon name={'common/clearLight'} w={'16px'} />}
|
||||
onClick={() =>
|
||||
openConfirm(() => {
|
||||
onClearHistory();
|
||||
})()
|
||||
}
|
||||
>
|
||||
<MyIcon name={'common/clearLight'} w={'16px'} />
|
||||
</IconButton>
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
@@ -249,45 +236,38 @@ const ChatHistorySlider = ({
|
||||
menuList={[
|
||||
{
|
||||
children: [
|
||||
...(onSetHistoryTop
|
||||
? [
|
||||
{
|
||||
label: item.top
|
||||
? t('common:core.chat.Unpin')
|
||||
: t('common:core.chat.Pin'),
|
||||
icon: 'core/chat/setTopLight',
|
||||
onClick: () => {
|
||||
onSetHistoryTop({
|
||||
chatId: item.id,
|
||||
top: !item.top
|
||||
});
|
||||
}
|
||||
}
|
||||
]
|
||||
: []),
|
||||
...(onSetCustomTitle
|
||||
? [
|
||||
{
|
||||
label: t('common:common.Custom Title'),
|
||||
icon: 'common/customTitleLight',
|
||||
onClick: () => {
|
||||
onOpenModal({
|
||||
defaultVal: item.customTitle || item.title,
|
||||
onSuccess: (e) =>
|
||||
onSetCustomTitle({
|
||||
chatId: item.id,
|
||||
title: e
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
]
|
||||
: []),
|
||||
{
|
||||
label: item.top
|
||||
? t('common:core.chat.Unpin')
|
||||
: t('common:core.chat.Pin'),
|
||||
icon: 'core/chat/setTopLight',
|
||||
onClick: () => {
|
||||
onUpdateHistory({
|
||||
chatId: item.id,
|
||||
top: !item.top
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
label: t('common:common.Custom Title'),
|
||||
icon: 'common/customTitleLight',
|
||||
onClick: () => {
|
||||
onOpenModal({
|
||||
defaultVal: item.customTitle || item.title,
|
||||
onSuccess: (e) =>
|
||||
onUpdateHistory({
|
||||
chatId: item.id,
|
||||
customTitle: e
|
||||
})
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('common:common.Delete'),
|
||||
icon: 'delete',
|
||||
onClick: () => {
|
||||
onDelHistory({ chatId: item.id });
|
||||
onDelHistory(item.id);
|
||||
if (item.id === activeChatId) {
|
||||
onChangeChatId();
|
||||
}
|
||||
|
||||
@@ -6,12 +6,16 @@ import { Box, Grid, Stack } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { PluginRunBoxTabEnum } from '@/components/core/chat/ChatContainer/PluginRunBox/constants';
|
||||
import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
|
||||
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
|
||||
const CustomPluginRunBox = (props: PluginRunBoxProps) => {
|
||||
const { tab, setTab } = props;
|
||||
const { isPc } = useSystem();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const tab = useContextSelector(ChatItemContext, (v) => v.pluginRunTab);
|
||||
const setTab = useContextSelector(ChatItemContext, (v) => v.setPluginRunTab);
|
||||
|
||||
useEffect(() => {
|
||||
if (isPc && tab === PluginRunBoxTabEnum.input) {
|
||||
setTab(PluginRunBoxTabEnum.output);
|
||||
@@ -24,7 +28,7 @@ const CustomPluginRunBox = (props: PluginRunBoxProps) => {
|
||||
<Box color={'myGray.900'} mb={5}>
|
||||
{t('common:common.Input')}
|
||||
</Box>
|
||||
<PluginRunBox {...props} tab={PluginRunBoxTabEnum.input} />
|
||||
<PluginRunBox {...props} showTab={PluginRunBoxTabEnum.input} />
|
||||
</Box>
|
||||
<Stack px={3} py={4} h={'100%'} alignItems={'flex-start'} w={'100%'} overflow={'auto'}>
|
||||
<Box display={'inline-block'}>
|
||||
|
||||
@@ -41,7 +41,6 @@ const SliderApps = ({ apps, activeAppId }: { apps: AppListItemType[]; activeAppI
|
||||
router.replace({
|
||||
query: {
|
||||
...router.query,
|
||||
chatId: '',
|
||||
appId
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import NextHead from '@/components/common/NextHead';
|
||||
import { useRouter } from 'next/router';
|
||||
import { delChatRecordById, getInitChatInfo } from '@/web/core/chat/api';
|
||||
import { getInitChatInfo } from '@/web/core/chat/api';
|
||||
import { Box, Flex, Drawer, DrawerOverlay, DrawerContent, useTheme } from '@chakra-ui/react';
|
||||
import { streamFetch } from '@/web/common/api/fetch';
|
||||
import { useChatStore } from '@/web/core/chat/context/storeChat';
|
||||
import { useChatStore } from '@/web/core/chat/context/useChatStore';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
@@ -21,103 +21,75 @@ import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
||||
import { getMyApps } from '@/web/core/app/api';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
|
||||
import { useCreation, useMount } from 'ahooks';
|
||||
import { useMount } from 'ahooks';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
|
||||
import { defaultChatData, GetChatTypeEnum } from '@/global/core/chat/constants';
|
||||
import ChatContextProvider, { ChatContext } from '@/web/core/chat/context/chatContext';
|
||||
import { AppListItemType } from '@fastgpt/global/core/app/type';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useChat } from '@/components/core/chat/ChatContainer/useChat';
|
||||
import ChatBox from '@/components/core/chat/ChatContainer/ChatBox';
|
||||
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
||||
import { InitChatResponse } from '@/global/core/chat/api';
|
||||
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import ChatItemContextProvider, { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
|
||||
import ChatRecordContextProvider, {
|
||||
ChatRecordContext
|
||||
} from '@/web/core/chat/context/chatRecordContext';
|
||||
import { InitChatResponse } from '@/global/core/chat/api';
|
||||
|
||||
const CustomPluginRunBox = dynamic(() => import('./components/CustomPluginRunBox'));
|
||||
|
||||
type Props = { appId: string; chatId: string };
|
||||
|
||||
const Chat = ({
|
||||
appId,
|
||||
chatId,
|
||||
myApps
|
||||
}: Props & {
|
||||
myApps: AppListItemType[];
|
||||
}) => {
|
||||
const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
||||
const router = useRouter();
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
const { userInfo } = useUserStore();
|
||||
const { isPc } = useSystem();
|
||||
const { setLastChatAppId } = useChatStore();
|
||||
const { userInfo } = useUserStore();
|
||||
const { setLastChatAppId, chatId, appId, outLinkAuthData } = useChatStore();
|
||||
|
||||
const {
|
||||
onUpdateHistory,
|
||||
onClearHistories,
|
||||
onDelHistory,
|
||||
isOpenSlider,
|
||||
onCloseSlider,
|
||||
forbidLoadChat,
|
||||
onChangeChatId,
|
||||
onUpdateHistoryTitle
|
||||
} = useContextSelector(ChatContext, (v) => v);
|
||||
const isOpenSlider = useContextSelector(ChatContext, (v) => v.isOpenSlider);
|
||||
const onCloseSlider = useContextSelector(ChatContext, (v) => v.onCloseSlider);
|
||||
const forbidLoadChat = useContextSelector(ChatContext, (v) => v.forbidLoadChat);
|
||||
const onChangeChatId = useContextSelector(ChatContext, (v) => v.onChangeChatId);
|
||||
const onUpdateHistoryTitle = useContextSelector(ChatContext, (v) => v.onUpdateHistoryTitle);
|
||||
|
||||
const params = useCreation(() => {
|
||||
return {
|
||||
chatId,
|
||||
appId,
|
||||
type: GetChatTypeEnum.normal
|
||||
};
|
||||
}, [appId, chatId]);
|
||||
const {
|
||||
ChatBoxRef,
|
||||
variablesForm,
|
||||
pluginRunTab,
|
||||
setPluginRunTab,
|
||||
resetVariables,
|
||||
chatRecords,
|
||||
ScrollData,
|
||||
setChatRecords,
|
||||
totalRecordsCount
|
||||
} = useChat(params);
|
||||
const resetVariables = useContextSelector(ChatItemContext, (v) => v.resetVariables);
|
||||
const isPlugin = useContextSelector(ChatItemContext, (v) => v.isPlugin);
|
||||
const setChatBoxData = useContextSelector(ChatItemContext, (v) => v.setChatBoxData);
|
||||
|
||||
// get chat app info
|
||||
const [chatData, setChatData] = useState<InitChatResponse>(defaultChatData);
|
||||
const isPlugin = chatData.app.type === AppTypeEnum.plugin;
|
||||
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
|
||||
const totalRecordsCount = useContextSelector(ChatRecordContext, (v) => v.totalRecordsCount);
|
||||
|
||||
// Load chat init data
|
||||
const [chatData, setChatData] = useState<InitChatResponse>(defaultChatData);
|
||||
const { loading: isLoading } = useRequest2(
|
||||
async () => {
|
||||
if (!appId || forbidLoadChat.current) return;
|
||||
|
||||
const res = await getInitChatInfo({ appId, chatId });
|
||||
res.userAvatar = userInfo?.avatar;
|
||||
|
||||
setChatData(res);
|
||||
setChatBoxData(res);
|
||||
// reset chat variables
|
||||
resetVariables({
|
||||
variables: res.variables
|
||||
});
|
||||
|
||||
setLastChatAppId(appId);
|
||||
},
|
||||
{
|
||||
manual: false,
|
||||
refreshDeps: [appId, chatId],
|
||||
onError(e: any) {
|
||||
setLastChatAppId('');
|
||||
|
||||
// reset all chat tore
|
||||
if (e?.code === 501) {
|
||||
setLastChatAppId('');
|
||||
router.replace('/app/list');
|
||||
} else {
|
||||
router.replace({
|
||||
query: {
|
||||
...router.query,
|
||||
appId: myApps[0]?._id,
|
||||
chatId: ''
|
||||
appId: myApps[0]?._id
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -136,7 +108,6 @@ const Chat = ({
|
||||
generatingMessage,
|
||||
variables
|
||||
}: StartChatFnProps) => {
|
||||
const completionChatId = chatId || getNanoid();
|
||||
// Just send a user prompt
|
||||
const histories = messages.slice(-1);
|
||||
const { responseText, responseData } = await streamFetch({
|
||||
@@ -145,7 +116,7 @@ const Chat = ({
|
||||
variables,
|
||||
responseChatItemId,
|
||||
appId,
|
||||
chatId: completionChatId
|
||||
chatId
|
||||
},
|
||||
onMessage: generatingMessage,
|
||||
abortCtrl: controller
|
||||
@@ -154,10 +125,7 @@ const Chat = ({
|
||||
const newTitle = getChatTitleFromChatMessage(GPTMessages2Chats(histories)[0]);
|
||||
|
||||
// new chat
|
||||
if (completionChatId !== chatId && controller.signal.reason !== 'leave') {
|
||||
onChangeChatId(completionChatId, true);
|
||||
}
|
||||
onUpdateHistoryTitle({ chatId: completionChatId, newTitle });
|
||||
onUpdateHistoryTitle({ chatId, newTitle });
|
||||
// update chat window
|
||||
setChatData((state) => ({
|
||||
...state,
|
||||
@@ -166,32 +134,13 @@ const Chat = ({
|
||||
|
||||
return { responseText, responseData, isNewChat: forbidLoadChat.current };
|
||||
},
|
||||
[chatId, appId, onUpdateHistoryTitle, forbidLoadChat, onChangeChatId]
|
||||
[chatId, appId, onUpdateHistoryTitle, forbidLoadChat]
|
||||
);
|
||||
const loading = isLoading;
|
||||
|
||||
const RenderHistorySlider = useMemo(() => {
|
||||
const Children = (
|
||||
<ChatHistorySlider
|
||||
confirmClearText={t('common:core.chat.Confirm to clear history')}
|
||||
appId={appId}
|
||||
appName={chatData.app.name}
|
||||
appAvatar={chatData.app.avatar}
|
||||
onDelHistory={(e) => onDelHistory({ ...e, appId })}
|
||||
onClearHistory={() => {
|
||||
onClearHistories({ appId });
|
||||
}}
|
||||
onSetHistoryTop={(e) => {
|
||||
onUpdateHistory({ ...e, appId });
|
||||
}}
|
||||
onSetCustomTitle={async (e) => {
|
||||
onUpdateHistory({
|
||||
appId,
|
||||
chatId: e.chatId,
|
||||
customTitle: e.title
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<ChatHistorySlider confirmClearText={t('common:core.chat.Confirm to clear history')} />
|
||||
);
|
||||
|
||||
return isPc || !appId ? (
|
||||
@@ -208,18 +157,7 @@ const Chat = ({
|
||||
<DrawerContent maxWidth={'75vw'}>{Children}</DrawerContent>
|
||||
</Drawer>
|
||||
);
|
||||
}, [
|
||||
appId,
|
||||
chatData.app.avatar,
|
||||
chatData.app.name,
|
||||
isOpenSlider,
|
||||
isPc,
|
||||
onClearHistories,
|
||||
onCloseSlider,
|
||||
onDelHistory,
|
||||
onUpdateHistory,
|
||||
t
|
||||
]);
|
||||
}, [appId, isOpenSlider, isPc, onCloseSlider, t]);
|
||||
|
||||
return (
|
||||
<Flex h={'100%'}>
|
||||
@@ -257,33 +195,20 @@ const Chat = ({
|
||||
<Box flex={'1 0 0'} bg={'white'}>
|
||||
{isPlugin ? (
|
||||
<CustomPluginRunBox
|
||||
pluginInputs={chatData.app.pluginInputs}
|
||||
variablesForm={variablesForm}
|
||||
histories={chatRecords}
|
||||
setHistories={setChatRecords}
|
||||
appId={chatData.appId}
|
||||
chatConfig={chatData.app.chatConfig}
|
||||
tab={pluginRunTab}
|
||||
setTab={setPluginRunTab}
|
||||
appId={appId}
|
||||
chatId={chatId}
|
||||
outLinkAuthData={outLinkAuthData}
|
||||
onNewChat={() => onChangeChatId(getNanoid())}
|
||||
onStartChat={onStartChat}
|
||||
/>
|
||||
) : (
|
||||
<ChatBox
|
||||
ScrollData={ScrollData}
|
||||
ref={ChatBoxRef}
|
||||
chatHistories={chatRecords}
|
||||
setChatHistories={setChatRecords}
|
||||
variablesForm={variablesForm}
|
||||
showEmptyIntro
|
||||
appAvatar={chatData.app.avatar}
|
||||
userAvatar={userInfo?.avatar}
|
||||
chatConfig={chatData.app?.chatConfig}
|
||||
feedbackType={'user'}
|
||||
onStartChat={onStartChat}
|
||||
onDelMessage={({ contentId }) => delChatRecordById({ contentId, appId, chatId })}
|
||||
appId={appId}
|
||||
chatId={chatId}
|
||||
outLinkAuthData={outLinkAuthData}
|
||||
showEmptyIntro
|
||||
feedbackType={'user'}
|
||||
onStartChat={onStartChat}
|
||||
chatType={'chat'}
|
||||
showRawSource
|
||||
showNodeStatus
|
||||
@@ -297,13 +222,12 @@ const Chat = ({
|
||||
);
|
||||
};
|
||||
|
||||
const Render = (props: Props) => {
|
||||
const Render = (props: { appId: string }) => {
|
||||
const { appId } = props;
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const router = useRouter();
|
||||
|
||||
const { lastChatAppId, lastChatId } = useChatStore();
|
||||
const { source, chatId, lastChatAppId, setSource, setAppId } = useChatStore();
|
||||
|
||||
const { data: myApps = [], runAsync: loadMyApps } = useRequest2(
|
||||
() => getMyApps({ getRecentlyChat: true }),
|
||||
@@ -316,16 +240,6 @@ const Render = (props: Props) => {
|
||||
useMount(async () => {
|
||||
// pc: redirect to latest model chat
|
||||
if (!appId) {
|
||||
if (lastChatAppId) {
|
||||
return router.replace({
|
||||
query: {
|
||||
...router.query,
|
||||
appId: lastChatAppId,
|
||||
chatId: lastChatId
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const apps = await loadMyApps();
|
||||
if (apps.length === 0) {
|
||||
toast({
|
||||
@@ -337,27 +251,45 @@ const Render = (props: Props) => {
|
||||
router.replace({
|
||||
query: {
|
||||
...router.query,
|
||||
appId: apps[0]._id,
|
||||
chatId: ''
|
||||
appId: lastChatAppId || apps[0]._id
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
setSource('online');
|
||||
});
|
||||
// Watch appId
|
||||
useEffect(() => {
|
||||
setAppId(appId);
|
||||
}, [appId, setAppId]);
|
||||
|
||||
const providerParams = useMemo(() => ({ appId, source: ChatSourceEnum.online }), [appId]);
|
||||
return (
|
||||
<ChatContextProvider params={providerParams}>
|
||||
<Chat {...props} myApps={myApps} />
|
||||
</ChatContextProvider>
|
||||
const chatHistoryProviderParams = useMemo(
|
||||
() => ({ appId, source: ChatSourceEnum.online }),
|
||||
[appId]
|
||||
);
|
||||
const chatRecordProviderParams = useMemo(() => {
|
||||
return {
|
||||
appId,
|
||||
type: GetChatTypeEnum.normal,
|
||||
chatId: chatId
|
||||
};
|
||||
}, [appId, chatId]);
|
||||
|
||||
return source === ChatSourceEnum.online ? (
|
||||
<ChatContextProvider params={chatHistoryProviderParams}>
|
||||
<ChatItemContextProvider>
|
||||
<ChatRecordContextProvider params={chatRecordProviderParams}>
|
||||
<Chat myApps={myApps} />
|
||||
</ChatRecordContextProvider>
|
||||
</ChatItemContextProvider>
|
||||
</ChatContextProvider>
|
||||
) : null;
|
||||
};
|
||||
|
||||
export async function getServerSideProps(context: any) {
|
||||
return {
|
||||
props: {
|
||||
appId: context?.query?.appId || '',
|
||||
chatId: context?.query?.chatId || '',
|
||||
...(await serviceSideProps(context, ['file', 'app', 'chat', 'workflow']))
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { Box, Flex, Drawer, DrawerOverlay, DrawerContent } from '@chakra-ui/react';
|
||||
import { streamFetch } from '@/web/common/api/fetch';
|
||||
@@ -13,7 +13,7 @@ import ChatHeader from './components/ChatHeader';
|
||||
import ChatHistorySlider from './components/ChatHistorySlider';
|
||||
import { serviceSideProps } from '@/web/common/utils/i18n';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { delChatRecordById, getInitOutLinkChatInfo } from '@/web/core/chat/api';
|
||||
import { getInitOutLinkChatInfo } from '@/web/core/chat/api';
|
||||
import { getChatTitleFromChatMessage } from '@fastgpt/global/core/chat/utils';
|
||||
import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
|
||||
import { OutLinkWithAppType } from '@fastgpt/global/support/outLink/type';
|
||||
@@ -26,16 +26,21 @@ import { InitChatResponse } from '@/global/core/chat/api';
|
||||
import { defaultChatData, GetChatTypeEnum } from '@/global/core/chat/constants';
|
||||
import { useMount } from 'ahooks';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import { useChat } from '@/components/core/chat/ChatContainer/useChat';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
||||
import { useShareChatStore } from '@/web/core/chat/storeShareChat';
|
||||
import ChatItemContextProvider, { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
|
||||
import ChatRecordContextProvider, {
|
||||
ChatRecordContext
|
||||
} from '@/web/core/chat/context/chatRecordContext';
|
||||
import { useChatStore } from '@/web/core/chat/context/useChatStore';
|
||||
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
|
||||
const CustomPluginRunBox = dynamic(() => import('./components/CustomPluginRunBox'));
|
||||
|
||||
type Props = {
|
||||
appId: string;
|
||||
appName: string;
|
||||
appIntro: string;
|
||||
appAvatar: string;
|
||||
@@ -46,17 +51,12 @@ type Props = {
|
||||
showNodeStatus: boolean;
|
||||
};
|
||||
|
||||
const OutLink = (
|
||||
props: Props & {
|
||||
outLinkUid: string;
|
||||
}
|
||||
) => {
|
||||
const OutLink = (props: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
const { outLinkUid, showRawSource, showNodeStatus } = props;
|
||||
const { showRawSource, showNodeStatus } = props;
|
||||
const {
|
||||
shareId = '',
|
||||
chatId = '',
|
||||
showHistory = '1',
|
||||
showHead = '1',
|
||||
authToken,
|
||||
@@ -64,49 +64,69 @@ const OutLink = (
|
||||
...customVariables
|
||||
} = router.query as {
|
||||
shareId: string;
|
||||
chatId: string;
|
||||
showHistory: '0' | '1';
|
||||
showHead: '0' | '1';
|
||||
authToken: string;
|
||||
[key: string]: string;
|
||||
};
|
||||
const { isPc } = useSystem();
|
||||
const { outLinkAuthData, appId, chatId } = useChatStore();
|
||||
|
||||
const isOpenSlider = useContextSelector(ChatContext, (v) => v.isOpenSlider);
|
||||
const onCloseSlider = useContextSelector(ChatContext, (v) => v.onCloseSlider);
|
||||
const forbidLoadChat = useContextSelector(ChatContext, (v) => v.forbidLoadChat);
|
||||
const onChangeChatId = useContextSelector(ChatContext, (v) => v.onChangeChatId);
|
||||
const onUpdateHistoryTitle = useContextSelector(ChatContext, (v) => v.onUpdateHistoryTitle);
|
||||
|
||||
const resetVariables = useContextSelector(ChatItemContext, (v) => v.resetVariables);
|
||||
const isPlugin = useContextSelector(ChatItemContext, (v) => v.isPlugin);
|
||||
const setChatBoxData = useContextSelector(ChatItemContext, (v) => v.setChatBoxData);
|
||||
|
||||
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
|
||||
const totalRecordsCount = useContextSelector(ChatRecordContext, (v) => v.totalRecordsCount);
|
||||
|
||||
const initSign = useRef(false);
|
||||
const [isEmbed, setIdEmbed] = useState(true);
|
||||
|
||||
const [chatData, setChatData] = useState<InitChatResponse>(defaultChatData);
|
||||
const { loading: isLoading } = useRequest2(
|
||||
async () => {
|
||||
const shareId = outLinkAuthData.shareId;
|
||||
const outLinkUid = outLinkAuthData.outLinkUid;
|
||||
if (!outLinkUid || !shareId || forbidLoadChat.current) return;
|
||||
|
||||
const {
|
||||
onUpdateHistoryTitle,
|
||||
onUpdateHistory,
|
||||
onClearHistories,
|
||||
onDelHistory,
|
||||
isOpenSlider,
|
||||
onCloseSlider,
|
||||
forbidLoadChat,
|
||||
onChangeChatId
|
||||
} = useContextSelector(ChatContext, (v) => v);
|
||||
const res = await getInitOutLinkChatInfo({
|
||||
chatId,
|
||||
shareId,
|
||||
outLinkUid
|
||||
});
|
||||
|
||||
const params = useMemo(() => {
|
||||
return {
|
||||
chatId,
|
||||
shareId,
|
||||
outLinkUid,
|
||||
appId: chatData.appId,
|
||||
type: GetChatTypeEnum.outLink
|
||||
};
|
||||
}, [chatData.appId, chatId, outLinkUid, shareId]);
|
||||
const {
|
||||
ChatBoxRef,
|
||||
variablesForm,
|
||||
pluginRunTab,
|
||||
setPluginRunTab,
|
||||
resetVariables,
|
||||
chatRecords,
|
||||
ScrollData,
|
||||
setChatRecords,
|
||||
totalRecordsCount
|
||||
} = useChat(params);
|
||||
setChatData(res);
|
||||
setChatBoxData(res);
|
||||
resetVariables({
|
||||
variables: res.variables
|
||||
});
|
||||
},
|
||||
{
|
||||
manual: false,
|
||||
refreshDeps: [shareId, outLinkAuthData, chatId],
|
||||
onSuccess() {
|
||||
// send init message
|
||||
if (!initSign.current) {
|
||||
initSign.current = true;
|
||||
if (window !== top) {
|
||||
window.top?.postMessage({ type: 'shareChatReady' }, '*');
|
||||
}
|
||||
}
|
||||
},
|
||||
onError(e: any) {
|
||||
if (chatId) {
|
||||
onChangeChatId('');
|
||||
}
|
||||
},
|
||||
onFinally() {
|
||||
forbidLoadChat.current = false;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const startChat = useCallback(
|
||||
async ({
|
||||
@@ -138,10 +158,8 @@ const OutLink = (
|
||||
...customVariables
|
||||
},
|
||||
responseChatItemId,
|
||||
shareId,
|
||||
chatId: completionChatId,
|
||||
appType: chatData.app.type,
|
||||
outLinkUid
|
||||
...outLinkAuthData
|
||||
},
|
||||
onMessage: generatingMessage,
|
||||
abortCtrl: controller
|
||||
@@ -175,57 +193,11 @@ const OutLink = (
|
||||
|
||||
return { responseText, responseData, isNewChat: forbidLoadChat.current };
|
||||
},
|
||||
[
|
||||
chatId,
|
||||
customVariables,
|
||||
shareId,
|
||||
chatData.app.type,
|
||||
outLinkUid,
|
||||
onUpdateHistoryTitle,
|
||||
forbidLoadChat,
|
||||
onChangeChatId
|
||||
]
|
||||
);
|
||||
|
||||
const { loading: isLoading } = useRequest2(
|
||||
async () => {
|
||||
if (!shareId || !outLinkUid || forbidLoadChat.current) return;
|
||||
|
||||
const res = await getInitOutLinkChatInfo({
|
||||
chatId,
|
||||
shareId,
|
||||
outLinkUid
|
||||
});
|
||||
setChatData(res);
|
||||
|
||||
resetVariables({
|
||||
variables: res.variables
|
||||
});
|
||||
},
|
||||
{
|
||||
manual: false,
|
||||
refreshDeps: [shareId, outLinkUid, chatId],
|
||||
onSuccess() {
|
||||
// send init message
|
||||
if (!initSign.current) {
|
||||
initSign.current = true;
|
||||
if (window !== top) {
|
||||
window.top?.postMessage({ type: 'shareChatReady' }, '*');
|
||||
}
|
||||
}
|
||||
},
|
||||
onError(e: any) {
|
||||
if (chatId) {
|
||||
onChangeChatId('');
|
||||
}
|
||||
},
|
||||
onFinally() {
|
||||
forbidLoadChat.current = false;
|
||||
}
|
||||
}
|
||||
[chatId, customVariables, outLinkAuthData, onUpdateHistoryTitle, forbidLoadChat, onChangeChatId]
|
||||
);
|
||||
|
||||
// window init
|
||||
const [isEmbed, setIdEmbed] = useState(true);
|
||||
useMount(() => {
|
||||
setIdEmbed(window !== top);
|
||||
});
|
||||
@@ -233,32 +205,7 @@ const OutLink = (
|
||||
const RenderHistoryList = useMemo(() => {
|
||||
const Children = (
|
||||
<ChatHistorySlider
|
||||
appName={chatData.app.name}
|
||||
appAvatar={chatData.app.avatar}
|
||||
confirmClearText={t('common:core.chat.Confirm to clear share chat history')}
|
||||
onDelHistory={({ chatId }) =>
|
||||
onDelHistory({ appId: chatData.appId, chatId, shareId, outLinkUid })
|
||||
}
|
||||
onClearHistory={() => {
|
||||
onClearHistories({ shareId, outLinkUid });
|
||||
}}
|
||||
onSetHistoryTop={(e) => {
|
||||
onUpdateHistory({
|
||||
...e,
|
||||
appId: chatData.appId,
|
||||
shareId,
|
||||
outLinkUid
|
||||
});
|
||||
}}
|
||||
onSetCustomTitle={(e) => {
|
||||
onUpdateHistory({
|
||||
appId: chatData.appId,
|
||||
chatId: e.chatId,
|
||||
customTitle: e.title,
|
||||
shareId,
|
||||
outLinkUid
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -280,21 +227,7 @@ const OutLink = (
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
);
|
||||
}, [
|
||||
chatData.app.avatar,
|
||||
chatData.app.name,
|
||||
chatData.appId,
|
||||
isOpenSlider,
|
||||
isPc,
|
||||
onClearHistories,
|
||||
onCloseSlider,
|
||||
onDelHistory,
|
||||
onUpdateHistory,
|
||||
outLinkUid,
|
||||
shareId,
|
||||
showHistory,
|
||||
t
|
||||
]);
|
||||
}, [isOpenSlider, isPc, onCloseSlider, showHistory, t]);
|
||||
|
||||
const loading = isLoading;
|
||||
|
||||
@@ -329,43 +262,21 @@ const OutLink = (
|
||||
) : null}
|
||||
{/* chat box */}
|
||||
<Box flex={1} bg={'white'}>
|
||||
{chatData.app.type === AppTypeEnum.plugin ? (
|
||||
{isPlugin ? (
|
||||
<CustomPluginRunBox
|
||||
pluginInputs={chatData.app.pluginInputs}
|
||||
variablesForm={variablesForm}
|
||||
histories={chatRecords}
|
||||
setHistories={setChatRecords}
|
||||
appId={chatData.appId}
|
||||
tab={pluginRunTab}
|
||||
setTab={setPluginRunTab}
|
||||
appId={appId}
|
||||
chatId={chatId}
|
||||
outLinkAuthData={outLinkAuthData}
|
||||
onNewChat={() => onChangeChatId(getNanoid())}
|
||||
onStartChat={startChat}
|
||||
/>
|
||||
) : (
|
||||
<ChatBox
|
||||
ScrollData={ScrollData}
|
||||
ref={ChatBoxRef}
|
||||
chatHistories={chatRecords}
|
||||
setChatHistories={setChatRecords}
|
||||
variablesForm={variablesForm}
|
||||
appAvatar={chatData.app.avatar}
|
||||
userAvatar={chatData.userAvatar}
|
||||
chatConfig={chatData.app?.chatConfig}
|
||||
appId={appId}
|
||||
chatId={chatId}
|
||||
outLinkAuthData={outLinkAuthData}
|
||||
feedbackType={'user'}
|
||||
onStartChat={startChat}
|
||||
onDelMessage={({ contentId }) =>
|
||||
delChatRecordById({
|
||||
contentId,
|
||||
appId: chatData.appId,
|
||||
chatId,
|
||||
shareId,
|
||||
outLinkUid
|
||||
})
|
||||
}
|
||||
appId={chatData.appId}
|
||||
chatId={chatId}
|
||||
shareId={shareId}
|
||||
outLinkUid={outLinkUid}
|
||||
chatType="share"
|
||||
showRawSource={showRawSource}
|
||||
showNodeStatus={showNodeStatus}
|
||||
@@ -380,29 +291,57 @@ const OutLink = (
|
||||
};
|
||||
|
||||
const Render = (props: Props) => {
|
||||
const { shareId, authToken, customUid } = props;
|
||||
const { shareId, authToken, customUid, appId } = props;
|
||||
const { localUId, loaded } = useShareChatStore();
|
||||
const { source, chatId, setSource, setAppId, setOutLinkAuthData } = useChatStore();
|
||||
|
||||
const [isLoaded, setIsLoaded] = useState(false);
|
||||
|
||||
const contextParams = useMemo(() => {
|
||||
const chatHistoryProviderParams = useMemo(() => {
|
||||
return { shareId, outLinkUid: authToken || customUid || localUId };
|
||||
}, [authToken, customUid, localUId, shareId]);
|
||||
const chatRecordProviderParams = useMemo(() => {
|
||||
return {
|
||||
appId,
|
||||
shareId,
|
||||
outLinkUid: chatHistoryProviderParams.outLinkUid,
|
||||
chatId,
|
||||
type: GetChatTypeEnum.outLink
|
||||
};
|
||||
}, [appId, chatHistoryProviderParams.outLinkUid, chatId, shareId]);
|
||||
|
||||
useMount(() => {
|
||||
setIsLoaded(true);
|
||||
});
|
||||
const systemLoaded = isLoaded && loaded && contextParams.outLinkUid;
|
||||
|
||||
return (
|
||||
<>
|
||||
{systemLoaded ? (
|
||||
<ChatContextProvider params={contextParams}>
|
||||
<OutLink {...props} outLinkUid={contextParams.outLinkUid} />;
|
||||
</ChatContextProvider>
|
||||
) : (
|
||||
<NextHead title="Loading..." />
|
||||
)}
|
||||
</>
|
||||
setSource('share');
|
||||
});
|
||||
const systemLoaded = isLoaded && loaded && chatHistoryProviderParams.outLinkUid;
|
||||
|
||||
// Set outLinkAuthData
|
||||
useEffect(() => {
|
||||
setOutLinkAuthData({
|
||||
shareId,
|
||||
outLinkUid: chatHistoryProviderParams.outLinkUid
|
||||
});
|
||||
return () => {
|
||||
setOutLinkAuthData({});
|
||||
};
|
||||
}, [chatHistoryProviderParams.outLinkUid, setOutLinkAuthData, shareId]);
|
||||
// Watch appId
|
||||
useEffect(() => {
|
||||
setAppId(appId);
|
||||
}, [appId, setAppId]);
|
||||
|
||||
return source === ChatSourceEnum.share ? (
|
||||
<ChatContextProvider params={chatHistoryProviderParams}>
|
||||
<ChatItemContextProvider>
|
||||
<ChatRecordContextProvider params={chatRecordProviderParams}>
|
||||
<OutLink {...props} />
|
||||
</ChatRecordContextProvider>
|
||||
</ChatItemContextProvider>
|
||||
</ChatContextProvider>
|
||||
) : (
|
||||
<NextHead title={props.appName} desc={props.appIntro} icon={props.appAvatar} />
|
||||
);
|
||||
};
|
||||
|
||||
@@ -433,6 +372,7 @@ export async function getServerSideProps(context: any) {
|
||||
|
||||
return {
|
||||
props: {
|
||||
appId: String(app?.appId?._id) ?? '',
|
||||
appName: app?.appId?.name ?? 'AI',
|
||||
appAvatar: app?.appId?.avatar ?? '',
|
||||
appIntro: app?.appId?.intro ?? 'AI',
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import NextHead from '@/components/common/NextHead';
|
||||
import { delChatRecordById, getTeamChatInfo } from '@/web/core/chat/api';
|
||||
import { getTeamChatInfo } from '@/web/core/chat/api';
|
||||
import { useRouter } from 'next/router';
|
||||
import { Box, Flex, Drawer, DrawerOverlay, DrawerContent, useTheme } from '@chakra-ui/react';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import SideBar from '@/components/SideBar';
|
||||
import PageContainer from '@/components/PageContainer';
|
||||
import { getMyTokensApps } from '@/web/core/chat/api';
|
||||
@@ -25,10 +24,16 @@ import { InitChatResponse } from '@/global/core/chat/api';
|
||||
import { defaultChatData, GetChatTypeEnum } from '@/global/core/chat/constants';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
import { useChat } from '@/components/core/chat/ChatContainer/useChat';
|
||||
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
||||
import ChatItemContextProvider, { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
|
||||
import ChatRecordContextProvider, {
|
||||
ChatRecordContext
|
||||
} from '@/web/core/chat/context/chatRecordContext';
|
||||
import { useChatStore } from '@/web/core/chat/context/useChatStore';
|
||||
import { useMount } from 'ahooks';
|
||||
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
|
||||
const CustomPluginRunBox = dynamic(() => import('./components/CustomPluginRunBox'));
|
||||
|
||||
type Props = { appId: string; chatId: string; teamId: string; teamToken: string };
|
||||
@@ -38,51 +43,59 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
||||
const router = useRouter();
|
||||
const {
|
||||
teamId = '',
|
||||
appId = '',
|
||||
chatId = '',
|
||||
appId: appIdQuery = '',
|
||||
teamToken,
|
||||
...customVariables
|
||||
} = router.query as Props & {
|
||||
[key: string]: string;
|
||||
};
|
||||
|
||||
const { toast } = useToast();
|
||||
const theme = useTheme();
|
||||
const { isPc } = useSystem();
|
||||
|
||||
const { outLinkAuthData, appId, chatId } = useChatStore();
|
||||
|
||||
const isOpenSlider = useContextSelector(ChatContext, (v) => v.isOpenSlider);
|
||||
const onCloseSlider = useContextSelector(ChatContext, (v) => v.onCloseSlider);
|
||||
const forbidLoadChat = useContextSelector(ChatContext, (v) => v.forbidLoadChat);
|
||||
const onChangeChatId = useContextSelector(ChatContext, (v) => v.onChangeChatId);
|
||||
const onUpdateHistoryTitle = useContextSelector(ChatContext, (v) => v.onUpdateHistoryTitle);
|
||||
|
||||
const resetVariables = useContextSelector(ChatItemContext, (v) => v.resetVariables);
|
||||
const setChatBoxData = useContextSelector(ChatItemContext, (v) => v.setChatBoxData);
|
||||
|
||||
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
|
||||
const totalRecordsCount = useContextSelector(ChatRecordContext, (v) => v.totalRecordsCount);
|
||||
|
||||
// get chat app info
|
||||
const [chatData, setChatData] = useState<InitChatResponse>(defaultChatData);
|
||||
const { loading: isLoading } = useRequest2(
|
||||
async () => {
|
||||
if (!appId || forbidLoadChat.current) return;
|
||||
|
||||
const {
|
||||
onUpdateHistoryTitle,
|
||||
onUpdateHistory,
|
||||
onClearHistories,
|
||||
onDelHistory,
|
||||
isOpenSlider,
|
||||
onCloseSlider,
|
||||
forbidLoadChat,
|
||||
onChangeChatId
|
||||
} = useContextSelector(ChatContext, (v) => v);
|
||||
const res = await getTeamChatInfo({ teamId, appId, chatId, teamToken });
|
||||
|
||||
const params = useMemo(() => {
|
||||
return {
|
||||
appId,
|
||||
chatId,
|
||||
teamId,
|
||||
teamToken,
|
||||
type: GetChatTypeEnum.team
|
||||
};
|
||||
}, [appId, chatId, teamId, teamToken]);
|
||||
const {
|
||||
ChatBoxRef,
|
||||
variablesForm,
|
||||
pluginRunTab,
|
||||
setPluginRunTab,
|
||||
resetVariables,
|
||||
chatRecords,
|
||||
ScrollData,
|
||||
setChatRecords,
|
||||
totalRecordsCount
|
||||
} = useChat(params);
|
||||
setChatData(res);
|
||||
setChatBoxData(res);
|
||||
// reset chat records
|
||||
resetVariables({
|
||||
variables: res.variables
|
||||
});
|
||||
},
|
||||
{
|
||||
manual: false,
|
||||
refreshDeps: [teamId, teamToken, appId, chatId],
|
||||
onError(e: any) {
|
||||
console.log(e);
|
||||
if (chatId) {
|
||||
onChangeChatId();
|
||||
}
|
||||
},
|
||||
onFinally() {
|
||||
forbidLoadChat.current = false;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const startChat = useCallback(
|
||||
async ({
|
||||
@@ -143,58 +156,9 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
||||
]
|
||||
);
|
||||
|
||||
// get chat app info
|
||||
const { loading: isLoading } = useRequest2(
|
||||
async () => {
|
||||
if (!appId || forbidLoadChat.current) return;
|
||||
|
||||
const res = await getTeamChatInfo({ teamId, appId, chatId, teamToken });
|
||||
setChatData(res);
|
||||
|
||||
// reset chat records
|
||||
resetVariables({
|
||||
variables: res.variables
|
||||
});
|
||||
},
|
||||
{
|
||||
manual: false,
|
||||
refreshDeps: [teamId, teamToken, appId, chatId],
|
||||
onError(e: any) {
|
||||
console.log(e);
|
||||
if (chatId) {
|
||||
onChangeChatId('');
|
||||
}
|
||||
},
|
||||
onFinally() {
|
||||
forbidLoadChat.current = false;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const RenderHistoryList = useMemo(() => {
|
||||
const Children = (
|
||||
<ChatHistorySlider
|
||||
appId={appId}
|
||||
appName={chatData.app.name}
|
||||
appAvatar={chatData.app.avatar}
|
||||
confirmClearText={t('common:core.chat.Confirm to clear history')}
|
||||
onDelHistory={(e) => onDelHistory({ ...e, appId, teamId, teamToken })}
|
||||
onClearHistory={() => {
|
||||
onClearHistories({ appId, teamId, teamToken });
|
||||
}}
|
||||
onSetHistoryTop={(e) => {
|
||||
onUpdateHistory({ ...e, teamId, teamToken, appId });
|
||||
}}
|
||||
onSetCustomTitle={async (e) => {
|
||||
onUpdateHistory({
|
||||
appId,
|
||||
chatId: e.chatId,
|
||||
customTitle: e.title,
|
||||
teamId,
|
||||
teamToken
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<ChatHistorySlider confirmClearText={t('common:core.chat.Confirm to clear history')} />
|
||||
);
|
||||
|
||||
return isPc || !appId ? (
|
||||
@@ -211,20 +175,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
||||
<DrawerContent maxWidth={'75vw'}>{Children}</DrawerContent>
|
||||
</Drawer>
|
||||
);
|
||||
}, [
|
||||
appId,
|
||||
chatData.app.avatar,
|
||||
chatData.app.name,
|
||||
isOpenSlider,
|
||||
isPc,
|
||||
onClearHistories,
|
||||
onCloseSlider,
|
||||
onDelHistory,
|
||||
onUpdateHistory,
|
||||
t,
|
||||
teamId,
|
||||
teamToken
|
||||
]);
|
||||
}, [appId, isOpenSlider, isPc, onCloseSlider, t]);
|
||||
|
||||
const loading = isLoading;
|
||||
|
||||
@@ -261,41 +212,19 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
||||
<Box flex={1}>
|
||||
{chatData.app.type === AppTypeEnum.plugin ? (
|
||||
<CustomPluginRunBox
|
||||
pluginInputs={chatData.app.pluginInputs}
|
||||
variablesForm={variablesForm}
|
||||
histories={chatRecords}
|
||||
setHistories={setChatRecords}
|
||||
appId={chatData.appId}
|
||||
tab={pluginRunTab}
|
||||
setTab={setPluginRunTab}
|
||||
appId={appId}
|
||||
chatId={chatId}
|
||||
outLinkAuthData={outLinkAuthData}
|
||||
onNewChat={() => onChangeChatId(getNanoid())}
|
||||
onStartChat={startChat}
|
||||
/>
|
||||
) : (
|
||||
<ChatBox
|
||||
ref={ChatBoxRef}
|
||||
ScrollData={ScrollData}
|
||||
chatHistories={chatRecords}
|
||||
setChatHistories={setChatRecords}
|
||||
variablesForm={variablesForm}
|
||||
appAvatar={chatData.app.avatar}
|
||||
userAvatar={chatData.userAvatar}
|
||||
chatConfig={chatData.app?.chatConfig}
|
||||
appId={appId}
|
||||
chatId={chatId}
|
||||
outLinkAuthData={outLinkAuthData}
|
||||
feedbackType={'user'}
|
||||
onStartChat={startChat}
|
||||
onDelMessage={({ contentId }) =>
|
||||
delChatRecordById({
|
||||
contentId,
|
||||
appId: chatData.appId,
|
||||
chatId,
|
||||
teamId,
|
||||
teamToken
|
||||
})
|
||||
}
|
||||
appId={chatData.appId}
|
||||
chatId={chatId}
|
||||
teamId={teamId}
|
||||
teamToken={teamToken}
|
||||
chatType="team"
|
||||
showRawSource
|
||||
showNodeStatus
|
||||
@@ -311,9 +240,8 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
||||
|
||||
const Render = (props: Props) => {
|
||||
const { teamId, appId, teamToken } = props;
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const router = useRouter();
|
||||
const { source, chatId, setSource, setAppId, setOutLinkAuthData } = useChatStore();
|
||||
|
||||
const { data: myApps = [], runAsync: loadMyApps } = useRequest2(
|
||||
async () => {
|
||||
@@ -323,34 +251,61 @@ const Render = (props: Props) => {
|
||||
return [];
|
||||
},
|
||||
{
|
||||
manual: false
|
||||
manual: true
|
||||
}
|
||||
);
|
||||
|
||||
// 初始化聊天框
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
if (appId || myApps.length === 0) return;
|
||||
useMount(async () => {
|
||||
setSource('team');
|
||||
|
||||
router.replace({
|
||||
query: {
|
||||
...router.query,
|
||||
appId: myApps[0]._id,
|
||||
chatId: ''
|
||||
}
|
||||
});
|
||||
})();
|
||||
}, [appId, loadMyApps, myApps, router, t, toast]);
|
||||
const apps = await loadMyApps();
|
||||
|
||||
if (appId || apps.length === 0) return;
|
||||
|
||||
router.replace({
|
||||
query: {
|
||||
...router.query,
|
||||
appId: apps[0]._id
|
||||
}
|
||||
});
|
||||
});
|
||||
// Watch appId
|
||||
useEffect(() => {
|
||||
setAppId(appId);
|
||||
}, [appId, setAppId]);
|
||||
useEffect(() => {
|
||||
setOutLinkAuthData({
|
||||
teamId,
|
||||
teamToken
|
||||
});
|
||||
return () => {
|
||||
setOutLinkAuthData({});
|
||||
};
|
||||
}, [teamId, teamToken, setOutLinkAuthData]);
|
||||
|
||||
const contextParams = useMemo(() => {
|
||||
return { teamId, appId, teamToken };
|
||||
}, [teamId, appId, teamToken]);
|
||||
const chatRecordProviderParams = useMemo(() => {
|
||||
return {
|
||||
appId,
|
||||
chatId,
|
||||
teamId,
|
||||
teamToken,
|
||||
type: GetChatTypeEnum.team
|
||||
};
|
||||
}, [appId, chatId, teamId, teamToken]);
|
||||
|
||||
return (
|
||||
return source === ChatSourceEnum.team ? (
|
||||
<ChatContextProvider params={contextParams}>
|
||||
<Chat {...props} myApps={myApps} />
|
||||
<ChatItemContextProvider>
|
||||
<ChatRecordContextProvider params={chatRecordProviderParams}>
|
||||
<Chat {...props} myApps={myApps} />
|
||||
</ChatRecordContextProvider>
|
||||
</ChatItemContextProvider>
|
||||
</ChatContextProvider>
|
||||
);
|
||||
) : null;
|
||||
};
|
||||
|
||||
export async function getServerSideProps(context: any) {
|
||||
|
||||
@@ -14,6 +14,8 @@ import { uploadFile2DB } from '@/web/common/file/controller';
|
||||
import { BucketNameEnum } from '@fastgpt/global/common/file/constants';
|
||||
import { ImportSourceItemType } from '@/web/core/dataset/type';
|
||||
import { useI18n } from '@/web/context/I18n';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { DatasetPageContext } from '@/web/core/dataset/context/datasetPageContext';
|
||||
|
||||
export type SelectFileItemType = {
|
||||
fileId: string;
|
||||
@@ -41,6 +43,7 @@ const FileSelector = ({
|
||||
const { toast } = useToast();
|
||||
const { feConfigs } = useSystemStore();
|
||||
|
||||
const datasetId = useContextSelector(DatasetPageContext, (v) => v.datasetId);
|
||||
const maxCount = feConfigs?.uploadFileMaxAmount || 1000;
|
||||
const maxSize = (feConfigs?.uploadFileMaxSize || 1024) * 1024 * 1024;
|
||||
|
||||
@@ -92,6 +95,9 @@ const FileSelector = ({
|
||||
const { fileId: uploadFileId } = await uploadFile2DB({
|
||||
file,
|
||||
bucketName: BucketNameEnum.dataset,
|
||||
data: {
|
||||
datasetId
|
||||
},
|
||||
percentListen: (e) => {
|
||||
setSelectFiles((state) =>
|
||||
state.map((item) =>
|
||||
|
||||
@@ -136,14 +136,13 @@ const Dataset = () => {
|
||||
|
||||
{isPc && RenderSearchInput}
|
||||
|
||||
{userInfo?.team?.permission.hasWritePer && (
|
||||
{(folderDetail
|
||||
? folderDetail.permission.hasWritePer
|
||||
: userInfo?.team?.permission.hasWritePer) && (
|
||||
<Box pl={[0, 4]}>
|
||||
<MyMenu
|
||||
size="md"
|
||||
offset={[0, 10]}
|
||||
width={120}
|
||||
iconSize="2rem"
|
||||
iconRadius="6px"
|
||||
placement="bottom-end"
|
||||
Button={
|
||||
<Button variant={'primary'} px="0">
|
||||
<Flex alignItems={'center'} px={5}>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import type { ResLogin } from '@/global/support/api/userRes.d';
|
||||
import { useChatStore } from '@/web/core/chat/context/storeChat';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import { clearToken, setToken } from '@/web/support/user/auth';
|
||||
import { postFastLogin } from '@/web/support/user/api';
|
||||
@@ -19,7 +18,6 @@ const FastLogin = ({
|
||||
token: string;
|
||||
callbackUrl: string;
|
||||
}) => {
|
||||
const { setLastChatId, setLastChatAppId } = useChatStore();
|
||||
const { setUserInfo } = useUserStore();
|
||||
const router = useRouter();
|
||||
const { toast } = useToast();
|
||||
@@ -29,15 +27,11 @@ const FastLogin = ({
|
||||
setToken(res.token);
|
||||
setUserInfo(res.user);
|
||||
|
||||
// init store
|
||||
setLastChatId('');
|
||||
setLastChatAppId('');
|
||||
|
||||
setTimeout(() => {
|
||||
router.push(decodeURIComponent(callbackUrl));
|
||||
}, 100);
|
||||
},
|
||||
[setLastChatId, setLastChatAppId, setUserInfo, router, callbackUrl]
|
||||
[setUserInfo, router, callbackUrl]
|
||||
);
|
||||
|
||||
const authCode = useCallback(
|
||||
|
||||
@@ -15,7 +15,7 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import type { ResLogin } from '@/global/support/api/userRes.d';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import { useChatStore } from '@/web/core/chat/context/storeChat';
|
||||
import { useChatStore } from '@/web/core/chat/context/useChatStore';
|
||||
import LoginForm from './components/LoginForm/LoginForm';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { serviceSideProps } from '@/web/common/utils/i18n';
|
||||
@@ -44,7 +44,7 @@ const Login = ({ ChineseRedirectUrl }: { ChineseRedirectUrl: string }) => {
|
||||
const { feConfigs } = useSystemStore();
|
||||
const [pageType, setPageType] = useState<`${LoginPageTypeEnum}`>();
|
||||
const { setUserInfo } = useUserStore();
|
||||
const { setLastChatId, setLastChatAppId } = useChatStore();
|
||||
const { setLastChatAppId } = useChatStore();
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const { isPc } = useSystem();
|
||||
|
||||
@@ -59,17 +59,13 @@ const Login = ({ ChineseRedirectUrl }: { ChineseRedirectUrl: string }) => {
|
||||
|
||||
const loginSuccess = useCallback(
|
||||
(res: ResLogin) => {
|
||||
// init store
|
||||
setLastChatId('');
|
||||
setLastChatAppId('');
|
||||
|
||||
setUserInfo(res.user);
|
||||
setToken(res.token);
|
||||
setTimeout(() => {
|
||||
router.push(lastRoute ? decodeURIComponent(lastRoute) : '/app/list');
|
||||
}, 300);
|
||||
},
|
||||
[lastRoute, router, setLastChatId, setLastChatAppId, setUserInfo]
|
||||
[lastRoute, router, setUserInfo]
|
||||
);
|
||||
|
||||
function DynamicComponent({ type }: { type: `${LoginPageTypeEnum}` }) {
|
||||
@@ -95,6 +91,9 @@ const Login = ({ ChineseRedirectUrl }: { ChineseRedirectUrl: string }) => {
|
||||
setPageType(
|
||||
feConfigs?.oauth?.wechat ? LoginPageTypeEnum.wechat : LoginPageTypeEnum.passwordLogin
|
||||
);
|
||||
|
||||
// init store
|
||||
setLastChatAppId('');
|
||||
}, [feConfigs.oauth]);
|
||||
|
||||
const {
|
||||
|
||||
@@ -2,7 +2,6 @@ import React, { useCallback, useEffect, useRef } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import type { ResLogin } from '@/global/support/api/userRes.d';
|
||||
import { useChatStore } from '@/web/core/chat/context/storeChat';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import { clearToken, setToken } from '@/web/support/user/auth';
|
||||
import { oauthLogin } from '@/web/support/user/api';
|
||||
@@ -17,7 +16,6 @@ let isOauthLogging = false;
|
||||
const provider = () => {
|
||||
const { t } = useTranslation();
|
||||
const { loginStore } = useSystemStore();
|
||||
const { setLastChatId, setLastChatAppId } = useChatStore();
|
||||
const { setUserInfo } = useUserStore();
|
||||
const router = useRouter();
|
||||
const { code, state, error } = router.query as { code: string; state: string; error?: string };
|
||||
@@ -28,13 +26,9 @@ const provider = () => {
|
||||
setToken(res.token);
|
||||
setUserInfo(res.user);
|
||||
|
||||
// init store
|
||||
setLastChatId('');
|
||||
setLastChatAppId('');
|
||||
|
||||
router.push(loginStore?.lastRoute ? decodeURIComponent(loginStore?.lastRoute) : '/app/list');
|
||||
},
|
||||
[setLastChatId, setLastChatAppId, setUserInfo, router, loginStore?.lastRoute]
|
||||
[setUserInfo, router, loginStore?.lastRoute]
|
||||
);
|
||||
|
||||
const authCode = useCallback(
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import type { ResLogin } from '@/global/support/api/userRes.d';
|
||||
import { useChatStore } from '@/web/core/chat/context/storeChat';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import { clearToken, setToken } from '@/web/support/user/auth';
|
||||
import { ssoLogin } from '@/web/support/user/api';
|
||||
@@ -15,7 +14,6 @@ let isOauthLogging = false;
|
||||
|
||||
const provider = () => {
|
||||
const { t } = useTranslation();
|
||||
const { setLastChatId, setLastChatAppId } = useChatStore();
|
||||
const { setUserInfo } = useUserStore();
|
||||
const router = useRouter();
|
||||
const { query } = router;
|
||||
@@ -27,11 +25,9 @@ const provider = () => {
|
||||
setToken(res.token);
|
||||
setUserInfo(res.user);
|
||||
// init store
|
||||
setLastChatId('');
|
||||
setLastChatAppId('');
|
||||
router.push('/app/list');
|
||||
},
|
||||
[setLastChatId, setLastChatAppId, setUserInfo, router]
|
||||
[setUserInfo, router]
|
||||
);
|
||||
|
||||
const handleSSO = useCallback(async () => {
|
||||
|
||||
Reference in New Issue
Block a user