mirror of
https://github.com/labring/FastGPT.git
synced 2025-10-13 14:29:40 +00:00
perf: sub agent output
This commit is contained in:
@@ -9,7 +9,7 @@ import type {
|
||||
RuntimeNodeItemType
|
||||
} from '@fastgpt/global/core/workflow/runtime/type';
|
||||
import { getLLMModel } from '../../../../ai/model';
|
||||
import { getNodeErrResponse, getHistories } from '../../utils';
|
||||
import { getNodeErrResponse, getHistories, getWorkflowChildResponseWrite } from '../../utils';
|
||||
import { runAgentCall } from '../../../../ai/llm/agentCall';
|
||||
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type';
|
||||
import { type ChatItemType } from '@fastgpt/global/core/chat/type';
|
||||
@@ -48,7 +48,7 @@ import { getMasterAgentDefaultPrompt } from './constants';
|
||||
import { addFilePrompt2Input, getFileInputPrompt } from './sub/file/utils';
|
||||
import type { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type';
|
||||
import { dispatchFileRead } from './sub/file';
|
||||
import { dispatchApp } from './sub/app';
|
||||
import { dispatchApp, dispatchPlugin } from './sub/app';
|
||||
|
||||
export type DispatchAgentModuleProps = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.history]?: ChatItemType[];
|
||||
@@ -252,6 +252,10 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
|
||||
|
||||
handleToolResponse: async ({ call, messages }) => {
|
||||
const toolId = call.function.name;
|
||||
const childWorkflowStreamResponse = getWorkflowChildResponseWrite({
|
||||
id: call.id,
|
||||
fn: workflowStreamResponse
|
||||
});
|
||||
|
||||
const {
|
||||
response,
|
||||
@@ -276,8 +280,7 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
|
||||
stream,
|
||||
onStreaming({ text }) {
|
||||
//TODO: 需要一个新的 plan sse event
|
||||
workflowStreamResponse?.({
|
||||
id: call.id,
|
||||
childWorkflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.toolResponse,
|
||||
data: {
|
||||
tool: {
|
||||
@@ -312,8 +315,7 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
|
||||
systemPrompt,
|
||||
task,
|
||||
onStreaming({ text }) {
|
||||
workflowStreamResponse?.({
|
||||
id: call.id,
|
||||
childWorkflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.toolResponse,
|
||||
data: {
|
||||
tool: {
|
||||
@@ -419,18 +421,32 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
|
||||
runningUserInfo,
|
||||
runningAppInfo,
|
||||
variables,
|
||||
workflowStreamResponse
|
||||
workflowStreamResponse: childWorkflowStreamResponse
|
||||
});
|
||||
return {
|
||||
response,
|
||||
usages,
|
||||
isEnd: false
|
||||
};
|
||||
} else if (node.flowNodeType === FlowNodeTypeEnum.appModule) {
|
||||
const { response, usages } = await dispatchApp({
|
||||
} else if (
|
||||
node.flowNodeType === FlowNodeTypeEnum.appModule ||
|
||||
node.flowNodeType === FlowNodeTypeEnum.pluginModule
|
||||
) {
|
||||
const fn =
|
||||
node.flowNodeType === FlowNodeTypeEnum.appModule ? dispatchApp : dispatchPlugin;
|
||||
console.log(requestParams, 22);
|
||||
const { response, usages } = await fn({
|
||||
...props,
|
||||
node
|
||||
node,
|
||||
// stream: false,
|
||||
workflowStreamResponse: undefined,
|
||||
callParams: {
|
||||
appId: node.pluginId,
|
||||
version: node.version,
|
||||
...requestParams
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
response,
|
||||
usages,
|
||||
@@ -455,8 +471,7 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
|
||||
|
||||
// Push stream response
|
||||
if (streamResponse) {
|
||||
workflowStreamResponse?.({
|
||||
id: call.id,
|
||||
childWorkflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.toolResponse,
|
||||
data: {
|
||||
tool: {
|
||||
|
@@ -1,17 +1,11 @@
|
||||
import type { DispatchSubAppResponse } from '../../type';
|
||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
||||
import type { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import type { ChatItemType } from '@fastgpt/global/core/chat/type';
|
||||
import {
|
||||
filterSystemVariables,
|
||||
getHistories
|
||||
} from '../../../../../../../core/workflow/dispatch/utils';
|
||||
import { filterSystemVariables } from '../../../../../../../core/workflow/dispatch/utils';
|
||||
import { authAppByTmbId } from '../../../../../../../support/permission/app/auth';
|
||||
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { getAppVersionById } from '../../../../../../../core/app/version/controller';
|
||||
import { getRunningUserInfoByTmbId } from '../../../../../../../support/user/team/utils';
|
||||
import { runWorkflow } from '../../../../../../../core/workflow/dispatch';
|
||||
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import {
|
||||
getWorkflowEntryNodeIds,
|
||||
rewriteNodeOutputByHistories,
|
||||
@@ -19,145 +13,181 @@ import {
|
||||
storeNodes2RuntimeNodes,
|
||||
textAdaptGptResponse
|
||||
} from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import { chatValue2RuntimePrompt, runtimePrompt2ChatsValue } from '@fastgpt/global/core/chat/adapt';
|
||||
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
|
||||
import { getUserChatInfoAndAuthTeamPoints } from '../../../../../../../support/permission/auth/team';
|
||||
import { parseUrlToFileType } from '@fastgpt/global/common/file/tools';
|
||||
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { getChildAppRuntimeById } from '../../../../../../app/plugin/controller';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { getPluginRunUserQuery } from '@fastgpt/global/core/workflow/utils';
|
||||
import { getPluginInputsFromStoreNodes } from '@fastgpt/global/core/app/plugin/utils';
|
||||
|
||||
export type appConfigType = {
|
||||
variables: Record<string, any>;
|
||||
type Props = ModuleDispatchProps<{}> & {
|
||||
callParams: {
|
||||
appId?: string;
|
||||
version?: string;
|
||||
[key: string]: any;
|
||||
};
|
||||
};
|
||||
|
||||
type Props = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.userChatInput]: string;
|
||||
[NodeInputKeyEnum.history]?: ChatItemType[] | number;
|
||||
[NodeInputKeyEnum.fileUrlList]?: string[];
|
||||
[NodeInputKeyEnum.forbidStream]?: boolean;
|
||||
[NodeInputKeyEnum.fileUrlList]?: string[];
|
||||
}>;
|
||||
|
||||
export const dispatchApp = async (props: Props): Promise<DispatchSubAppResponse> => {
|
||||
const {
|
||||
runningAppInfo,
|
||||
histories,
|
||||
query,
|
||||
lastInteractive,
|
||||
node: { pluginId: appId, version },
|
||||
workflowStreamResponse,
|
||||
params,
|
||||
variables,
|
||||
stream
|
||||
} = props;
|
||||
const {
|
||||
system_forbid_stream = false,
|
||||
userChatInput,
|
||||
history,
|
||||
fileUrlList,
|
||||
...childrenAppVariables
|
||||
} = params;
|
||||
|
||||
const { files } = chatValue2RuntimePrompt(query);
|
||||
|
||||
const userInputFiles = (() => {
|
||||
if (fileUrlList) {
|
||||
return fileUrlList.map((url) => parseUrlToFileType(url)).filter(Boolean);
|
||||
}
|
||||
// Adapt version 4.8.13 upgrade
|
||||
return files;
|
||||
})();
|
||||
|
||||
if (!userChatInput && !userInputFiles) {
|
||||
return Promise.reject(new Error('Input is empty'));
|
||||
}
|
||||
if (!appId) {
|
||||
return Promise.reject(new Error('pluginId is empty'));
|
||||
}
|
||||
|
||||
try {
|
||||
// Auth the app by tmbId(Not the user, but the workflow user)
|
||||
const { app: appData } = await authAppByTmbId({
|
||||
appId: appId,
|
||||
tmbId: runningAppInfo.tmbId,
|
||||
per: ReadPermissionVal
|
||||
});
|
||||
const { nodes, edges, chatConfig } = await getAppVersionById({
|
||||
callParams: {
|
||||
appId,
|
||||
versionId: version,
|
||||
app: appData
|
||||
});
|
||||
|
||||
const childStreamResponse = system_forbid_stream ? false : stream;
|
||||
// Auto line
|
||||
if (childStreamResponse) {
|
||||
workflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.answer,
|
||||
data: textAdaptGptResponse({
|
||||
text: '\n'
|
||||
})
|
||||
});
|
||||
version,
|
||||
userChatInput,
|
||||
system_forbid_stream,
|
||||
history,
|
||||
fileUrlList,
|
||||
...data
|
||||
}
|
||||
} = props;
|
||||
|
||||
const chatHistories = getHistories(history, histories);
|
||||
if (!appId) {
|
||||
return Promise.reject(new Error('AppId is empty'));
|
||||
}
|
||||
|
||||
// Rewrite children app variables
|
||||
const systemVariables = filterSystemVariables(variables);
|
||||
const { externalProvider } = await getUserChatInfoAndAuthTeamPoints(appData.tmbId);
|
||||
const childrenRunVariables = {
|
||||
...systemVariables,
|
||||
...childrenAppVariables,
|
||||
histories: chatHistories,
|
||||
appId: String(appData._id),
|
||||
...(externalProvider ? externalProvider.externalWorkflowVariables : {})
|
||||
// Auth the app by tmbId(Not the user, but the workflow user)
|
||||
const { app: appData } = await authAppByTmbId({
|
||||
appId,
|
||||
tmbId: runningAppInfo.tmbId,
|
||||
per: ReadPermissionVal
|
||||
});
|
||||
const { nodes, edges, chatConfig } = await getAppVersionById({
|
||||
appId,
|
||||
versionId: version,
|
||||
app: appData
|
||||
});
|
||||
|
||||
// Rewrite children app variables
|
||||
const systemVariables = filterSystemVariables(variables);
|
||||
const { externalProvider } = await getUserChatInfoAndAuthTeamPoints(appData.tmbId);
|
||||
const childrenRunVariables = {
|
||||
...systemVariables,
|
||||
histories: [],
|
||||
appId: String(appData._id),
|
||||
...data,
|
||||
...(externalProvider ? externalProvider.externalWorkflowVariables : {})
|
||||
};
|
||||
|
||||
const runtimeNodes = rewriteNodeOutputByHistories(
|
||||
storeNodes2RuntimeNodes(nodes, getWorkflowEntryNodeIds(nodes))
|
||||
);
|
||||
const runtimeEdges = storeEdges2RuntimeEdges(edges);
|
||||
|
||||
const { assistantResponses, flowUsages } = await runWorkflow({
|
||||
...props,
|
||||
runningAppInfo: {
|
||||
id: String(appData._id),
|
||||
teamId: String(appData.teamId),
|
||||
tmbId: String(appData.tmbId),
|
||||
isChildApp: true
|
||||
},
|
||||
runningUserInfo: await getRunningUserInfoByTmbId(appData.tmbId),
|
||||
runtimeNodes,
|
||||
runtimeEdges,
|
||||
histories: [],
|
||||
variables: childrenRunVariables,
|
||||
query: [
|
||||
{
|
||||
type: ChatItemValueTypeEnum.text,
|
||||
text: {
|
||||
content: userChatInput
|
||||
}
|
||||
}
|
||||
],
|
||||
chatConfig
|
||||
});
|
||||
|
||||
const { text } = chatValue2RuntimePrompt(assistantResponses);
|
||||
|
||||
return {
|
||||
response: text,
|
||||
usages: flowUsages
|
||||
};
|
||||
};
|
||||
|
||||
export const dispatchPlugin = async (props: Props): Promise<DispatchSubAppResponse> => {
|
||||
const {
|
||||
runningAppInfo,
|
||||
callParams: { appId, version, system_forbid_stream, ...data }
|
||||
} = props;
|
||||
|
||||
if (!appId) {
|
||||
return Promise.reject(new Error('AppId is empty'));
|
||||
}
|
||||
|
||||
// Auth the app by tmbId(Not the user, but the workflow user)
|
||||
const {
|
||||
app: { tmbId }
|
||||
} = await authAppByTmbId({
|
||||
appId,
|
||||
tmbId: runningAppInfo.tmbId,
|
||||
per: ReadPermissionVal
|
||||
});
|
||||
const plugin = await getChildAppRuntimeById({ id: appId, versionId: version });
|
||||
|
||||
const outputFilterMap =
|
||||
plugin.nodes
|
||||
.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginOutput)
|
||||
?.inputs.reduce<Record<string, boolean>>((acc, cur) => {
|
||||
acc[cur.key] = cur.isToolOutput === false ? false : true;
|
||||
return acc;
|
||||
}, {}) ?? {};
|
||||
const runtimeNodes = storeNodes2RuntimeNodes(
|
||||
plugin.nodes,
|
||||
getWorkflowEntryNodeIds(plugin.nodes)
|
||||
).map((node) => {
|
||||
// Update plugin input value
|
||||
if (node.flowNodeType === FlowNodeTypeEnum.pluginInput) {
|
||||
return {
|
||||
...node,
|
||||
showStatus: false,
|
||||
inputs: node.inputs.map((input) => ({
|
||||
...input,
|
||||
value: data[input.key] ?? input.value
|
||||
}))
|
||||
};
|
||||
}
|
||||
return {
|
||||
...node,
|
||||
showStatus: false
|
||||
};
|
||||
});
|
||||
|
||||
const childrenInteractive =
|
||||
lastInteractive?.type === 'childrenInteractive'
|
||||
? lastInteractive.params.childrenResponse
|
||||
: undefined;
|
||||
const runtimeNodes = rewriteNodeOutputByHistories(
|
||||
storeNodes2RuntimeNodes(
|
||||
nodes,
|
||||
getWorkflowEntryNodeIds(nodes, childrenInteractive || undefined)
|
||||
),
|
||||
childrenInteractive
|
||||
);
|
||||
const { externalProvider } = await getUserChatInfoAndAuthTeamPoints(tmbId);
|
||||
const runtimeVariables = {
|
||||
...filterSystemVariables(props.variables),
|
||||
appId: String(plugin.id),
|
||||
...(externalProvider ? externalProvider.externalWorkflowVariables : {})
|
||||
};
|
||||
|
||||
const runtimeEdges = storeEdges2RuntimeEdges(edges, childrenInteractive);
|
||||
const theQuery = childrenInteractive
|
||||
? query
|
||||
: runtimePrompt2ChatsValue({ files: userInputFiles, text: userChatInput });
|
||||
|
||||
const { assistantResponses } = await runWorkflow({
|
||||
const { flowResponses, flowUsages, assistantResponses, runTimes, system_memories } =
|
||||
await runWorkflow({
|
||||
...props,
|
||||
lastInteractive: childrenInteractive,
|
||||
// Rewrite stream mode
|
||||
...(system_forbid_stream
|
||||
? {
|
||||
stream: false,
|
||||
workflowStreamResponse: undefined
|
||||
}
|
||||
: {}),
|
||||
runningAppInfo: {
|
||||
id: String(appData._id),
|
||||
teamId: String(appData.teamId),
|
||||
tmbId: String(appData.tmbId),
|
||||
id: String(plugin.id),
|
||||
// 如果系统插件有 teamId 和 tmbId,则使用系统插件的 teamId 和 tmbId(管理员指定了插件作为系统插件)
|
||||
teamId: plugin.teamId || runningAppInfo.teamId,
|
||||
tmbId: plugin.tmbId || runningAppInfo.tmbId,
|
||||
isChildApp: true
|
||||
},
|
||||
runningUserInfo: await getRunningUserInfoByTmbId(appData.tmbId),
|
||||
variables: runtimeVariables,
|
||||
query: getPluginRunUserQuery({
|
||||
pluginInputs: getPluginInputsFromStoreNodes(plugin.nodes),
|
||||
variables: runtimeVariables
|
||||
}).value,
|
||||
chatConfig: {},
|
||||
runtimeNodes,
|
||||
runtimeEdges,
|
||||
histories: chatHistories,
|
||||
variables: childrenRunVariables,
|
||||
query: theQuery,
|
||||
chatConfig
|
||||
runtimeEdges: storeEdges2RuntimeEdges(plugin.edges)
|
||||
});
|
||||
const output = flowResponses.find((item) => item.moduleType === FlowNodeTypeEnum.pluginOutput);
|
||||
const response = output?.pluginOutput ? JSON.stringify(output?.pluginOutput) : 'No output';
|
||||
|
||||
const { text } = chatValue2RuntimePrompt(assistantResponses);
|
||||
|
||||
return {
|
||||
response: text,
|
||||
usages: []
|
||||
};
|
||||
} catch (error) {
|
||||
return Promise.reject('dispatch app failed');
|
||||
}
|
||||
return {
|
||||
response,
|
||||
usages: flowUsages
|
||||
};
|
||||
};
|
||||
|
@@ -70,6 +70,18 @@ export const getWorkflowResponseWrite = ({
|
||||
};
|
||||
return fn;
|
||||
};
|
||||
export const getWorkflowChildResponseWrite = ({
|
||||
id,
|
||||
fn
|
||||
}: {
|
||||
id: string;
|
||||
fn?: WorkflowResponseType;
|
||||
}): WorkflowResponseType | undefined => {
|
||||
if (!fn) return;
|
||||
return (e: Parameters<WorkflowResponseType>[0]) => {
|
||||
return fn({ ...e, id });
|
||||
};
|
||||
};
|
||||
|
||||
export const filterToolNodeIdByEdges = ({
|
||||
nodeId,
|
||||
|
@@ -318,6 +318,7 @@ const ChatBox = ({
|
||||
}
|
||||
} else if (event === SseResponseEventEnum.toolCall && tool) {
|
||||
const val: AIChatItemValueItemType = {
|
||||
id: responseValueId,
|
||||
type: ChatItemValueTypeEnum.tool,
|
||||
tools: [tool]
|
||||
};
|
||||
|
@@ -20,6 +20,7 @@ import SecretInputModal, {
|
||||
} from '@/pageComponents/app/plugin/SecretInputModal';
|
||||
import { SystemToolInputTypeMap } from '@fastgpt/global/core/app/systemTool/constants';
|
||||
import { useBoolean } from 'ahooks';
|
||||
import { useSafeTranslation } from '@fastgpt/web/hooks/useSafeTranslation';
|
||||
|
||||
const ConfigToolModal = ({
|
||||
configTool,
|
||||
@@ -30,7 +31,7 @@ const ConfigToolModal = ({
|
||||
onCloseConfigTool: () => void;
|
||||
onAddTool: (tool: AppFormEditFormType['selectedTools'][number]) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { t } = useSafeTranslation();
|
||||
const [isOpenSecretModal, { setTrue: setTrueSecretModal, setFalse: setFalseSecretModal }] =
|
||||
useBoolean(false);
|
||||
|
||||
@@ -86,8 +87,8 @@ const ConfigToolModal = ({
|
||||
<Box key={input.key} _notLast={{ mb: 4 }}>
|
||||
<Flex alignItems={'center'} mb={1}>
|
||||
{input.required && <Box color={'red.500'}>*</Box>}
|
||||
<FormLabel>{input.label}</FormLabel>
|
||||
{input.description && <QuestionTip ml={1} label={input.description} />}
|
||||
<FormLabel>{t(input.label)}</FormLabel>
|
||||
{input.description && <QuestionTip ml={1} label={t(input.description)} />}
|
||||
</Flex>
|
||||
|
||||
{input.key === NodeInputKeyEnum.systemInputConfig && input.inputList ? (
|
||||
|
Reference in New Issue
Block a user