mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-30 18:48:55 +00:00
Plugin support select file (#2756)
* feat: plugin support upload files (#2716) * feat: plugin support file upload * file history * fix history & chattest * chore: code * plugin config icon & file preview padding * perf: undefined fn * fix: plugin file numbers & plugin template add config (#2743) * perf: run plugin without human message (#2749) * perf: run plugin without human message * fix build * fix build * rename node * perf: ui * perf: plugin rerun with last params & plugin log add file (#2755) * perf: plugin run with last params & plugin log add file * delete console * perf: plugin refresh code * fix: ts --------- Co-authored-by: heheer <heheer@sealos.io>
This commit is contained in:
@@ -4,7 +4,7 @@ import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/cons
|
||||
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 { UserChatItemValueItemType } from '@fastgpt/global/core/chat/type';
|
||||
import type { 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';
|
||||
@@ -13,7 +13,10 @@ import { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
||||
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 {
|
||||
getPluginRunUserQuery,
|
||||
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';
|
||||
@@ -28,6 +31,7 @@ import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
||||
import { getWorkflowResponseWrite } from '@fastgpt/service/core/workflow/dispatch/utils';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
import { WORKFLOW_MAX_RUN_TIMES } from '@fastgpt/service/core/workflow/constants';
|
||||
import { getPluginInputsFromStoreNodes } from '@fastgpt/global/core/app/plugin/utils';
|
||||
|
||||
export type Props = {
|
||||
messages: ChatCompletionMessageParam[];
|
||||
@@ -65,8 +69,6 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
throw new Error('Edges is not array');
|
||||
}
|
||||
const chatMessages = GPTMessages2Chats(messages);
|
||||
const userInput = chatMessages.pop()?.value as UserChatItemValueItemType[] | undefined;
|
||||
|
||||
// console.log(JSON.stringify(chatMessages, null, 2), '====', chatMessages.length);
|
||||
|
||||
/* user auth */
|
||||
@@ -82,6 +84,22 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
|
||||
const isPlugin = app.type === AppTypeEnum.plugin;
|
||||
|
||||
const userQuestion: UserChatItemType = (() => {
|
||||
if (isPlugin) {
|
||||
return getPluginRunUserQuery({
|
||||
pluginInputs: getPluginInputsFromStoreNodes(app.modules),
|
||||
variables,
|
||||
files: variables.files
|
||||
});
|
||||
}
|
||||
|
||||
const latestHumanChat = chatMessages.pop() as UserChatItemType | undefined;
|
||||
if (!latestHumanChat) {
|
||||
throw new Error('User question is empty');
|
||||
}
|
||||
return latestHumanChat;
|
||||
})();
|
||||
|
||||
let runtimeNodes = storeNodes2RuntimeNodes(nodes, getWorkflowEntryNodeIds(nodes, chatMessages));
|
||||
|
||||
// Plugin need to replace inputs
|
||||
@@ -89,7 +107,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
runtimeNodes = updatePluginInputByVariables(runtimeNodes, variables);
|
||||
variables = {};
|
||||
} else {
|
||||
if (!userInput) {
|
||||
if (!userQuestion.value) {
|
||||
throw new Error('Params Error');
|
||||
}
|
||||
}
|
||||
@@ -117,7 +135,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
runtimeNodes,
|
||||
runtimeEdges: initWorkflowEdgeStatus(edges, chatMessages),
|
||||
variables,
|
||||
query: removeEmptyUserInput(userInput),
|
||||
query: removeEmptyUserInput(userQuestion.value),
|
||||
chatConfig,
|
||||
histories: chatMessages,
|
||||
stream: true,
|
||||
|
@@ -49,13 +49,16 @@ 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 {
|
||||
getPluginRunUserQuery,
|
||||
updatePluginInputByVariables
|
||||
} from '@fastgpt/global/core/workflow/utils';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
import { getSystemTime } from '@fastgpt/global/common/time/timezone';
|
||||
import { rewriteNodeOutputByHistories } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import { getWorkflowResponseWrite } from '@fastgpt/service/core/workflow/dispatch/utils';
|
||||
import { getPluginRunUserQuery } from '@fastgpt/service/core/workflow/utils';
|
||||
import { WORKFLOW_MAX_RUN_TIMES } from '@fastgpt/service/core/workflow/constants';
|
||||
import { getPluginInputsFromStoreNodes } from '@fastgpt/global/core/app/plugin/utils';
|
||||
|
||||
type FastGptWebChatProps = {
|
||||
chatId?: string; // undefined: get histories from messages, '': new chat, 'xxxxx': get histories from db
|
||||
@@ -185,8 +188,11 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
// Get obj=Human history
|
||||
const userQuestion: UserChatItemType = (() => {
|
||||
if (isPlugin) {
|
||||
// TODO:get plugin files from variables
|
||||
return getPluginRunUserQuery({ nodes: app.modules, variables });
|
||||
return getPluginRunUserQuery({
|
||||
pluginInputs: getPluginInputsFromStoreNodes(app.modules),
|
||||
variables,
|
||||
files: variables.files
|
||||
});
|
||||
}
|
||||
|
||||
const latestHumanChat = chatMessages.pop() as UserChatItemType | undefined;
|
||||
|
@@ -145,6 +145,7 @@ const DetailLogsModal = ({
|
||||
{isPlugin ? (
|
||||
<Box px={5} pt={2} h={'100%'}>
|
||||
<PluginRunBox
|
||||
chatConfig={chat?.app?.chatConfig}
|
||||
pluginInputs={chat?.app.pluginInputs}
|
||||
variablesForm={variablesForm}
|
||||
histories={chatRecords}
|
||||
|
@@ -408,10 +408,11 @@ export const useWorkflow = () => {
|
||||
/* node */
|
||||
const handleRemoveNode = useMemoizedFn((change: NodeRemoveChange, node: Node) => {
|
||||
if (node.data.forbidDelete) {
|
||||
return toast({
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: t('common:core.workflow.Can not delete node')
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the node has child nodes, remove the child nodes
|
||||
@@ -438,6 +439,8 @@ export const useWorkflow = () => {
|
||||
setEdges((state) =>
|
||||
state.filter((edge) => edge.source !== change.id && edge.target !== change.id)
|
||||
);
|
||||
|
||||
return true;
|
||||
});
|
||||
const handleSelectNode = useMemoizedFn((change: NodeSelectionChange) => {
|
||||
// If the node is not selected and the Ctrl key is pressed, select the node
|
||||
@@ -506,8 +509,9 @@ export const useWorkflow = () => {
|
||||
for (const change of changes) {
|
||||
if (change.type === 'remove') {
|
||||
const node = nodes.find((n) => n.id === change.id);
|
||||
if (node) {
|
||||
handleRemoveNode(change, node);
|
||||
// 如果删除失败,则不继续执行
|
||||
if (node && !handleRemoveNode(change, node)) {
|
||||
return;
|
||||
}
|
||||
} else if (change.type === 'select') {
|
||||
handleSelectNode(change);
|
||||
|
@@ -13,6 +13,11 @@ import { getAppChatConfig } from '@fastgpt/global/core/workflow/utils';
|
||||
import { useCreation } from 'ahooks';
|
||||
import ChatFunctionTip from '@/components/core/app/Tip';
|
||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||
import { WorkflowContext } from '../../../context';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import FileSelect from '@/components/core/app/FileSelect';
|
||||
import { userFilesInput } from '@fastgpt/global/core/workflow/template/system/workflowStart';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
|
||||
type ComponentProps = {
|
||||
chatConfig: AppChatConfigType;
|
||||
@@ -60,6 +65,9 @@ const NodePluginConfig = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
>
|
||||
<Container w={'360px'}>
|
||||
<Instruction {...componentsProps} />
|
||||
<Box pt={4}>
|
||||
<FileSelectConfig {...componentsProps} />
|
||||
</Box>
|
||||
</Container>
|
||||
</NodeCard>
|
||||
);
|
||||
@@ -72,6 +80,7 @@ function Instruction({ chatConfig: { instruction }, setAppDetail }: ComponentPro
|
||||
return (
|
||||
<>
|
||||
<Flex>
|
||||
<MyIcon name={'core/app/simpleMode/chat'} mr={2} w={'20px'} />
|
||||
<FormLabel color={'myGray.600'} fontWeight={'medium'} fontSize={'14px'}>
|
||||
{t('workflow:plugin.Instructions')}
|
||||
</FormLabel>
|
||||
@@ -100,3 +109,48 @@ function Instruction({ chatConfig: { instruction }, setAppDetail }: ComponentPro
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function FileSelectConfig({ chatConfig: { fileSelectConfig }, setAppDetail }: ComponentProps) {
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
const nodes = useContextSelector(WorkflowContext, (v) => v.nodes);
|
||||
const pluginInputNode = nodes.find((item) => item.type === FlowNodeTypeEnum.pluginInput)!;
|
||||
|
||||
return (
|
||||
<FileSelect
|
||||
value={fileSelectConfig}
|
||||
color={'myGray.600'}
|
||||
fontWeight={'medium'}
|
||||
fontSize={'14px'}
|
||||
onChange={(e) => {
|
||||
setAppDetail((state) => ({
|
||||
...state,
|
||||
chatConfig: {
|
||||
...state.chatConfig,
|
||||
fileSelectConfig: e
|
||||
}
|
||||
}));
|
||||
|
||||
// Dynamic add or delete userFilesInput
|
||||
const canUploadFiles = e.canSelectFile || e.canSelectImg;
|
||||
const repeatKey = pluginInputNode?.data.outputs.find(
|
||||
(item) => item.key === userFilesInput.key
|
||||
);
|
||||
if (canUploadFiles) {
|
||||
!repeatKey &&
|
||||
onChangeNode({
|
||||
nodeId: pluginInputNode.id,
|
||||
type: 'addOutput',
|
||||
value: userFilesInput
|
||||
});
|
||||
} else {
|
||||
repeatKey &&
|
||||
onChangeNode({
|
||||
nodeId: pluginInputNode.id,
|
||||
type: 'delOutput',
|
||||
key: userFilesInput.key
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@@ -22,6 +22,7 @@ import { WorkflowContext } from '../../../context';
|
||||
import IOTitle from '../../components/IOTitle';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { defaultInput } from './InputEditModal';
|
||||
import RenderOutput from '../render/RenderOutput';
|
||||
|
||||
const FieldEditModal = dynamic(() => import('./InputEditModal'));
|
||||
|
||||
@@ -140,9 +141,15 @@ const NodePluginInput = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
}}
|
||||
/>
|
||||
</Container>
|
||||
{!!outputs.length && (
|
||||
<Container>
|
||||
<IOTitle text={t('common:common.Output')} />
|
||||
<RenderOutput nodeId={nodeId} flowOutputList={outputs} />
|
||||
</Container>
|
||||
)}
|
||||
</NodeCard>
|
||||
);
|
||||
}, [data, inputs, nodeId, onChangeNode, selected, t]);
|
||||
}, [data, inputs, nodeId, onChangeNode, outputs, selected, t]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@@ -72,7 +72,7 @@ export const useChatTest = ({
|
||||
|
||||
const CustomChatContainer = useMemoizedFn(() =>
|
||||
appDetail.type === AppTypeEnum.plugin ? (
|
||||
<Box p={3}>
|
||||
<Box p={5}>
|
||||
<PluginRunBox
|
||||
pluginInputs={pluginInputs}
|
||||
variablesForm={variablesForm}
|
||||
|
@@ -17,7 +17,6 @@ import { useTranslation } from 'next-i18next';
|
||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
|
||||
import AIModelSelector from '@/components/Select/AIModelSelector';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||
import ComplianceTip from '@/components/common/ComplianceTip/index';
|
||||
|
Reference in New Issue
Block a user