Plugin runtime (#2050)

* feat: plugin run (#1950)

* feat: plugin run

* fix

* ui

* fix

* change user input type

* fix

* fix

* temp

* split out plugin chat

* perf: chatbox

* perf: chatbox

* fix: plugin runtime (#2032)

* fix: plugin runtime

* fix

* fix build

* fix build

* perf: chat send prompt

* perf: chat log ux

* perf: chatbox context and share page plugin runtime

* perf: plugin run time config

* fix: ts

* feat: doc

* perf: isPc check

* perf: variable input render

* feat: app search

* fix: response box height

* fix: phone ui

* perf: lock

* perf: plugin route

* fix: chat (#2049)

---------

Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com>
This commit is contained in:
Archer
2024-07-15 22:50:48 +08:00
committed by GitHub
parent 090c880860
commit b5c98a4f63
126 changed files with 5012 additions and 4317 deletions

View File

@@ -44,6 +44,7 @@ import {
import StandardPlanContentList from '@/components/support/wallet/StandardPlanContentList';
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
const StandDetailModal = dynamic(() => import('./standardDetailModal'));
const TeamMenu = dynamic(() => import('@/components/support/user/team/TeamMenu'));
@@ -54,7 +55,7 @@ const LafAccountModal = dynamic(() => import('@/components/support/laf/LafAccoun
const CommunityModal = dynamic(() => import('@/components/CommunityModal'));
const Account = () => {
const { isPc } = useSystemStore();
const { isPc } = useSystem();
const { teamPlanStatus } = useUserStore();
const standardPlan = teamPlanStatus?.standardConstants;
@@ -99,7 +100,7 @@ const MyInfo = () => {
const { reset } = useForm<UserUpdateParams>({
defaultValues: userInfo as UserType
});
const { isPc } = useSystemStore();
const { isPc } = useSystem();
const { toast } = useToast();
const {

View File

@@ -9,12 +9,14 @@ import { usePagination } from '@fastgpt/web/hooks/usePagination';
import { useLoading } from '@fastgpt/web/hooks/useLoading';
import { useTranslation } from 'next-i18next';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
const InformTable = () => {
const { t } = useTranslation();
const theme = useTheme();
const { Loading } = useLoading();
const { isPc } = useSystemStore();
const { isPc } = useSystem();
const {
data: informs,
isLoading,

View File

@@ -31,6 +31,7 @@ import Avatar from '@/components/Avatar';
import MySelect from '@fastgpt/web/components/common/MySelect';
import { formatNumber } from '@fastgpt/global/common/math/tools';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
const UsageDetail = dynamic(() => import('./UsageDetail'));
const UsageTable = () => {
@@ -41,7 +42,7 @@ const UsageTable = () => {
to: new Date()
});
const [usageSource, setUsageSource] = useState<UsageSourceEnum | ''>('');
const { isPc } = useSystemStore();
const { isPc } = useSystem();
const { userInfo } = useUserStore();
const [usageDetail, setUsageDetail] = useState<UsageItemType>();

View File

@@ -12,6 +12,7 @@ import UserInfo from './components/Info';
import { serviceSideProps } from '@/web/common/utils/i18n';
import { useTranslation } from 'next-i18next';
import Script from 'next/script';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
const Promotion = dynamic(() => import('./components/Promotion'));
const UsageTable = dynamic(() => import('./components/UsageTable'));
@@ -34,7 +35,8 @@ enum TabEnum {
const Account = ({ currentTab }: { currentTab: TabEnum }) => {
const { t } = useTranslation();
const { userInfo, setUserInfo } = useUserStore();
const { feConfigs, isPc, systemVersion } = useSystemStore();
const { feConfigs, systemVersion } = useSystemStore();
const { isPc } = useSystem();
const tabList = [
{

View File

@@ -14,6 +14,7 @@ import { AppFolderTypeList, AppTypeEnum } from '@fastgpt/global/core/app/constan
import { AppDefaultPermissionVal } from '@fastgpt/global/support/permission/app/constant';
import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
import { replaceRegChars } from '@fastgpt/global/common/string/tools';
export type ListAppBody = {
parentId?: ParentIdType;
@@ -55,8 +56,8 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
const searchMatch = searchKey
? {
$or: [
{ name: { $regex: searchKey, $options: 'i' } },
{ intro: { $regex: searchKey, $options: 'i' } }
{ name: { $regex: new RegExp(`${replaceRegChars(searchKey)}`, 'i') } },
{ intro: { $regex: new RegExp(`${replaceRegChars(searchKey)}`, 'i') } }
]
}
: {};
@@ -65,7 +66,14 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
return {
// get all chat app
teamId,
type: { $in: [AppTypeEnum.workflow, AppTypeEnum.simple] },
type: { $in: [AppTypeEnum.workflow, AppTypeEnum.simple, AppTypeEnum.plugin] },
...searchMatch
};
}
if (searchKey) {
return {
teamId,
...searchMatch
};
}
@@ -74,8 +82,7 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
teamId,
...(type && Array.isArray(type) && { type: { $in: type } }),
...(type && { type }),
...parseParentIdInMongo(parentId),
...searchMatch
...parseParentIdInMongo(parentId)
};
})();

View File

@@ -1,15 +1,10 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { connectToDatabase } from '@/service/mongo';
import { sseErrRes } from '@fastgpt/service/common/response';
import { 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 {
ChatItemType,
ChatItemValueItemType,
UserChatItemValueItemType
} from '@fastgpt/global/core/chat/type';
import type { UserChatItemValueItemType } 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';
@@ -18,10 +13,14 @@ import { RuntimeEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
import { RuntimeNodeItemType } from '@fastgpt/global/core/workflow/runtime/type';
import { removeEmptyUserInput } from '@fastgpt/global/core/chat/utils';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { updatePluginInputByVariables } from '@fastgpt/global/core/workflow/utils';
import { NextAPI } from '@/service/middleware/entry';
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
import { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type';
export type Props = {
history: ChatItemType[];
prompt: UserChatItemValueItemType[];
messages: ChatCompletionMessageParam[];
nodes: RuntimeNodeItemType[];
edges: RuntimeEdgeItemType[];
variables: Record<string, any>;
@@ -29,7 +28,7 @@ export type Props = {
appName: string;
};
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
async function handler(req: NextApiRequest, res: NextApiResponse) {
res.on('close', () => {
res.end();
});
@@ -38,26 +37,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
res.end();
});
let {
nodes = [],
edges = [],
history = [],
prompt,
variables = {},
appName,
appId
} = req.body as Props;
let { nodes = [], edges = [], messages = [], variables = {}, appName, appId } = req.body as Props;
try {
await connectToDatabase();
if (!history || !nodes || !prompt || prompt.length === 0) {
throw new Error('Prams Error');
}
if (!Array.isArray(nodes)) {
throw new Error('Nodes is not array');
}
if (!Array.isArray(edges)) {
throw new Error('Edges is not array');
}
// [histories, user]
const chatMessages = GPTMessages2Chats(messages);
const userInput = chatMessages.pop()?.value as UserChatItemValueItemType[] | undefined;
/* user auth */
const [{ app }, { teamId, tmbId }] = await Promise.all([
@@ -67,6 +51,23 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
authToken: true
})
]);
const isPlugin = app.type === AppTypeEnum.plugin;
if (!Array.isArray(nodes)) {
throw new Error('Nodes is not array');
}
if (!Array.isArray(edges)) {
throw new Error('Edges is not array');
}
// Plugin need to replace inputs
if (isPlugin) {
nodes = updatePluginInputByVariables(nodes, variables);
} else {
if (!userInput) {
throw new Error('Params Error');
}
}
// auth balance
const { user } = await getUserChatInfoAndAuthTeamPoints(tmbId);
@@ -82,8 +83,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
runtimeNodes: nodes,
runtimeEdges: edges,
variables,
query: removeEmptyUserInput(prompt),
histories: history,
query: removeEmptyUserInput(userInput),
histories: chatMessages,
stream: true,
detail: true,
maxRunTimes: 200
@@ -117,6 +118,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
}
}
export default NextAPI(handler);
export const config = {
api: {
bodyParser: {

View File

@@ -11,6 +11,7 @@ import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runti
import { getAppLatestVersion } from '@fastgpt/service/core/app/controller';
import { NextAPI } from '@/service/middleware/entry';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
async function handler(
req: NextApiRequest,
@@ -53,6 +54,8 @@ async function handler(
}),
getAppLatestVersion(app._id, app)
]);
const pluginInputs =
app?.modules?.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput)?.inputs ?? [];
return {
chatId,
@@ -72,7 +75,9 @@ async function handler(
chatModels: getChatModelNameListByModules(nodes),
name: app.name,
avatar: app.avatar,
intro: app.intro
intro: app.intro,
type: app.type,
pluginInputs
}
};
}

View File

@@ -15,6 +15,8 @@ import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import { getAppLatestVersion } from '@fastgpt/service/core/app/controller';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
@@ -47,7 +49,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
chatId,
limit: 30,
field: `dataId obj value userGoodFeedback userBadFeedback ${
shareChat.responseDetail
shareChat.responseDetail || app.type === AppTypeEnum.plugin
? `adminFeedback ${DispatchNodeResponseKeyEnum.nodeResponse}`
: ''
} `
@@ -56,11 +58,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
]);
// pick share response field
histories.forEach((item) => {
if (item.obj === ChatRoleEnum.AI) {
item.responseData = filterPublicNodeResponseData({ flowResponses: item.responseData });
}
});
app.type !== AppTypeEnum.plugin &&
histories.forEach((item) => {
if (item.obj === ChatRoleEnum.AI) {
item.responseData = filterPublicNodeResponseData({ flowResponses: item.responseData });
}
});
jsonRes<InitChatResponse>(res, {
data: {
@@ -82,7 +85,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
chatModels: getChatModelNameListByModules(nodes),
name: app.name,
avatar: app.avatar,
intro: app.intro
intro: app.intro,
type: app.type,
pluginInputs:
app?.modules?.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput)
?.inputs ?? []
}
}
});

View File

@@ -15,6 +15,8 @@ import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
import { filterPublicNodeResponseData } from '@fastgpt/global/core/chat/utils';
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import { getAppLatestVersion } from '@fastgpt/service/core/app/controller';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
@@ -58,11 +60,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
]);
// pick share response field
histories.forEach((item) => {
if (item.obj === ChatRoleEnum.AI) {
item.responseData = filterPublicNodeResponseData({ flowResponses: item.responseData });
}
});
app.type !== AppTypeEnum.plugin &&
histories.forEach((item) => {
if (item.obj === ChatRoleEnum.AI) {
item.responseData = filterPublicNodeResponseData({ flowResponses: item.responseData });
}
});
jsonRes<InitChatResponse>(res, {
data: {
@@ -83,7 +86,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
chatModels: getChatModelNameListByModules(nodes),
name: app.name,
avatar: app.avatar,
intro: app.intro
intro: app.intro,
type: app.type,
pluginInputs:
app?.modules?.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput)
?.inputs ?? []
}
}
});

View File

@@ -3,7 +3,11 @@ import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { sseErrRes, jsonRes } from '@fastgpt/service/common/response';
import { addLog } from '@fastgpt/service/common/system/log';
import { ChatRoleEnum, ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
import {
ChatItemValueTypeEnum,
ChatRoleEnum,
ChatSourceEnum
} from '@fastgpt/global/core/chat/constants';
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
import { dispatchWorkFlow } from '@fastgpt/service/core/workflow/dispatch';
import type { ChatCompletionCreateParams } from '@fastgpt/global/core/ai/type.d';
@@ -28,10 +32,10 @@ import { authTeamSpaceToken } from '@/service/support/permission/auth/team';
import {
concatHistories,
filterPublicNodeResponseData,
getChatTitleFromChatMessage,
removeEmptyUserInput
} from '@fastgpt/global/core/chat/utils';
import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools';
import { connectToDatabase } from '@/service/mongo';
import { getUserChatInfoAndAuthTeamPoints } from '@/service/support/permission/auth/team';
import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant';
import { MongoApp } from '@fastgpt/service/core/app/schema';
@@ -49,9 +53,17 @@ import { setEntryEntries } from '@fastgpt/service/core/workflow/dispatchV1/utils
import { NextAPI } from '@/service/middleware/entry';
import { getAppLatestVersion } from '@fastgpt/service/core/app/controller';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { updatePluginInputByVariables } from '@fastgpt/global/core/workflow/utils';
import { getNanoid } from '@fastgpt/global/common/string/tools';
import {
getPluginInputsFromStoreNodes,
getPluginRunContent
} from '@fastgpt/global/core/app/plugin/utils';
import { getSystemTime } from '@fastgpt/global/common/time/timezone';
type FastGptWebChatProps = {
chatId?: string; // undefined: nonuse history, '': new chat, 'xxxxx': use history
chatId?: string; // undefined: get histories from messages, '': new chat, 'xxxxx': get histories from db
appId?: string;
};
@@ -59,14 +71,11 @@ export type Props = ChatCompletionCreateParams &
FastGptWebChatProps &
OutLinkChatAuthProps & {
messages: ChatCompletionMessageParam[];
responseChatItemId?: string;
stream?: boolean;
detail?: boolean;
variables: Record<string, any>;
variables: Record<string, any>; // Global variables or plugin inputs
};
export type ChatResponseType = {
newChatId: string;
quoteLen?: number;
};
type AuthResponseType = {
teamId: string;
@@ -89,7 +98,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
res.end();
});
const {
let {
chatId,
appId,
// share chat
@@ -98,41 +107,39 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
// team chat
teamId: spaceTeamId,
teamToken,
stream = false,
detail = false,
messages = [],
variables = {}
variables = {},
responseChatItemId = getNanoid()
} = req.body as Props;
try {
const originIp = requestIp.getClientIp(req);
await connectToDatabase();
// body data check
if (!messages) {
throw new Error('Prams Error');
}
const originIp = requestIp.getClientIp(req);
const startTime = Date.now();
try {
if (!Array.isArray(messages)) {
throw new Error('messages is not array');
}
if (messages.length === 0) {
throw new Error('messages is empty');
}
let startTime = Date.now();
// Web chat params: [Human, AI]
/*
Web params: chatId + [Human]
API params: chatId + [Human]
API params: [histories, Human]
*/
const chatMessages = GPTMessages2Chats(messages);
if (chatMessages[chatMessages.length - 1].obj !== ChatRoleEnum.Human) {
chatMessages.pop();
}
// user question
const question = chatMessages.pop() as UserChatItemType;
if (!question) {
throw new Error('Question is empty');
}
// Computed start hook params
const startHookText = (() => {
// Chat
const userQuestion = chatMessages[chatMessages.length - 1] as UserChatItemType | undefined;
if (userQuestion) return chatValue2RuntimePrompt(userQuestion.value).text;
const { text, files } = chatValue2RuntimePrompt(question.value);
// plugin
return JSON.stringify(variables);
})();
/*
1. auth app permission
@@ -149,7 +156,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
outLinkUid,
chatId,
ip: originIp,
question: text
question: startHookText
});
}
// team space chat
@@ -169,8 +176,45 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
chatId
});
})();
const isPlugin = app.type === AppTypeEnum.plugin;
// 1. get and concat history; 2. get app workflow
// Check message type
if (isPlugin) {
detail = true;
} else {
if (messages.length === 0) {
throw new Error('messages is empty');
}
}
// Get obj=Human history
const userQuestion: UserChatItemType = (() => {
if (isPlugin) {
return {
dataId: getNanoid(24),
obj: ChatRoleEnum.Human,
value: [
{
type: ChatItemValueTypeEnum.text,
text: {
content: getPluginRunContent({
pluginInputs: getPluginInputsFromStoreNodes(app.modules)
})
}
}
]
};
}
const latestHumanChat = chatMessages.pop() as UserChatItemType | undefined;
if (!latestHumanChat) {
throw new Error('User question is empty');
}
return latestHumanChat;
})();
const { text, files } = chatValue2RuntimePrompt(userQuestion.value);
// Get and concat history;
const limit = getMaxHistoryLimitFromNodes(app.modules);
const [{ histories }, { nodes, edges, chatConfig }] = await Promise.all([
getChatItems({
@@ -182,7 +226,14 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
getAppLatestVersion(app._id, app)
]);
const newHistories = concatHistories(histories, chatMessages);
const responseChatItemId: string | undefined = messages[messages.length - 1].dataId;
// Get runtimeNodes
const runtimeNodes = isPlugin
? updatePluginInputByVariables(
storeNodes2RuntimeNodes(nodes, getDefaultEntryNodeIds(nodes)),
variables
)
: storeNodes2RuntimeNodes(nodes, getDefaultEntryNodeIds(nodes));
/* start flow controller */
const { flowResponses, flowUsages, assistantResponses, newVariables } = await (async () => {
@@ -196,10 +247,10 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
app,
chatId,
responseChatItemId,
runtimeNodes: storeNodes2RuntimeNodes(nodes, getDefaultEntryNodeIds(nodes)),
runtimeNodes,
runtimeEdges: initWorkflowEdgeStatus(edges),
variables,
query: removeEmptyUserInput(question.value),
query: removeEmptyUserInput(userQuestion.value),
histories: newHistories,
stream,
detail,
@@ -245,6 +296,10 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
return ChatSourceEnum.online;
})();
const newTitle = isPlugin
? variables.cTime ?? getSystemTime(user.timezone)
: getChatTitleFromChatMessage(userQuestion);
await saveChat({
chatId,
appId: app._id,
@@ -254,11 +309,12 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
appChatConfig: chatConfig,
variables: newVariables,
isUpdateUseTime: isOwnerUse && source === ChatSourceEnum.online, // owner update use time
newTitle,
shareId,
outLinkUid: outLinkUserId,
source,
content: [
question,
userQuestion,
{
dataId: responseChatItemId,
obj: ChatRoleEnum.AI,
@@ -295,7 +351,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
});
if (detail) {
if (responseDetail) {
if (responseDetail || isPlugin) {
responseWrite({
res,
event: SseResponseEventEnum.flowResponses,
@@ -312,6 +368,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
return assistantResponses[0].text?.content;
return assistantResponses;
})();
res.json({
...(detail ? { responseData: feResponseData, newVariables } : {}),
id: chatId || '',

View File

@@ -0,0 +1,198 @@
import React from 'react';
import { Flex, Box, useTheme } from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useTranslation } from 'next-i18next';
import { HUMAN_ICON } from '@fastgpt/global/common/system/constants';
import { getInitChatInfo } from '@/web/core/chat/api';
import MyTag from '@fastgpt/web/components/common/Tag/index';
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';
import { PluginRunBoxTabEnum } from '@/components/core/chat/ChatContainer/PluginRunBox/constants';
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';
const PluginRunBox = dynamic(() => import('@/components/core/chat/ChatContainer/PluginRunBox'));
const DetailLogsModal = ({
appId,
chatId,
onClose
}: {
appId: string;
chatId: string;
onClose: () => void;
}) => {
const { t } = useTranslation();
const { isPc } = useSystem();
const theme = useTheme();
const {
ChatBoxRef,
chatRecords,
setChatRecords,
variablesForm,
pluginRunTab,
setPluginRunTab,
resetChatRecords
} = useChat();
const { data: chat, isFetching } = useQuery(
['getChatDetail', chatId],
() => getInitChatInfo({ appId, chatId, loadCustomFeedbacks: true }),
{
onSuccess(res) {
const history = res.history.map((item) => ({
...item,
dataId: item.dataId || getNanoid(),
status: 'finish' as any
}));
resetChatRecords({
records: history,
variables: res.variables
});
}
}
);
const title = chat?.title;
const chatModels = chat?.app?.chatModels;
const isPlugin = chat?.app.type === AppTypeEnum.plugin;
return (
<>
<MyBox
isLoading={isFetching}
display={'flex'}
flexDirection={'column'}
zIndex={3}
position={['fixed', 'absolute']}
top={[0, '2%']}
right={0}
h={['100%', '96%']}
w={'100%'}
maxW={['100%', '600px']}
bg={'white'}
boxShadow={'3px 0 20px rgba(0,0,0,0.2)'}
borderRadius={'md'}
overflow={'hidden'}
transition={'.2s ease'}
>
{/* Header */}
{isPlugin ? (
<Flex
alignItems={'flex-start'}
justifyContent={'space-between'}
px={3}
pt={3}
bg={'myGray.25'}
borderBottom={'base'}
>
<LightRowTabs<PluginRunBoxTabEnum>
list={[
{ label: t('common.Input'), value: PluginRunBoxTabEnum.input },
...(chatRecords.length > 0
? [
{ label: t('common.Output'), value: PluginRunBoxTabEnum.output },
{ label: '完整结果', value: PluginRunBoxTabEnum.detail }
]
: [])
]}
value={pluginRunTab}
onChange={setPluginRunTab}
inlineStyles={{ px: 0.5, pb: 2 }}
gap={5}
py={0}
fontSize={'sm'}
/>
<CloseIcon onClick={onClose} />
</Flex>
) : (
<Flex
alignItems={'center'}
px={[3, 5]}
h={['46px', '60px']}
borderBottom={theme.borders.base}
borderBottomColor={'gray.200'}
color={'myGray.900'}
>
{isPc ? (
<>
<Box mr={3} color={'myGray.1000'}>
{title}
</Box>
{chatRecords.length > 0 && (
<>
<MyTag colorSchema="blue">
<MyIcon name={'history'} w={'14px'} />
<Box ml={1}>{`${chatRecords.length}条记录`}</Box>
</MyTag>
{!!chatModels && (
<MyTag ml={2} colorSchema={'green'}>
<MyIcon name={'core/chat/chatModelTag'} w={'14px'} />
<Box ml={1}>{chatModels.join(',')}</Box>
</MyTag>
)}
</>
)}
<Box flex={1} />
</>
) : (
<>
<Flex px={3} alignItems={'center'} flex={'1 0 0'} w={0} justifyContent={'center'}>
<Box ml={1} className="textEllipsis">
{title}
</Box>
</Flex>
</>
)}
<CloseIcon onClick={onClose} />
</Flex>
)}
{/* Chat container */}
<Box pt={2} flex={'1 0 0'}>
{isPlugin ? (
<Box px={5} pt={2} h={'100%'}>
<PluginRunBox
pluginInputs={chat?.app.pluginInputs}
variablesForm={variablesForm}
histories={chatRecords}
setHistories={setChatRecords}
appId={chat.appId}
tab={pluginRunTab}
setTab={setPluginRunTab}
/>
</Box>
) : (
<ChatBox
ref={ChatBoxRef}
chatHistories={chatRecords}
setChatHistories={setChatRecords}
variablesForm={variablesForm}
appAvatar={chat?.app.avatar}
userAvatar={HUMAN_ICON}
feedbackType={'admin'}
showMarkIcon
showVoiceIcon={false}
chatConfig={chat?.app?.chatConfig}
appId={appId}
chatId={chatId}
/>
)}
</Box>
</MyBox>
<Box zIndex={2} position={'fixed'} top={0} left={0} bottom={0} right={0} onClick={onClose} />
</>
);
};
export default DetailLogsModal;

View File

@@ -1,4 +1,4 @@
import React, { useMemo, useRef, useState } from 'react';
import React, { useState } from 'react';
import {
Flex,
Box,
@@ -9,7 +9,6 @@ import {
Th,
Td,
Tbody,
useTheme,
useDisclosure,
ModalBody,
HStack
@@ -19,31 +18,26 @@ import { useTranslation } from 'next-i18next';
import { getAppChatLogs } from '@/web/core/app/api';
import dayjs from 'dayjs';
import { ChatSourceMap } from '@fastgpt/global/core/chat/constants';
import { HUMAN_ICON } from '@fastgpt/global/common/system/constants';
import { AppLogsListItemType } from '@/types/app';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import ChatBox from '@/components/ChatBox';
import type { ComponentRef } from '@/components/ChatBox/type.d';
import { useQuery } from '@tanstack/react-query';
import { getInitChatInfo } from '@/web/core/chat/api';
import MyTag from '@fastgpt/web/components/common/Tag/index';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { addDays } from 'date-fns';
import MyBox from '@fastgpt/web/components/common/MyBox';
import { usePagination } from '@fastgpt/web/hooks/usePagination';
import DateRangePicker, { DateRangeType } from '@fastgpt/web/components/common/DateRangePicker';
import { formatChatValue2InputType } from '@/components/ChatBox/utils';
import { getNanoid } from '@fastgpt/global/common/string/tools';
import { useI18n } from '@/web/context/I18n';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
import { useContextSelector } from 'use-context-selector';
import { AppContext } from '../context';
import { cardStyles } from '../constants';
import dynamic from 'next/dynamic';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
const DetailLogsModal = dynamic(() => import('./DetailLogsModal'));
const Logs = () => {
const { t } = useTranslation();
const { appT } = useI18n();
const { isPc } = useSystemStore();
const { isPc } = useSystem();
const appId = useContextSelector(AppContext, (v) => v.appId);
@@ -225,131 +219,3 @@ const Logs = () => {
};
export default React.memo(Logs);
const DetailLogsModal = ({
appId,
chatId,
onClose
}: {
appId: string;
chatId: string;
onClose: () => void;
}) => {
const ChatBoxRef = useRef<ComponentRef>(null);
const { isPc } = useSystemStore();
const theme = useTheme();
const { data: chat, isFetching } = useQuery(
['getChatDetail', chatId],
() => getInitChatInfo({ appId, chatId, loadCustomFeedbacks: true }),
{
onSuccess(res) {
const history = res.history.map((item) => ({
...item,
dataId: item.dataId || getNanoid(),
status: 'finish' as any
}));
ChatBoxRef.current?.resetHistory(history);
ChatBoxRef.current?.resetVariables(res.variables);
if (res.history.length > 0) {
setTimeout(() => {
ChatBoxRef.current?.scrollToBottom('auto');
}, 500);
}
}
}
);
const history = useMemo(() => (chat?.history ? chat.history : []), [chat]);
const title = useMemo(() => {
const { text } = formatChatValue2InputType(history[history.length - 2]?.value);
return text?.slice(0, 8);
}, [history]);
const chatModels = chat?.app?.chatModels;
return (
<>
<MyBox
isLoading={isFetching}
display={'flex'}
flexDirection={'column'}
zIndex={3}
position={['fixed', 'absolute']}
top={[0, '2%']}
right={0}
h={['100%', '96%']}
w={'100%'}
maxW={['100%', '600px']}
bg={'white'}
boxShadow={'3px 0 20px rgba(0,0,0,0.2)'}
borderRadius={'md'}
overflow={'hidden'}
transition={'.2s ease'}
>
<Flex
alignItems={'center'}
px={[3, 5]}
h={['46px', '60px']}
borderBottom={theme.borders.base}
borderBottomColor={'gray.200'}
color={'myGray.900'}
>
{isPc ? (
<>
<Box mr={3} color={'myGray.1000'}>
{title}
</Box>
<MyTag colorSchema="blue">
<MyIcon name={'history'} w={'14px'} />
<Box ml={1}>{`${history.length}条记录`}</Box>
</MyTag>
{!!chatModels && chatModels.length > 0 && (
<MyTag ml={2} colorSchema={'green'}>
<MyIcon name={'core/chat/chatModelTag'} w={'14px'} />
<Box ml={1}>{chatModels.join(',')}</Box>
</MyTag>
)}
<Box flex={1} />
</>
) : (
<>
<Flex px={3} alignItems={'center'} flex={'1 0 0'} w={0} justifyContent={'center'}>
<Box ml={1} className="textEllipsis">
{title}
</Box>
</Flex>
</>
)}
<Flex
alignItems={'center'}
justifyContent={'center'}
w={'20px'}
h={'20px'}
borderRadius={'50%'}
cursor={'pointer'}
_hover={{ bg: 'myGray.100' }}
onClick={onClose}
>
<MyIcon name={'common/closeLight'} w={'12px'} h={'12px'} color={'myGray.700'} />
</Flex>
</Flex>
<Box pt={2} flex={'1 0 0'}>
<ChatBox
ref={ChatBoxRef}
appAvatar={chat?.app.avatar}
userAvatar={HUMAN_ICON}
feedbackType={'admin'}
showMarkIcon
showVoiceIcon={false}
chatConfig={chat?.app?.chatConfig}
appId={appId}
chatId={chatId}
/>
</Box>
</MyBox>
<Box zIndex={2} position={'fixed'} top={0} left={0} bottom={0} right={0} onClick={onClose} />
</>
);
};

View File

@@ -14,11 +14,15 @@ import { useRouter } from 'next/router';
import AppCard from '../WorkflowComponents/AppCard';
import { uiWorkflow2StoreWorkflow } from '../WorkflowComponents/utils';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import RouteTab from '../RouteTab';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
const PublishHistories = dynamic(() => import('../PublishHistoriesSlider'));
const Header = () => {
const { t } = useTranslation();
const router = useRouter();
const { isPc } = useSystem();
const { appDetail, onPublish, currentTab } = useContextSelector(AppContext, (v) => v);
const isV2Workflow = appDetail?.version === 'v2';
@@ -27,6 +31,7 @@ const Header = () => {
flowData2StoreDataAndCheck,
onSaveWorkflow,
setHistoriesDefaultData,
setWorkflowTestData,
historiesDefaultData,
initData
} = useContextSelector(WorkflowContext, (v) => v);
@@ -62,11 +67,11 @@ const Header = () => {
const Render = useMemo(() => {
return (
<>
{/* {!isPc && (
{!isPc && (
<Flex pt={2} justifyContent={'center'}>
<RouteTab />
</Flex>
)} */}
)}
<Flex
mt={[2, 0]}
py={3}
@@ -101,18 +106,18 @@ const Header = () => {
/>
</Box>
{/* {isPc && (
{isPc && (
<Box position={'absolute'} left={'50%'} transform={'translateX(-50%)'}>
<RouteTab />
</Box>
)} */}
)}
<Box flex={1} />
{currentTab === TabEnum.appEdit && (
<>
{!historiesDefaultData && (
<IconButton
// mr={[2, 4]}
mr={[2, 4]}
icon={<MyIcon name={'history'} w={'18px'} />}
aria-label={''}
size={'sm'}
@@ -129,7 +134,7 @@ const Header = () => {
}}
/>
)}
{/* <Button
<Button
size={'sm'}
leftIcon={<MyIcon name={'core/workflow/debug'} w={['14px', '16px']} />}
variant={'whitePrimary'}
@@ -141,7 +146,7 @@ const Header = () => {
}}
>
{t('core.workflow.Debug')}
</Button> */}
</Button>
{!historiesDefaultData && (
<PopoverConfirm
@@ -176,12 +181,15 @@ const Header = () => {
}, [
appDetail.chatConfig,
currentTab,
flowData2StoreDataAndCheck,
historiesDefaultData,
initData,
isPc,
isV2Workflow,
onclickPublish,
saveAndBack,
setHistoriesDefaultData,
setWorkflowTestData,
t
]);

View File

@@ -27,7 +27,7 @@ const ChatTest = ({ appForm }: { appForm: AppSimpleEditFormType }) => {
setWorkflowData({ nodes, edges });
}, [appForm, setWorkflowData]);
const { resetChatBox, ChatBox } = useChatTest({
const { restartChat, ChatContainer } = useChatTest({
...workflowData,
chatConfig: appForm.chatConfig
});
@@ -48,13 +48,13 @@ const ChatTest = ({ appForm }: { appForm: AppSimpleEditFormType }) => {
aria-label={'delete'}
onClick={(e) => {
e.stopPropagation();
resetChatBox();
restartChat();
}}
/>
</MyTooltip>
</Flex>
<Box flex={1}>
<ChatBox />
<ChatContainer />
</Box>
</Flex>
);

View File

@@ -1,6 +1,5 @@
import React from 'react';
import { Box } from '@chakra-ui/react';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useMount } from 'ahooks';
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
import { appWorkflow2Form } from '@fastgpt/global/core/app/utils';
@@ -15,6 +14,7 @@ import { useContextSelector } from 'use-context-selector';
import { cardStyles } from '../constants';
import styles from './styles.module.scss';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
const Edit = ({
appForm,
@@ -23,7 +23,7 @@ const Edit = ({
appForm: AppSimpleEditFormType;
setAppForm: React.Dispatch<React.SetStateAction<AppSimpleEditFormType>>;
}) => {
const { isPc } = useSystemStore();
const { isPc } = useSystem();
const { loadAllDatasets } = useDatasetStore();
const { appDetail } = useContextSelector(AppContext, (v) => v);

View File

@@ -257,8 +257,8 @@ const EditForm = ({
<Flex alignItems={'center'}>
<Flex alignItems={'center'} flex={1}>
<MyIcon name={'core/app/toolCall'} w={'20px'} />
<FormLabel ml={2}>{t('core.app.Tool call')}()</FormLabel>
<QuestionTip ml={1} label={t('core.app.Tool call tip')} />
<FormLabel ml={2}>{appT('Plugin dispatch')}</FormLabel>
<QuestionTip ml={1} label={appT('Plugin dispatch tip')} />
</Flex>
<Button
variant={'transparentBase'}
@@ -315,7 +315,13 @@ const EditForm = ({
<VariableEdit
variables={appForm.chatConfig.variables}
onChange={(e) => {
appForm.chatConfig.variables = e;
setAppForm((state) => ({
...state,
chatConfig: {
...state.chatConfig,
variables: e
}
}));
}}
/>
</Box>

View File

@@ -21,6 +21,7 @@ import { compareWorkflow } from '@/web/core/workflow/utils';
import MyTag from '@fastgpt/web/components/common/Tag/index';
import { publishStatusStyle } from '../constants';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
const Header = ({
appForm,
@@ -30,7 +31,7 @@ const Header = ({
setAppForm: React.Dispatch<React.SetStateAction<AppSimpleEditFormType>>;
}) => {
const { t } = useTranslation();
const { isPc } = useSystemStore();
const { isPc } = useSystem();
const router = useRouter();
const { appId, appDetail, onPublish, currentTab } = useContextSelector(AppContext, (v) => v);

View File

@@ -17,11 +17,12 @@ import { useRouter } from 'next/router';
import AppCard from '../WorkflowComponents/AppCard';
import { uiWorkflow2StoreWorkflow } from '../WorkflowComponents/utils';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
const PublishHistories = dynamic(() => import('../PublishHistoriesSlider'));
const Header = () => {
const { t } = useTranslation();
const { isPc } = useSystemStore();
const { isPc } = useSystem();
const router = useRouter();
const { appDetail, onPublish, currentTab } = useContextSelector(AppContext, (v) => v);

View File

@@ -1,7 +1,7 @@
import type { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node.d';
import React, { forwardRef, ForwardedRef } from 'react';
import React from 'react';
import { SmallCloseIcon } from '@chakra-ui/icons';
import { Box, Flex, IconButton } from '@chakra-ui/react';
import { Box, Flex, HStack, IconButton } from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { useTranslation } from 'next-i18next';
@@ -10,29 +10,27 @@ import { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
import { useContextSelector } from 'use-context-selector';
import { AppContext } from '@/pages/app/detail/components/context';
import { useChatTest } from '@/pages/app/detail/components/useChatTest';
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';
export type ChatTestComponentRef = {
resetChatTest: () => void;
};
const ChatTest = (
{
isOpen,
nodes = [],
edges = [],
onClose
}: {
isOpen: boolean;
nodes?: StoreNodeItemType[];
edges?: StoreEdgeItemType[];
onClose: () => void;
},
ref: ForwardedRef<ChatTestComponentRef>
) => {
const ChatTest = ({
isOpen,
nodes = [],
edges = [],
onClose
}: {
isOpen: boolean;
nodes?: StoreNodeItemType[];
edges?: StoreEdgeItemType[];
onClose: () => void;
}) => {
const { t } = useTranslation();
const { appDetail } = useContextSelector(AppContext, (v) => v);
const isPlugin = appDetail.type === AppTypeEnum.plugin;
const { resetChatBox, ChatBox } = useChatTest({
const { restartChat, ChatContainer, pluginRunTab, setPluginRunTab, chatRecords } = useChatTest({
nodes,
edges,
chatConfig: appDetail.chatConfig
@@ -64,40 +62,76 @@ const ChatTest = (
overflow={'hidden'}
transition={'.2s ease'}
>
<Flex py={4} px={5} whiteSpace={'nowrap'}>
<Box fontSize={'lg'} fontWeight={'bold'} flex={1}>
{t('core.chat.Debug test')}
</Box>
<MyTooltip label={t('core.chat.Restart')}>
<IconButton
className="chat"
size={'smSquare'}
icon={<MyIcon name={'common/clearLight'} w={'14px'} />}
variant={'whiteDanger'}
borderRadius={'md'}
aria-label={'delete'}
onClick={(e) => {
resetChatBox();
}}
{isPlugin ? (
<Flex
alignItems={'flex-start'}
justifyContent={'space-between'}
px={3}
pt={3}
bg={'myGray.25'}
borderBottom={'base'}
>
<LightRowTabs<PluginRunBoxTabEnum>
list={[
{ label: t('common.Input'), value: PluginRunBoxTabEnum.input },
...(chatRecords.length > 0
? [
{ label: t('common.Output'), value: PluginRunBoxTabEnum.output },
{ label: '完整结果', value: PluginRunBoxTabEnum.detail }
]
: [])
]}
value={pluginRunTab}
onChange={setPluginRunTab}
inlineStyles={{ px: 0.5, pb: 2 }}
gap={5}
py={0}
fontSize={'sm'}
/>
</MyTooltip>
<MyTooltip label={t('common.Close')}>
<IconButton
ml={3}
icon={<SmallCloseIcon fontSize={'22px'} />}
variant={'grayBase'}
size={'smSquare'}
aria-label={''}
onClick={onClose}
/>
</MyTooltip>
</Flex>
<Box flex={1}>
<ChatBox />
<CloseIcon mt={1} onClick={onClose} />
</Flex>
) : (
<Flex
py={4}
px={5}
whiteSpace={'nowrap'}
bg={isPlugin ? 'myGray.25' : ''}
borderBottom={isPlugin ? '1px solid #F4F4F7' : ''}
>
<Box fontSize={'lg'} fontWeight={'bold'} flex={1}>
{t('core.chat.Debug test')}
</Box>
<MyTooltip label={t('core.chat.Restart')}>
<IconButton
className="chat"
size={'smSquare'}
icon={<MyIcon name={'common/clearLight'} w={'14px'} />}
variant={'whiteDanger'}
borderRadius={'md'}
aria-label={'delete'}
onClick={restartChat}
/>
</MyTooltip>
<MyTooltip label={t('common.Close')}>
<IconButton
ml={4}
icon={<SmallCloseIcon fontSize={'22px'} />}
variant={'grayBase'}
size={'smSquare'}
aria-label={''}
onClick={onClose}
/>
</MyTooltip>
</Flex>
)}
<Box flex={'1 0 0'} overflow={'auto'}>
<ChatContainer />
</Box>
</Flex>
</>
);
};
export default React.memo(forwardRef(ChatTest));
export default React.memo(ChatTest);

View File

@@ -32,6 +32,7 @@ import { getAppFolderPath } from '@/web/core/app/api/app';
import { useWorkflowUtils } from './hooks/useUtils';
import { moduleTemplatesFlat } from '@fastgpt/global/core/workflow/template/constants';
import { cloneDeep } from 'lodash';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
type ModuleTemplateListProps = {
isOpen: boolean;
@@ -268,7 +269,7 @@ const RenderList = React.memo(function RenderList({
const { t } = useTranslation();
const { appT } = useI18n();
const { isPc } = useSystemStore();
const { isPc } = useSystem();
const { x, y, zoom } = useViewport();
const { setLoading } = useSystemStore();
const { toast } = useToast();

View File

@@ -138,7 +138,7 @@ export const useDebug = () => {
setRuntimeEdges(undefined);
};
const onclickRun = (data: Record<string, any>) => {
const onClickRun = (data: Record<string, any>) => {
onStartNodeDebug({
entryNodeId: runtimeNode.nodeId,
runtimeNodes: runtimeNodes.map((node) =>
@@ -261,7 +261,7 @@ export const useDebug = () => {
})}
</Box>
<Flex py={2} justifyContent={'flex-end'} px={6}>
<Button onClick={handleSubmit(onclickRun)}></Button>
<Button onClick={handleSubmit(onClickRun)}></Button>
</Flex>
</MyRightDrawer>
);

View File

@@ -13,7 +13,6 @@ import { ToolTargetHandle } from './Handle/ToolHandle';
import { useEditTextarea } from '@fastgpt/web/hooks/useEditTextarea';
import { ConnectionSourceHandle, ConnectionTargetHandle } from './Handle/ConnectionHandle';
import { useDebug } from '../../hooks/useDebug';
import { ResponseBox } from '@/components/ChatBox/components/WholeResponseModal';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
import { getPreviewPluginNode } from '@/web/core/app/api/plugin';
import { storeNode2FlowNode, getLatestNodeTemplate } from '@/web/core/workflow/utils';
@@ -26,6 +25,7 @@ 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';
import { ResponseBox } from '@/components/core/chat/components/WholeResponseModal';
type Props = FlowNodeItemType & {
children?: React.ReactNode | React.ReactNode[] | string;
@@ -566,11 +566,10 @@ const NodeDebugResponse = React.memo(function NodeDebugResponse({
w={'420px'}
maxH={'100%'}
minH={'300px'}
overflowY={'auto'}
border={'base'}
>
{/* Status header */}
<Flex px={4} mb={1} py={3} alignItems={'center'} borderBottom={'base'}>
<Flex h={'54x'} px={4} mb={1} py={3} alignItems={'center'} borderBottom={'base'}>
<MyIcon mr={1} name={'core/workflow/debugResult'} w={'20px'} color={'primary.600'} />
<Box fontWeight={'bold'} flex={'1'}>
{t('core.workflow.debug.Run result')}
@@ -606,7 +605,7 @@ const NodeDebugResponse = React.memo(function NodeDebugResponse({
)}
</Flex>
{/* Show result */}
<Box maxH={'100%'} overflow={'auto'}>
<Box maxH={'calc(100%-54px)'} overflow={'auto'}>
{!debugResult.message && !response && (
<EmptyTip text={t('core.workflow.debug.Not result')} pt={2} pb={5} />
)}

View File

@@ -44,7 +44,7 @@ import { EventNameEnum, eventBus } from '@/web/common/utils/eventbus';
import { getHandleId } from '@fastgpt/global/core/workflow/utils';
import { AppChatConfigType } from '@fastgpt/global/core/app/type';
import { AppContext } from '@/pages/app/detail/components/context';
import ChatTest, { type ChatTestComponentRef } from './Flow/ChatTest';
import ChatTest from './Flow/ChatTest';
import { useDisclosure } from '@chakra-ui/react';
import { uiWorkflow2StoreWorkflow } from './utils';
import { useTranslation } from 'next-i18next';
@@ -750,7 +750,6 @@ const WorkflowContextProvider = ({
}, [edges, nodes]);
/* chat test */
const ChatTestRef = useRef<ChatTestComponentRef>(null);
const { isOpen: isOpenTest, onOpen: onOpenTest, onClose: onCloseTest } = useDisclosure();
const [workflowTestData, setWorkflowTestData] = useState<{
nodes: StoreNodeItemType[];
@@ -813,7 +812,7 @@ const WorkflowContextProvider = ({
return (
<WorkflowContext.Provider value={value}>
{children}
<ChatTest ref={ChatTestRef} isOpen={isOpenTest} {...workflowTestData} onClose={onCloseTest} />
<ChatTest isOpen={isOpenTest} {...workflowTestData} onClose={onCloseTest} />
</WorkflowContext.Provider>
);
};

View File

@@ -1,7 +1,6 @@
import { useUserStore } from '@/web/support/user/useUserStore';
import React, { useCallback, useRef } from 'react';
import ChatBox from '@/components/ChatBox';
import type { ComponentRef, StartChatFnProps } from '@/components/ChatBox/type.d';
import React from 'react';
import type { StartChatFnProps } from '@/components/core/chat/ChatContainer/type';
import { streamFetch } from '@/web/common/api/fetch';
import { checkChatSupportSelectFileByModules } from '@/web/core/chat/utils';
import {
@@ -16,6 +15,14 @@ import { useContextSelector } from 'use-context-selector';
import { AppContext } from './context';
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
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 } from '@chakra-ui/react';
import ChatBox from '@/components/core/chat/ChatContainer/ChatBox';
const PluginRunBox = dynamic(() => import('@/components/core/chat/ChatContainer/PluginRunBox'));
export const useChatTest = ({
nodes,
@@ -27,22 +34,19 @@ export const useChatTest = ({
chatConfig: AppChatConfigType;
}) => {
const { userInfo } = useUserStore();
const ChatBoxRef = useRef<ComponentRef>(null);
const { appDetail } = useContextSelector(AppContext, (v) => v);
const startChat = useMemoizedFn(
async ({ chatList, controller, generatingMessage, variables }: StartChatFnProps) => {
async ({ messages, controller, generatingMessage, variables }: StartChatFnProps) => {
/* get histories */
let historyMaxLen = getMaxHistoryLimitFromNodes(nodes);
const history = chatList.slice(-historyMaxLen - 2, -2);
const historyMaxLen = getMaxHistoryLimitFromNodes(nodes);
// 流请求,获取数据
const { responseText, responseData } = await streamFetch({
url: '/api/core/chat/chatTest',
data: {
history,
prompt: chatList[chatList.length - 2].value,
// Send histories and user messages
messages: messages.slice(-historyMaxLen - 2),
nodes: storeNodes2RuntimeNodes(nodes, getDefaultEntryNodeIds(nodes)),
edges: initWorkflowEdgeStatus(edges),
variables,
@@ -57,28 +61,57 @@ export const useChatTest = ({
}
);
const resetChatBox = useCallback(() => {
ChatBoxRef.current?.resetHistory([]);
ChatBoxRef.current?.resetVariables();
}, []);
const pluginInputs =
nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput)?.inputs || [];
const {
ChatBoxRef,
chatRecords,
setChatRecords,
variablesForm,
pluginRunTab,
setPluginRunTab,
clearChatRecords
} = useChat();
const CustomChatBox = useMemoizedFn(() => (
<ChatBox
ref={ChatBoxRef}
appId={appDetail._id}
appAvatar={appDetail.avatar}
userAvatar={userInfo?.avatar}
showMarkIcon
chatConfig={chatConfig}
showFileSelector={checkChatSupportSelectFileByModules(nodes)}
onStartChat={startChat}
onDelMessage={() => {}}
/>
));
const CustomChatContainer = useMemoizedFn(() =>
appDetail.type === AppTypeEnum.plugin ? (
<Box h={'100%'} p={3}>
<PluginRunBox
pluginInputs={pluginInputs}
variablesForm={variablesForm}
histories={chatRecords}
setHistories={setChatRecords}
appId={appDetail._id}
tab={pluginRunTab}
setTab={setPluginRunTab}
onNewChat={clearChatRecords}
onStartChat={startChat}
/>
</Box>
) : (
<ChatBox
ref={ChatBoxRef}
chatHistories={chatRecords}
setChatHistories={setChatRecords}
variablesForm={variablesForm}
appId={appDetail._id}
appAvatar={appDetail.avatar}
userAvatar={userInfo?.avatar}
showMarkIcon
chatConfig={chatConfig}
showFileSelector={checkChatSupportSelectFileByModules(nodes)}
onStartChat={startChat}
onDelMessage={() => {}}
/>
)
);
return {
resetChatBox,
ChatBox: CustomChatBox
restartChat: clearChatRecords,
ChatContainer: CustomChatContainer,
chatRecords,
pluginRunTab,
setPluginRunTab
};
};

View File

@@ -29,6 +29,7 @@ import { useContextSelector } from 'use-context-selector';
import { AppListContext } from './context';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { useI18n } from '@/web/context/I18n';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
type FormType = {
avatar: string;
@@ -44,7 +45,7 @@ const CreateModal = ({ onClose, type }: { type: CreateAppType; onClose: () => vo
const { toast } = useToast();
const router = useRouter();
const { parentId, loadMyApps } = useContextSelector(AppListContext, (v) => v);
const { isPc } = useSystemStore();
const { isPc } = useSystem();
const typeMap = useRef({
[AppTypeEnum.simple]: {

View File

@@ -271,6 +271,21 @@ const ListItem = () => {
}
]
: []),
...([AppTypeEnum.plugin].includes(app.type)
? [
{
children: [
{
icon: 'core/chat/chatLight',
label: appT('Go to run'),
onClick: () => {
router.push(`/chat?appId=${app._id}`);
}
}
]
}
]
: []),
{
children: [
{

View File

@@ -14,6 +14,7 @@ import { AppUpdateParams } from '@/global/core/app/api';
import dynamic from 'next/dynamic';
import { useI18n } from '@/web/context/I18n';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { useThrottleEffect } from 'ahooks';
const MoveModal = dynamic(() => import('@/components/common/folder/MoveModal'));
type AppListContextType = {
@@ -27,6 +28,8 @@ type AppListContextType = {
onUpdateApp: (id: string, data: AppUpdateParams) => Promise<any>;
setMoveAppId: React.Dispatch<React.SetStateAction<string | undefined>>;
refetchFolderDetail: () => Promise<AppDetailType | null>;
searchKey: string;
setSearchKey: React.Dispatch<React.SetStateAction<string>>;
};
export const AppListContext = createContext<AppListContextType>({
@@ -47,6 +50,10 @@ export const AppListContext = createContext<AppListContextType>({
appType: 'ALL',
refetchFolderDetail: async function (): Promise<AppDetailType | null> {
throw new Error('Function not implemented.');
},
searchKey: '',
setSearchKey: function (value: React.SetStateAction<string>): void {
throw new Error('Function not implemented.');
}
});
@@ -57,6 +64,7 @@ const AppListContextProvider = ({ children }: { children: ReactNode }) => {
parentId?: string | null;
type: AppTypeEnum;
};
const [searchKey, setSearchKey] = useState('');
const {
data = [],
@@ -72,14 +80,22 @@ const AppListContextProvider = ({ children }: { children: ReactNode }) => {
return [AppTypeEnum.folder, type];
})();
return getMyApps({ parentId, type: formatType });
return getMyApps({ parentId, type: formatType, searchKey });
},
{
manual: false,
refreshOnWindowFocus: true,
refreshDeps: [parentId, type]
}
);
useThrottleEffect(
() => {
loadMyApps();
},
[searchKey],
{
wait: 500
}
);
const { data: paths = [], runAsync: refetchPaths } = useRequest2(
() => getAppFolderPath(parentId),
@@ -138,7 +154,9 @@ const AppListContextProvider = ({ children }: { children: ReactNode }) => {
folderDetail,
paths,
onUpdateApp,
setMoveAppId
setMoveAppId,
searchKey,
setSearchKey
};
return (
<AppListContext.Provider value={contextValue}>

View File

@@ -1,5 +1,13 @@
import React, { useState } from 'react';
import { Box, Flex, Button, useDisclosure } from '@chakra-ui/react';
import React, { useMemo, useState } from 'react';
import {
Box,
Flex,
Button,
useDisclosure,
Input,
InputGroup,
InputLeftElement
} from '@chakra-ui/react';
import { AddIcon } from '@chakra-ui/icons';
import { serviceSideProps } from '@/web/common/utils/i18n';
import { useUserStore } from '@/web/support/user/useUserStore';
@@ -28,11 +36,12 @@ import {
getCollaboratorList,
postUpdateAppCollaborators
} from '@/web/core/app/api/collaborator';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import type { CreateAppType } from './components/CreateModal';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import MyBox from '@fastgpt/web/components/common/MyBox';
import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
import MyIcon from '@fastgpt/web/components/common/Icon';
const CreateModal = dynamic(() => import('./components/CreateModal'));
const EditFolderModal = dynamic(
@@ -44,7 +53,7 @@ const MyApps = () => {
const { t } = useTranslation();
const { appT } = useI18n();
const router = useRouter();
const { isPc } = useSystemStore();
const { isPc } = useSystem();
const {
paths,
parentId,
@@ -55,7 +64,9 @@ const MyApps = () => {
setMoveAppId,
isFetchingApps,
folderDetail,
refetchFolderDetail
refetchFolderDetail,
searchKey,
setSearchKey
} = useContextSelector(AppListContext, (v) => v);
const { userInfo } = useUserStore();
@@ -84,13 +95,26 @@ const MyApps = () => {
errorToast: 'Error'
});
const RenderSearchInput = useMemo(
() => (
<InputGroup maxW={['auto', '250px']}>
<InputLeftElement h={'full'} alignItems={'center'} display={'flex'}>
<MyIcon name={'common/searchLight'} w={'1rem'} />
</InputLeftElement>
<Input
value={searchKey}
onChange={(e) => setSearchKey(e.target.value)}
placeholder={appT('Search app')}
maxLength={30}
bg={'white'}
/>
</InputGroup>
),
[searchKey, setSearchKey, appT]
);
return (
<MyBox
display={'flex'}
flexDirection={'column'}
isLoading={myApps.length === 0 && isFetchingApps}
h={'100%'}
>
<Flex flexDirection={'column'} h={'100%'}>
{paths.length > 0 && (
<Box pt={[4, 6]} pl={3}>
<FolderPath
@@ -116,11 +140,7 @@ const MyApps = () => {
overflowY={'auto'}
overflowX={'hidden'}
>
<Flex
pt={paths.length > 0 ? 4 : [4, 6]}
alignItems={'center'}
justifyContent={'space-between'}
>
<Flex pt={paths.length > 0 ? 3 : [4, 6]} alignItems={'center'} gap={3}>
<LightRowTabs
list={[
{
@@ -146,6 +166,7 @@ const MyApps = () => {
display={'flex'}
alignItems={'center'}
fontSize={['sm', 'md']}
flexShrink={0}
onChange={(e) => {
router.push({
query: {
@@ -155,6 +176,9 @@ const MyApps = () => {
});
}}
/>
<Box flex={1} />
{isPc && RenderSearchInput}
{userInfo?.team.permission.hasWritePer &&
folderDetail?.type !== AppTypeEnum.httpPlugin && (
@@ -208,8 +232,14 @@ const MyApps = () => {
)}
</Flex>
<List />
{!isPc && <Box mt={2}>{RenderSearchInput}</Box>}
<MyBox flex={'1 0 0'} isLoading={myApps.length === 0 && isFetchingApps}>
<List />
</MyBox>
</Box>
{/* Folder slider */}
{!!folderDetail && isPc && (
<Box pt={[4, 6]} pr={[4, 6]}>
<FolderSlideCard
@@ -278,7 +308,7 @@ const MyApps = () => {
<CreateModal type={createAppType} onClose={() => setCreateAppType(undefined)} />
)}
{isOpenCreateHttpPlugin && <HttpEditModal onClose={onCloseCreateHttpPlugin} />}
</MyBox>
</Flex>
);
};

View File

@@ -1,4 +1,4 @@
import React, { useMemo } from 'react';
import React from 'react';
import { Flex, useTheme, Box } from '@chakra-ui/react';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import MyIcon from '@fastgpt/web/components/common/Icon';
@@ -6,51 +6,47 @@ import Avatar from '@/components/Avatar';
import ToolMenu from './ToolMenu';
import type { ChatItemType } from '@fastgpt/global/core/chat/type';
import { useTranslation } from 'next-i18next';
import { getChatTitleFromChatMessage } from '@fastgpt/global/core/chat/utils';
import MyTag from '@fastgpt/web/components/common/Tag/index';
import { useContextSelector } from 'use-context-selector';
import { ChatContext } from '@/web/core/chat/context/chatContext';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { InitChatResponse } from '@/global/core/chat/api';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
const ChatHeader = ({
chatData,
history,
appName,
appAvatar,
chatModels,
showHistory,
onRoute2AppDetail
}: {
chatData: InitChatResponse;
history: ChatItemType[];
appName: string;
appAvatar: string;
chatModels?: string[];
showHistory?: boolean;
onRoute2AppDetail?: () => void;
}) => {
const theme = useTheme();
const { t } = useTranslation();
const { isPc } = useSystemStore();
const title = useMemo(
() =>
getChatTitleFromChatMessage(history[history.length - 2], appName || t('core.chat.New Chat')),
[appName, history, t]
);
const { isPc } = useSystem();
const chatModels = chatData.app.chatModels;
const isPlugin = chatData.app.type === AppTypeEnum.plugin;
const onOpenSlider = useContextSelector(ChatContext, (v) => v.onOpenSlider);
return (
return isPc && isPlugin ? null : (
<Flex
alignItems={'center'}
px={[3, 5]}
h={['46px', '60px']}
minH={['46px', '60px']}
borderBottom={theme.borders.sm}
color={'myGray.900'}
fontSize={'sm'}
>
{isPc ? (
<>
<Box mr={3} color={'myGray.1000'}>
{title}
<Box mr={3} maxW={'160px'} className="textEllipsis" color={'myGray.1000'}>
{chatData.title}
</Box>
<MyTag>
<MyIcon name={'history'} w={'14px'} />
@@ -85,15 +81,16 @@ const ChatHeader = ({
)}
<Flex px={3} alignItems={'center'} flex={'1 0 0'} w={0} justifyContent={'center'}>
<Avatar src={appAvatar} w={'16px'} />
<Avatar src={chatData.app.avatar} w={'16px'} />
<Box ml={1} className="textEllipsis" onClick={onRoute2AppDetail}>
{appName}
{chatData.app.name}
</Box>
</Flex>
</>
)}
{/* control */}
<ToolMenu history={history} />
{!isPlugin && <ToolMenu history={history} />}
</Flex>
);
};

View File

@@ -23,6 +23,7 @@ import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { useContextSelector } from 'use-context-selector';
import { ChatContext } from '@/web/core/chat/context/chatContext';
import MyBox from '@fastgpt/web/components/common/MyBox';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
type HistoryItemType = {
id: string;
@@ -65,7 +66,7 @@ const ChatHistorySlider = ({
const { t } = useTranslation();
const { appT } = useI18n();
const { isPc } = useSystemStore();
const { isPc } = useSystem();
const { userInfo } = useUserStore();
const [currentTab, setCurrentTab] = useState<TabEnum>(TabEnum.history);
@@ -91,8 +92,6 @@ const ChatHistorySlider = ({
return !activeChat ? [newChat].concat(formatHistories) : formatHistories;
}, [activeChatId, histories, t]);
const showApps = apps?.length > 0;
// custom title edit
const { onOpenModal, EditModal: EditTitleModal } = useEditTitle({
title: t('core.chat.Custom History Title'),

View File

@@ -0,0 +1,71 @@
import { PluginRunBoxProps } from '@/components/core/chat/ChatContainer/PluginRunBox/type';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
import React, { useEffect } from 'react';
import PluginRunBox from '@/components/core/chat/ChatContainer/PluginRunBox';
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';
const CustomPluginRunBox = (props: PluginRunBoxProps) => {
const { tab, setTab } = props;
const { isPc } = useSystem();
const { t } = useTranslation();
useEffect(() => {
if (isPc && tab === PluginRunBoxTabEnum.input) {
setTab(PluginRunBoxTabEnum.output);
}
}, [isPc, setTab, tab]);
return isPc ? (
<Grid gridTemplateColumns={'450px 1fr'} h={'100%'}>
<Box px={3} py={4} borderRight={'base'} h={'100%'} overflowY={'auto'} w={'100%'}>
<Box color={'myGray.900'} mb={5}>
{t('common.Input')}
</Box>
<PluginRunBox {...props} tab={PluginRunBoxTabEnum.input} />
</Box>
<Stack px={3} py={4} h={'100%'} alignItems={'flex-start'} w={'100%'} overflow={'auto'}>
<Box display={'inline-block'} mb={5}>
<LightRowTabs<PluginRunBoxTabEnum>
list={[
{ label: t('common.Output'), value: PluginRunBoxTabEnum.output },
{ label: '完整结果', value: PluginRunBoxTabEnum.detail }
]}
value={tab}
onChange={setTab}
inlineStyles={{ px: 0.5, pt: 0 }}
gap={5}
py={0}
fontSize={'sm'}
/>
</Box>
<Box flex={'1 0 0'} overflow={'auto'} w={'100%'}>
<PluginRunBox {...props} />
</Box>
</Stack>
</Grid>
) : (
<Stack py={2} px={4} h={'100%'}>
<LightRowTabs<PluginRunBoxTabEnum>
list={[
{ label: t('common.Input'), value: PluginRunBoxTabEnum.input },
{ label: t('common.Output'), value: PluginRunBoxTabEnum.output },
{ label: '完整结果', value: PluginRunBoxTabEnum.detail }
]}
value={tab}
onChange={setTab}
inlineStyles={{ px: 0.5, pt: 0 }}
gap={5}
py={0}
fontSize={'sm'}
/>
<Box mt={3} flex={'1 0 0'} w={'100%'}>
<PluginRunBox {...props} />
</Box>
</Stack>
);
};
export default React.memo(CustomPluginRunBox);

View File

@@ -25,7 +25,7 @@ const SliderApps = ({ apps, activeAppId }: { apps: AppListItemType[]; activeAppI
const getAppList = useCallback(async ({ parentId }: GetResourceFolderListProps) => {
return getMyApps({
parentId,
type: [AppTypeEnum.folder, AppTypeEnum.simple, AppTypeEnum.workflow]
type: [AppTypeEnum.folder, AppTypeEnum.simple, AppTypeEnum.workflow, AppTypeEnum.plugin]
}).then((res) =>
res.map<GetResourceListItemResponse>((item) => ({
id: item._id,
@@ -112,6 +112,7 @@ const SliderApps = ({ apps, activeAppId }: { apps: AppListItemType[]; activeAppI
{({ onClose }) => (
<Box minH={'200px'}>
<SelectOneResource
maxH={'60vh'}
value={activeAppId}
onSelect={(id) => {
if (!id) return;

View File

@@ -1,5 +1,5 @@
import React, { useMemo } from 'react';
import { useChatBox } from '@/components/ChatBox/hooks/useChatBox';
import { useChatBox } from '@/components/core/chat/ChatContainer/ChatBox/hooks/useChatBox';
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
import { Box, IconButton } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useRef, useState } from 'react';
import React, { useCallback, useState } from 'react';
import NextHead from '@/components/common/NextHead';
import { useRouter } from 'next/router';
import { delChatRecordById, getChatHistories, getInitChatInfo } from '@/web/core/chat/api';
@@ -9,8 +9,7 @@ import { useChatStore } from '@/web/core/chat/context/storeChat';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { useTranslation } from 'next-i18next';
import ChatBox from '@/components/ChatBox';
import type { ComponentRef, StartChatFnProps } from '@/components/ChatBox/type.d';
import type { StartChatFnProps } from '@/components/core/chat/ChatContainer/type';
import PageContainer from '@/components/PageContainer';
import SideBar from '@/components/SideBar';
import ChatHistorySlider from './components/ChatHistorySlider';
@@ -32,6 +31,13 @@ import { defaultChatData } 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';
const CustomPluginRunBox = dynamic(() => import('./components/CustomPluginRunBox'));
type Props = { appId: string; chatId: string };
@@ -46,8 +52,6 @@ const Chat = ({
const theme = useTheme();
const { t } = useTranslation();
const ChatBoxRef = useRef<ComponentRef>(null);
const { setLastChatAppId } = useChatStore();
const {
loadHistories,
@@ -59,84 +63,43 @@ const Chat = ({
forbidLoadChat,
onChangeChatId
} = useContextSelector(ChatContext, (v) => v);
const {
ChatBoxRef,
chatRecords,
setChatRecords,
variablesForm,
pluginRunTab,
setPluginRunTab,
resetChatRecords
} = useChat();
const { userInfo } = useUserStore();
const { isPc } = useSystemStore();
const startChat = useCallback(
async ({ messages, controller, generatingMessage, variables }: StartChatFnProps) => {
const prompts = messages.slice(-2);
const completionChatId = chatId ? chatId : getNanoid();
const { responseText, responseData } = await streamFetch({
data: {
messages: prompts,
variables,
appId,
chatId: completionChatId
},
onMessage: generatingMessage,
abortCtrl: controller
});
const newTitle = getChatTitleFromChatMessage(GPTMessages2Chats(prompts)[0]);
// new chat
if (completionChatId !== chatId) {
if (controller.signal.reason !== 'leave') {
onChangeChatId(completionChatId, true);
loadHistories();
}
} else {
// update chat
onUpdateHistory({
appId,
chatId: completionChatId,
title: newTitle
});
}
// update chat window
setChatData((state) => ({
...state,
title: newTitle,
history: ChatBoxRef.current?.getChatHistories() || state.history
}));
return { responseText, responseData, isNewChat: forbidLoadChat.current };
},
[appId, chatId, forbidLoadChat, loadHistories, onChangeChatId, onUpdateHistory]
);
const { isPc } = useSystem();
// get chat app info
const [chatData, setChatData] = useState<InitChatResponse>(defaultChatData);
const isPlugin = chatData.app.type === AppTypeEnum.plugin;
const { loading } = useRequest2(
async () => {
if (!appId || forbidLoadChat.current) return;
const res = await getInitChatInfo({ appId, chatId });
setChatData(res);
const history = res.history.map((item) => ({
...item,
dataId: item.dataId || getNanoid(),
status: ChatStatusEnum.finish
}));
const result: InitChatResponse = {
...res,
history
};
// reset chat box
ChatBoxRef.current?.resetHistory(history);
ChatBoxRef.current?.resetVariables(res.variables);
if (history.length > 0) {
setTimeout(() => {
ChatBoxRef.current?.scrollToBottom('auto');
}, 500);
}
// reset chat records
resetChatRecords({
records: history,
variables: res.variables
});
setLastChatAppId(appId);
setChatData(result);
},
{
manual: false,
@@ -157,6 +120,51 @@ const Chat = ({
}
);
const onStartChat = useCallback(
async ({
messages,
responseChatItemId,
controller,
generatingMessage,
variables
}: StartChatFnProps) => {
const completionChatId = chatId || getNanoid();
// Just send a user prompt
const histories = messages.slice(-1);
const { responseText, responseData } = await streamFetch({
data: {
messages: histories,
variables,
responseChatItemId,
appId,
chatId: completionChatId
},
onMessage: generatingMessage,
abortCtrl: controller
});
const newTitle = getChatTitleFromChatMessage(GPTMessages2Chats(histories)[0]);
// new chat
if (completionChatId !== chatId) {
if (controller.signal.reason !== 'leave') {
onChangeChatId(completionChatId, true);
}
}
loadHistories();
// update chat window
setChatData((state) => ({
...state,
title: newTitle
}));
return { responseText, responseData, isNewChat: forbidLoadChat.current };
},
[appId, chatId, forbidLoadChat, loadHistories, onChangeChatId]
);
return (
<Flex h={'100%'}>
<NextHead title={chatData.app.name} icon={chatData.app.avatar}></NextHead>
@@ -168,7 +176,7 @@ const Chat = ({
)}
<PageContainer isLoading={loading} flex={'1 0 0'} w={0} p={[0, '16px']} position={'relative'}>
<Flex h={'100%'} flexDirection={['column', 'row']} bg={'white'}>
<Flex h={'100%'} flexDirection={['column', 'row']}>
{/* pc always show history. */}
{((children: React.ReactNode) => {
return isPc || !appId ? (
@@ -218,29 +226,44 @@ const Chat = ({
>
{/* header */}
<ChatHeader
appAvatar={chatData.app.avatar}
appName={chatData.app.name}
history={chatData.history}
chatModels={chatData.app.chatModels}
chatData={chatData}
history={chatRecords}
onRoute2AppDetail={() => router.push(`/app/detail?appId=${appId}`)}
showHistory
/>
{/* chat box */}
<Box flex={1}>
<ChatBox
ref={ChatBoxRef}
showEmptyIntro
appAvatar={chatData.app.avatar}
userAvatar={userInfo?.avatar}
chatConfig={chatData.app?.chatConfig}
showFileSelector={checkChatSupportSelectFileByChatModels(chatData.app.chatModels)}
feedbackType={'user'}
onStartChat={startChat}
onDelMessage={({ contentId }) => delChatRecordById({ contentId, appId, chatId })}
appId={appId}
chatId={chatId}
/>
<Box flex={'1 0 0'} bg={'white'}>
{isPlugin ? (
<CustomPluginRunBox
pluginInputs={chatData.app.pluginInputs}
variablesForm={variablesForm}
histories={chatRecords}
setHistories={setChatRecords}
appId={chatData.appId}
tab={pluginRunTab}
setTab={setPluginRunTab}
onNewChat={() => onChangeChatId(getNanoid())}
onStartChat={onStartChat}
/>
) : (
<ChatBox
ref={ChatBoxRef}
chatHistories={chatRecords}
setChatHistories={setChatRecords}
variablesForm={variablesForm}
showEmptyIntro
appAvatar={chatData.app.avatar}
userAvatar={userInfo?.avatar}
chatConfig={chatData.app?.chatConfig}
showFileSelector={checkChatSupportSelectFileByChatModels(chatData.app.chatModels)}
feedbackType={'user'}
onStartChat={onStartChat}
onDelMessage={({ contentId }) => delChatRecordById({ contentId, appId, chatId })}
appId={appId}
chatId={chatId}
/>
)}
</Box>
</Flex>
</Flex>

View File

@@ -1,18 +1,17 @@
import React, { useCallback, useRef, useState } from 'react';
import { useRouter } from 'next/router';
import { Box, Flex, Drawer, DrawerOverlay, DrawerContent } from '@chakra-ui/react';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { streamFetch } from '@/web/common/api/fetch';
import { useShareChatStore } from '@/web/core/chat/storeShareChat';
import SideBar from '@/components/SideBar';
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { customAlphabet } from 'nanoid';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
import ChatBox from '@/components/ChatBox';
import type { ComponentRef, StartChatFnProps } from '@/components/ChatBox/type.d';
import ChatBox from '@/components/core/chat/ChatContainer/ChatBox';
import type { StartChatFnProps } from '@/components/core/chat/ChatContainer/type';
import PageContainer from '@/components/PageContainer';
import ChatHeader from './components/ChatHeader';
import ChatHistorySlider from './components/ChatHistorySlider';
@@ -33,6 +32,13 @@ import { InitChatResponse } from '@/global/core/chat/api';
import { defaultChatData } 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';
const CustomPluginRunBox = dynamic(() => import('./components/CustomPluginRunBox'));
type Props = {
appName: string;
@@ -60,8 +66,7 @@ const OutLink = ({ appName, appIntro, appAvatar }: Props) => {
authToken: string;
[key: string]: string;
};
const { isPc } = useSystemStore();
const ChatBoxRef = useRef<ComponentRef>(null);
const { isPc } = useSystem();
const initSign = useRef(false);
const [isEmbed, setIdEmbed] = useState(true);
@@ -82,17 +87,27 @@ const OutLink = ({ appName, appIntro, appAvatar }: Props) => {
onChangeChatId
} = useContextSelector(ChatContext, (v) => v);
const {
ChatBoxRef,
chatRecords,
setChatRecords,
variablesForm,
pluginRunTab,
setPluginRunTab,
resetChatRecords
} = useChat();
const startChat = useCallback(
async ({ messages, controller, generatingMessage, variables }: StartChatFnProps) => {
const prompts = messages.slice(-2);
const completionChatId = chatId ? chatId : nanoid();
const completionChatId = chatId || getNanoid();
const histories = messages.slice(-1);
//post message to report chat start
window.top?.postMessage(
{
type: 'shareChatStart',
data: {
question: prompts[0]?.content
question: histories[0]?.content
}
},
'*'
@@ -100,41 +115,32 @@ const OutLink = ({ appName, appIntro, appAvatar }: Props) => {
const { responseText, responseData } = await streamFetch({
data: {
messages: prompts,
messages: histories,
variables: {
...variables,
...customVariables
},
shareId,
chatId: completionChatId,
appType: chatData.app.type,
outLinkUid
},
onMessage: generatingMessage,
abortCtrl: controller
});
const newTitle = getChatTitleFromChatMessage(GPTMessages2Chats(prompts)[0]);
const newTitle = getChatTitleFromChatMessage(GPTMessages2Chats(histories)[0]);
// new chat
if (completionChatId !== chatId) {
onChangeChatId(completionChatId, true);
loadHistories();
} else {
// update chat
onUpdateHistory({
appId,
chatId: completionChatId,
title: newTitle,
shareId,
outLinkUid
});
}
loadHistories();
// update chat window
setChatData((state) => ({
...state,
title: newTitle,
history: ChatBoxRef.current?.getChatHistories() || state.history
title: newTitle
}));
// hook message
@@ -142,7 +148,7 @@ const OutLink = ({ appName, appIntro, appAvatar }: Props) => {
{
type: 'shareChatFinish',
data: {
question: prompts[0]?.content,
question: histories[0]?.content,
answer: responseText
}
},
@@ -155,12 +161,11 @@ const OutLink = ({ appName, appIntro, appAvatar }: Props) => {
chatId,
customVariables,
shareId,
chatData.app.type,
outLinkUid,
forbidLoadChat,
onChangeChatId,
loadHistories,
onUpdateHistory,
appId
loadHistories
]
);
@@ -173,26 +178,18 @@ const OutLink = ({ appName, appIntro, appAvatar }: Props) => {
shareId,
outLinkUid
});
setChatData(res);
const history = res.history.map((item) => ({
...item,
dataId: item.dataId || nanoid(),
status: ChatStatusEnum.finish
}));
const result: InitChatResponse = {
...res,
history
};
// reset chat box
ChatBoxRef.current?.resetHistory(history);
ChatBoxRef.current?.resetVariables(res.variables);
if (history.length > 0) {
setTimeout(() => {
ChatBoxRef.current?.scrollToBottom('auto');
}, 500);
}
setChatData(result);
resetChatRecords({
records: history,
variables: res.variables
});
},
{
manual: false,
@@ -233,7 +230,7 @@ const OutLink = ({ appName, appIntro, appAvatar }: Props) => {
? { p: '0 !important', insertProps: { borderRadius: '0', boxShadow: 'none' } }
: { p: [0, 5] })}
>
<Flex h={'100%'} flexDirection={['column', 'row']} bg={'white'}>
<Flex h={'100%'} flexDirection={['column', 'row']}>
{showHistory === '1' &&
((children: React.ReactNode) => {
return isPc ? (
@@ -294,37 +291,52 @@ const OutLink = ({ appName, appIntro, appAvatar }: Props) => {
{/* header */}
{showHead === '1' ? (
<ChatHeader
appAvatar={chatData.app.avatar}
appName={chatData.app.name}
chatData={chatData}
history={chatData.history}
showHistory={showHistory === '1'}
/>
) : null}
{/* chat box */}
<Box flex={1}>
<ChatBox
ref={ChatBoxRef}
appAvatar={chatData.app.avatar}
userAvatar={chatData.userAvatar}
chatConfig={chatData.app?.chatConfig}
showFileSelector={checkChatSupportSelectFileByChatModels(chatData.app.chatModels)}
feedbackType={'user'}
onUpdateVariable={(e) => {}}
onStartChat={startChat}
onDelMessage={({ contentId }) =>
delChatRecordById({
contentId,
appId: chatData.appId,
chatId,
shareId,
outLinkUid
})
}
appId={chatData.appId}
chatId={chatId}
shareId={shareId}
outLinkUid={outLinkUid}
/>
<Box flex={1} bg={'white'}>
{chatData.app.type === AppTypeEnum.plugin ? (
<CustomPluginRunBox
pluginInputs={chatData.app.pluginInputs}
variablesForm={variablesForm}
histories={chatRecords}
setHistories={setChatRecords}
appId={chatData.appId}
tab={pluginRunTab}
setTab={setPluginRunTab}
onNewChat={() => onChangeChatId(getNanoid())}
onStartChat={startChat}
/>
) : (
<ChatBox
ref={ChatBoxRef}
chatHistories={chatRecords}
setChatHistories={setChatRecords}
variablesForm={variablesForm}
appAvatar={chatData.app.avatar}
userAvatar={chatData.userAvatar}
chatConfig={chatData.app?.chatConfig}
showFileSelector={checkChatSupportSelectFileByChatModels(chatData.app.chatModels)}
feedbackType={'user'}
onStartChat={startChat}
onDelMessage={({ contentId }) =>
delChatRecordById({
contentId,
appId: chatData.appId,
chatId,
shareId,
outLinkUid
})
}
appId={chatData.appId}
chatId={chatId}
shareId={shareId}
outLinkUid={outLinkUid}
/>
)}
</Box>
</Flex>
</Flex>

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useRef, useState } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import NextHead from '@/components/common/NextHead';
import { delChatRecordById, getChatHistories, getTeamChatInfo } from '@/web/core/chat/api';
import { useRouter } from 'next/router';
@@ -15,8 +15,8 @@ import { useTranslation } from 'next-i18next';
import { checkChatSupportSelectFileByChatModels } from '@/web/core/chat/utils';
import { customAlphabet } from 'nanoid';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
import ChatBox from '@/components/ChatBox';
import type { ComponentRef, StartChatFnProps } from '@/components/ChatBox/type.d';
import ChatBox from '@/components/core/chat/ChatContainer/ChatBox';
import type { StartChatFnProps } from '@/components/core/chat/ChatContainer/type';
import { streamFetch } from '@/web/common/api/fetch';
import { getChatTitleFromChatMessage } from '@fastgpt/global/core/chat/utils';
import { ChatStatusEnum } from '@fastgpt/global/core/chat/constants';
@@ -29,6 +29,13 @@ import { AppListItemType } from '@fastgpt/global/core/app/type';
import { useContextSelector } from 'use-context-selector';
import { InitChatResponse } from '@/global/core/chat/api';
import { defaultChatData } 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';
const CustomPluginRunBox = dynamic(() => import('./components/CustomPluginRunBox'));
type Props = { appId: string; chatId: string; teamId: string; teamToken: string };
@@ -47,8 +54,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
const { toast } = useToast();
const theme = useTheme();
const { isPc } = useSystemStore();
const ChatBoxRef = useRef<ComponentRef>(null);
const { isPc } = useSystem();
const [chatData, setChatData] = useState<InitChatResponse>(defaultChatData);
@@ -60,18 +66,28 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
isOpenSlider,
onCloseSlider,
forbidLoadChat,
onChangeChatId,
onChangeAppId
onChangeChatId
} = useContextSelector(ChatContext, (v) => v);
const {
ChatBoxRef,
chatRecords,
setChatRecords,
variablesForm,
pluginRunTab,
setPluginRunTab,
resetChatRecords
} = useChat();
const startChat = useCallback(
async ({ messages, controller, generatingMessage, variables }: StartChatFnProps) => {
const prompts = messages.slice(-2);
const completionChatId = chatId ? chatId : nanoid();
const completionChatId = chatId || getNanoid();
// Just send a user prompt
const histories = messages.slice(-1);
const { responseText, responseData } = await streamFetch({
data: {
messages: prompts,
messages: histories,
variables: {
...variables,
...customVariables
@@ -79,37 +95,31 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
appId,
teamId,
teamToken,
chatId: completionChatId
chatId: completionChatId,
appType: chatData.app.type
},
onMessage: generatingMessage,
abortCtrl: controller
});
const newTitle = getChatTitleFromChatMessage(GPTMessages2Chats(prompts)[0]);
const newTitle = getChatTitleFromChatMessage(GPTMessages2Chats(histories)[0]);
// new chat
if (completionChatId !== chatId) {
onChangeChatId(completionChatId, true);
loadHistories();
} else {
onUpdateHistory({
appId: chatData.appId,
chatId: completionChatId,
title: newTitle,
teamId,
teamToken
});
}
loadHistories();
// update chat window
setChatData((state) => ({
...state,
title: newTitle,
history: ChatBoxRef.current?.getChatHistories() || state.history
title: newTitle
}));
return { responseText, responseData, isNewChat: forbidLoadChat.current };
},
[
chatData.app.type,
chatId,
customVariables,
appId,
@@ -117,9 +127,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
teamToken,
forbidLoadChat,
onChangeChatId,
loadHistories,
onUpdateHistory,
chatData.appId
loadHistories
]
);
@@ -129,27 +137,19 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
if (!appId || forbidLoadChat.current) return;
const res = await getTeamChatInfo({ teamId, appId, chatId, teamToken });
setChatData(res);
const history = res.history.map((item) => ({
...item,
dataId: item.dataId || nanoid(),
status: ChatStatusEnum.finish
}));
const result: InitChatResponse = {
...res,
history
};
// have records.
ChatBoxRef.current?.resetHistory(history);
ChatBoxRef.current?.resetVariables(res.variables);
if (res.history.length > 0) {
setTimeout(() => {
ChatBoxRef.current?.scrollToBottom('auto');
}, 500);
}
setChatData(result);
// reset chat records
resetChatRecords({
records: history,
variables: res.variables
});
},
{
manual: false,
@@ -230,31 +230,48 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
flexDirection={'column'}
>
{/* header */}
<ChatHeader
appAvatar={chatData.app.avatar}
appName={chatData.app.name}
history={chatData.history}
showHistory
/>
<ChatHeader chatData={chatData} history={chatData.history} showHistory />
{/* chat box */}
<Box flex={1}>
<ChatBox
ref={ChatBoxRef}
appAvatar={chatData.app.avatar}
userAvatar={chatData.userAvatar}
chatConfig={chatData.app?.chatConfig}
showFileSelector={checkChatSupportSelectFileByChatModels(chatData.app.chatModels)}
feedbackType={'user'}
onUpdateVariable={(e) => {}}
onStartChat={startChat}
onDelMessage={({ contentId }) =>
delChatRecordById({ contentId, appId: chatData.appId, chatId, teamId, teamToken })
}
appId={chatData.appId}
chatId={chatId}
teamId={teamId}
teamToken={teamToken}
/>
{chatData.app.type === AppTypeEnum.plugin ? (
<CustomPluginRunBox
pluginInputs={chatData.app.pluginInputs}
variablesForm={variablesForm}
histories={chatRecords}
setHistories={setChatRecords}
appId={chatData.appId}
tab={pluginRunTab}
setTab={setPluginRunTab}
onNewChat={() => onChangeChatId(getNanoid())}
onStartChat={startChat}
/>
) : (
<ChatBox
ref={ChatBoxRef}
chatHistories={chatRecords}
setChatHistories={setChatRecords}
variablesForm={variablesForm}
appAvatar={chatData.app.avatar}
userAvatar={chatData.userAvatar}
chatConfig={chatData.app?.chatConfig}
showFileSelector={checkChatSupportSelectFileByChatModels(chatData.app.chatModels)}
feedbackType={'user'}
onStartChat={startChat}
onDelMessage={({ contentId }) =>
delChatRecordById({
contentId,
appId: chatData.appId,
chatId,
teamId,
teamToken
})
}
appId={chatData.appId}
chatId={chatId}
teamId={teamId}
teamToken={teamToken}
/>
)}
</Box>
</Flex>
</Flex>

View File

@@ -31,6 +31,7 @@ import { ImportDataSourceEnum } from '@fastgpt/global/core/dataset/constants';
import { useContextSelector } from 'use-context-selector';
import { CollectionPageContext } from './Context';
import { DatasetPageContext } from '@/web/core/dataset/context/datasetPageContext';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
const FileSourceSelector = dynamic(() => import('../Import/components/FileSourceSelector'));
@@ -42,7 +43,7 @@ const Header = ({}: {}) => {
const router = useRouter();
const { parentId = '' } = router.query as { parentId: string };
const { isPc } = useSystemStore();
const { isPc } = useSystem();
const lastSearch = useRef('');
const { searchText, setSearchText, total, getData, pageNum, onOpenWebsiteModal } =

View File

@@ -53,13 +53,14 @@ import { useContextSelector } from 'use-context-selector';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import MyTag from '@fastgpt/web/components/common/Tag/index';
import MyBox from '@fastgpt/web/components/common/MyBox';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
const DataCard = () => {
const BoxRef = useRef<HTMLDivElement>(null);
const theme = useTheme();
const lastSearch = useRef('');
const router = useRouter();
const { isPc } = useSystemStore();
const { isPc } = useSystem();
const { collectionId = '', datasetId } = router.query as {
collectionId: string;
datasetId: string;

View File

@@ -81,7 +81,7 @@ function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean
<Box fontSize={'md'}>{t('core.dataset.import.Data process params')}</Box>
</Flex>
<Flex mt={4} alignItems={'center'}>
<Box display={['block', 'flex']} mt={4} alignItems={'center'}>
<FormLabel flex={'0 0 100px'}>{t('core.dataset.import.Training mode')}</FormLabel>
<LeftRadio
list={trainingModeList.map(([key, value]) => ({
@@ -98,8 +98,8 @@ function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean
display={'flex'}
flexWrap={'wrap'}
/>
</Flex>
<Flex mt={5}>
</Box>
<Box display={['block', 'flex']} mt={5}>
<FormLabel flex={'0 0 100px'}>{t('core.dataset.import.Process way')}</FormLabel>
<LeftRadio
list={[
@@ -118,10 +118,7 @@ function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean
<Box>
<Flex alignItems={'center'}>
<Box>{t('core.dataset.import.Ideal chunk length')}</Box>
<MyTooltip
label={t('core.dataset.import.Ideal chunk length Tips')}
forceShow
>
<MyTooltip label={t('core.dataset.import.Ideal chunk length Tips')}>
<MyIcon
name={'common/questionLight'}
ml={1}
@@ -175,10 +172,7 @@ function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean
<Box mt={3}>
<Box>
{t('core.dataset.import.Custom split char')}
<MyTooltip
label={t('core.dataset.import.Custom split char Tips')}
forceShow
>
<MyTooltip label={t('core.dataset.import.Custom split char Tips')}>
<MyIcon
name={'common/questionLight'}
ml={1}
@@ -263,16 +257,16 @@ function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean
setValue('way', e);
}}
></LeftRadio>
</Flex>
<Flex mt={5} alignItems={'center'} pl={'100px'} gap={3}>
</Box>
<Box mt={5} pl={[0, '100px']} gap={3}>
{feConfigs?.show_pay && (
<MyTooltip label={priceTip}>
<MyTag colorSchema={'gray'} py={'6px'} borderRadius={'md'} px={3}>
<MyTag colorSchema={'gray'} py={'6px'} borderRadius={'md'} px={3} whiteSpace={'wrap'}>
{priceTip}
</MyTag>
</MyTooltip>
)}
</Flex>
</Box>
<Flex mt={5} gap={3} justifyContent={'flex-end'}>
<Button
onClick={() => {
@@ -283,7 +277,7 @@ function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean
</Button>
</Flex>
</Box>
<Box flex={'1 0 0'} w={'0'}>
<Box flex={'1 0 0'} w={['auto', '0']}>
<Preview showPreviewChunks={showPreviewChunks} />
</Box>

View File

@@ -12,6 +12,7 @@ import { useContextSelector } from 'use-context-selector';
import { DatasetPageContext } from '@/web/core/dataset/context/datasetPageContext';
import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
import { useI18n } from '@/web/context/I18n';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
export enum TabEnum {
dataCard = 'dataCard',
@@ -27,7 +28,7 @@ const Slider = ({ currentTab }: { currentTab: TabEnum }) => {
const { datasetT } = useI18n();
const router = useRouter();
const query = router.query;
const { isPc } = useSystemStore();
const { isPc } = useSystem();
const { datasetDetail, vectorTrainingMap, agentTrainingMap, rebuildingCount } =
useContextSelector(DatasetPageContext, (v) => v);

View File

@@ -21,13 +21,15 @@ import AIModelSelector from '@/components/Select/AIModelSelector';
import { useI18n } from '@/web/context/I18n';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import { DatasetDefaultPermissionVal } from '@fastgpt/global/support/permission/dataset/constant';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
const CreateModal = ({ onClose, parentId }: { onClose: () => void; parentId?: string }) => {
const { t } = useTranslation();
const { datasetT } = useI18n();
const { toast } = useToast();
const router = useRouter();
const { isPc, feConfigs, vectorModelList, datasetModelList } = useSystemStore();
const { feConfigs, vectorModelList, datasetModelList } = useSystemStore();
const { isPc } = useSystem();
const filterNotHiddenVectorModelList = vectorModelList.filter((item) => !item.hidden);

View File

@@ -30,6 +30,7 @@ import {
deleteDatasetCollaborators,
getCollaboratorList
} from '@/web/core/dataset/api/collaborator';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
const EditFolderModal = dynamic(
() => import('@fastgpt/web/components/common/MyModal/EditFolderModal')
@@ -38,7 +39,7 @@ const EditFolderModal = dynamic(
const CreateModal = dynamic(() => import('./component/CreateModal'));
const Dataset = () => {
const { isPc } = useSystemStore();
const { isPc } = useSystem();
const { t } = useTranslation();
const router = useRouter();
const { parentId } = router.query as { parentId: string };