4.8.10 test (#2601)

* perf: workflow children run params

* feat: workflow userId

* fix: ui size

* perf: Markdown whitespace and ai images split

* fix: openai sdk ts
This commit is contained in:
Archer
2024-09-03 13:43:56 +08:00
committed by GitHub
parent 761e35c226
commit 9a57e94b79
43 changed files with 318 additions and 288 deletions

View File

@@ -151,12 +151,23 @@ export const GPTMessages2Chats = (
obj === ChatRoleEnum.System && obj === ChatRoleEnum.System &&
item.role === ChatCompletionRequestMessageRoleEnum.System item.role === ChatCompletionRequestMessageRoleEnum.System
) { ) {
value.push({ if (Array.isArray(item.content)) {
type: ChatItemValueTypeEnum.text, item.content.forEach((item) => [
text: { value.push({
content: item.content type: ChatItemValueTypeEnum.text,
} text: {
}); content: item.text
}
})
]);
} else {
value.push({
type: ChatItemValueTypeEnum.text,
text: {
content: item.content
}
});
}
} else if ( } else if (
obj === ChatRoleEnum.Human && obj === ChatRoleEnum.Human &&
item.role === ChatCompletionRequestMessageRoleEnum.User item.role === ChatCompletionRequestMessageRoleEnum.User

View File

@@ -26,10 +26,15 @@ export type ChatDispatchProps = {
res?: NextApiResponse; res?: NextApiResponse;
requestOrigin?: string; requestOrigin?: string;
mode: 'test' | 'chat' | 'debug'; mode: 'test' | 'chat' | 'debug';
teamId: string; // App teamId
tmbId: string; // App tmbId
user: UserModelSchema; user: UserModelSchema;
app: AppDetailType | AppSchema;
runningAppInfo: {
id: string; // May be the id of the system plug-in (cannot be used directly to look up the table)
teamId: string;
tmbId: string; // App tmbId
};
uid: string; // Who run this workflow
chatId?: string; chatId?: string;
responseChatItemId?: string; responseChatItemId?: string;
histories: ChatItemType[]; histories: ChatItemType[];
@@ -50,10 +55,12 @@ export type ModuleDispatchProps<T> = ChatDispatchProps & {
}; };
export type SystemVariablesType = { export type SystemVariablesType = {
userId: string;
appId: string; appId: string;
chatId?: string; chatId?: string;
responseChatItemId?: string; responseChatItemId?: string;
histories: ChatItemType[]; histories: ChatItemType[];
cTime: string;
}; };
/* node props */ /* node props */
@@ -69,7 +76,7 @@ export type RuntimeNodeItemType = {
inputs: FlowNodeInputItemType[]; inputs: FlowNodeInputItemType[];
outputs: FlowNodeOutputItemType[]; outputs: FlowNodeOutputItemType[];
pluginId?: string; pluginId?: string; // workflow id / plugin id
}; };
export type PluginRuntimeType = { export type PluginRuntimeType = {

View File

@@ -17,7 +17,7 @@ import { RunAppModule } from './system/abandoned/runApp/index';
import { PluginInputModule } from './system/pluginInput'; import { PluginInputModule } from './system/pluginInput';
import { PluginOutputModule } from './system/pluginOutput'; import { PluginOutputModule } from './system/pluginOutput';
import { RunPluginModule } from './system/runPlugin'; import { RunPluginModule } from './system/runPlugin';
import { RunAppPluginModule } from './system/runAppPlugin'; import { RunAppNode } from './system/runApp';
import { AiQueryExtension } from './system/queryExtension'; import { AiQueryExtension } from './system/queryExtension';
import type { FlowNodeTemplateType } from '../type/node'; import type { FlowNodeTemplateType } from '../type/node';
@@ -73,6 +73,6 @@ export const moduleTemplatesFlat: FlowNodeTemplateType[] = [
), ),
EmptyNode, EmptyNode,
RunPluginModule, RunPluginModule,
RunAppPluginModule, RunAppNode,
RunAppModule RunAppModule
]; ];

View File

@@ -3,7 +3,7 @@ import { FlowNodeTypeEnum } from '../../node/constant';
import { FlowNodeTemplateType } from '../../type/node'; import { FlowNodeTemplateType } from '../../type/node';
import { getHandleConfig } from '../utils'; import { getHandleConfig } from '../utils';
export const RunAppPluginModule: FlowNodeTemplateType = { export const RunAppNode: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.appModule, id: FlowNodeTypeEnum.appModule,
templateType: FlowNodeTemplateTypeEnum.other, templateType: FlowNodeTemplateTypeEnum.other,
flowNodeType: FlowNodeTypeEnum.appModule, flowNodeType: FlowNodeTypeEnum.appModule,

View File

@@ -326,27 +326,6 @@ export const updatePluginInputByVariables = (
); );
}; };
/* Remove pluginInput variables from global variables
(completions api: Plugin input get value from global variables)
*/
export const removePluginInputVariables = (
variables: Record<string, any>,
nodes: RuntimeNodeItemType[]
) => {
const pluginInputNode = nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput);
if (!pluginInputNode) return variables;
return Object.keys(variables).reduce(
(acc, key) => {
if (!pluginInputNode.inputs.find((input) => input.key === key)) {
acc[key] = variables[key];
}
return acc;
},
{} as Record<string, any>
);
};
// replace {{$xx.xx$}} variables for text // replace {{$xx.xx$}} variables for text
export function replaceEditorVariable({ export function replaceEditorVariable({
text, text,

View File

@@ -11,7 +11,7 @@
"jschardet": "3.1.1", "jschardet": "3.1.1",
"nanoid": "^4.0.1", "nanoid": "^4.0.1",
"next": "14.2.5", "next": "14.2.5",
"openai": "4.53.0", "openai": "4.57.0",
"openapi-types": "^12.1.3", "openapi-types": "^12.1.3",
"timezones-list": "^3.0.2" "timezones-list": "^3.0.2"
}, },

View File

@@ -121,46 +121,22 @@ export const loadRequestMessages = async ({
const imageRegex = const imageRegex =
/(https?:\/\/[^\s/$.?#].[^\s]*\.(?:png|jpe?g|gif|webp|bmp|tiff?|svg|ico|heic|avif))/i; /(https?:\/\/[^\s/$.?#].[^\s]*\.(?:png|jpe?g|gif|webp|bmp|tiff?|svg|ico|heic|avif))/i;
const result: { type: 'text' | 'image'; value: string }[] = []; const result: ChatCompletionContentPart[] = [];
let lastIndex = 0;
let match;
// 使用正则表达式查找所有匹配项 // 提取所有HTTPS图片URL并添加到result开头
while ((match = imageRegex.exec(input.slice(lastIndex))) !== null) { const httpsImages = input.match(imageRegex) || [];
const textBefore = input.slice(lastIndex, lastIndex + match.index); httpsImages.forEach((url) => {
result.push({
// 如果图片URL前有文本添加文本部分 type: 'image_url',
if (textBefore) { image_url: {
result.push({ type: 'text', value: textBefore }); url: url
}
// 添加图片URL
result.push({ type: 'image', value: match[0] });
lastIndex += match.index + match[0].length;
}
// 添加剩余的文本(如果有的话)
if (lastIndex < input.length) {
result.push({ type: 'text', value: input.slice(lastIndex) });
}
return result
.map((item) => {
if (item.type === 'text') {
return { type: 'text', text: item.value };
} }
if (item.type === 'image') { });
return { });
type: 'image_url',
image_url: { // 添加原始input作为文本
url: item.value result.push({ type: 'text', text: input });
} return result;
};
}
return { type: 'text', text: item.value };
})
.filter(Boolean) as ChatCompletionContentPart[];
} }
// Load image // Load image
const parseUserContent = async (content: string | ChatCompletionContentPart[]) => { const parseUserContent = async (content: string | ChatCompletionContentPart[]) => {

View File

@@ -31,7 +31,7 @@ type Response = DispatchNodeResultType<{
export const dispatchAppRequest = async (props: Props): Promise<Response> => { export const dispatchAppRequest = async (props: Props): Promise<Response> => {
const { const {
app: workflowApp, runningAppInfo,
workflowStreamResponse, workflowStreamResponse,
histories, histories,
query, query,
@@ -45,7 +45,7 @@ export const dispatchAppRequest = async (props: Props): Promise<Response> => {
// 检查该工作流的tmb是否有调用该app的权限不是校验对话的人是否有权限 // 检查该工作流的tmb是否有调用该app的权限不是校验对话的人是否有权限
const { app: appData } = await authAppByTmbId({ const { app: appData } = await authAppByTmbId({
appId: app.id, appId: app.id,
tmbId: workflowApp.tmbId, tmbId: runningAppInfo.tmbId,
per: ReadPermissionVal per: ReadPermissionVal
}); });
@@ -61,7 +61,11 @@ export const dispatchAppRequest = async (props: Props): Promise<Response> => {
const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlow({ const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlow({
...props, ...props,
app: appData, runningAppInfo: {
id: String(appData._id),
teamId: String(appData.teamId),
tmbId: String(appData.tmbId)
},
runtimeNodes: storeNodes2RuntimeNodes( runtimeNodes: storeNodes2RuntimeNodes(
appData.modules, appData.modules,
getWorkflowEntryNodeIds(appData.modules) getWorkflowEntryNodeIds(appData.modules)

View File

@@ -37,7 +37,7 @@ export async function dispatchDatasetSearch(
props: DatasetSearchProps props: DatasetSearchProps
): Promise<DatasetSearchResponse> { ): Promise<DatasetSearchResponse> {
const { const {
teamId, runningAppInfo: { teamId },
histories, histories,
node, node,
params: { params: {

View File

@@ -6,7 +6,8 @@ import {
import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants'; import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import type { import type {
ChatDispatchProps, ChatDispatchProps,
ModuleDispatchProps ModuleDispatchProps,
SystemVariablesType
} from '@fastgpt/global/core/workflow/runtime/type'; } from '@fastgpt/global/core/workflow/runtime/type';
import type { RuntimeNodeItemType } from '@fastgpt/global/core/workflow/runtime/type.d'; import type { RuntimeNodeItemType } from '@fastgpt/global/core/workflow/runtime/type.d';
import type { import type {
@@ -554,13 +555,15 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
/* get system variable */ /* get system variable */
export function getSystemVariable({ export function getSystemVariable({
user, user,
app, runningAppInfo,
chatId, chatId,
responseChatItemId, responseChatItemId,
histories = [] histories = [],
}: Props) { uid
}: Props): SystemVariablesType {
return { return {
appId: String(app._id), userId: uid,
appId: String(runningAppInfo.id),
chatId, chatId,
responseChatItemId, responseChatItemId,
histories, histories,

View File

@@ -29,7 +29,7 @@ type UserSelectResponse = DispatchNodeResultType<{
export const dispatchUserSelect = async (props: Props): Promise<UserSelectResponse> => { export const dispatchUserSelect = async (props: Props): Promise<UserSelectResponse> => {
const { const {
workflowStreamResponse, workflowStreamResponse,
app: { _id: appId }, runningAppInfo: { id: appId },
chatId, chatId,
node: { nodeId, isEntry }, node: { nodeId, isEntry },
params: { description, userSelectOptions }, params: { description, userSelectOptions },

View File

@@ -13,6 +13,7 @@ import { authPluginByTmbId } from '../../../../support/permission/app/auth';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { computedPluginUsage } from '../../../app/plugin/utils'; import { computedPluginUsage } from '../../../app/plugin/utils';
import { filterSystemVariables } from '../utils'; import { filterSystemVariables } from '../utils';
import { getPluginRunUserQuery } from '../../utils';
type RunPluginProps = ModuleDispatchProps<{ type RunPluginProps = ModuleDispatchProps<{
[key: string]: any; [key: string]: any;
@@ -22,9 +23,8 @@ type RunPluginResponse = DispatchNodeResultType<{}>;
export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPluginResponse> => { export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPluginResponse> => {
const { const {
node: { pluginId }, node: { pluginId },
app: workflowApp, runningAppInfo,
mode, mode,
teamId,
params: data // Plugin input params: data // Plugin input
} = props; } = props;
@@ -33,9 +33,9 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
} }
// auth plugin // auth plugin
await authPluginByTmbId({ const pluginData = await authPluginByTmbId({
appId: pluginId, appId: pluginId,
tmbId: workflowApp.tmbId, tmbId: runningAppInfo.tmbId,
per: ReadPermissionVal per: ReadPermissionVal
}); });
@@ -61,14 +61,21 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
showStatus: false showStatus: false
}; };
}); });
const runtimeVariables = {
...filterSystemVariables(props.variables),
appId: String(plugin.id)
};
const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlow({ const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlow({
...props, ...props,
runningAppInfo: {
variables: { id: String(plugin.id),
...filterSystemVariables(props.variables), teamId: plugin.teamId || '',
appId: String(plugin.id) tmbId: pluginData?.tmbId || ''
}, },
variables: runtimeVariables,
query: getPluginRunUserQuery(plugin.nodes, runtimeVariables).value,
chatConfig: {},
runtimeNodes, runtimeNodes,
runtimeEdges: initWorkflowEdgeStatus(plugin.edges) runtimeEdges: initWorkflowEdgeStatus(plugin.edges)
}); });
@@ -90,7 +97,7 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
totalPoints: usagePoints, totalPoints: usagePoints,
pluginOutput: output?.pluginOutput, pluginOutput: output?.pluginOutput,
pluginDetail: pluginDetail:
mode === 'test' && plugin.teamId === teamId mode === 'test' && plugin.teamId === runningAppInfo.teamId
? flowResponses.filter((item) => { ? flowResponses.filter((item) => {
const filterArr = [FlowNodeTypeEnum.pluginOutput]; const filterArr = [FlowNodeTypeEnum.pluginOutput];
return !filterArr.includes(item.moduleType as any); return !filterArr.includes(item.moduleType as any);

View File

@@ -16,6 +16,7 @@ import { chatValue2RuntimePrompt, runtimePrompt2ChatsValue } from '@fastgpt/glob
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type'; import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
import { authAppByTmbId } from '../../../../support/permission/app/auth'; import { authAppByTmbId } from '../../../../support/permission/app/auth';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { getAppLatestVersion } from '../../../app/controller';
type Props = ModuleDispatchProps<{ type Props = ModuleDispatchProps<{
[NodeInputKeyEnum.userChatInput]: string; [NodeInputKeyEnum.userChatInput]: string;
@@ -29,7 +30,7 @@ type Response = DispatchNodeResultType<{
export const dispatchRunAppNode = async (props: Props): Promise<Response> => { export const dispatchRunAppNode = async (props: Props): Promise<Response> => {
const { const {
app: workflowApp, runningAppInfo,
histories, histories,
query, query,
node: { pluginId }, node: { pluginId },
@@ -49,9 +50,10 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => {
// Auth the app by tmbId(Not the user, but the workflow user) // Auth the app by tmbId(Not the user, but the workflow user)
const { app: appData } = await authAppByTmbId({ const { app: appData } = await authAppByTmbId({
appId: pluginId, appId: pluginId,
tmbId: workflowApp.tmbId, tmbId: runningAppInfo.tmbId,
per: ReadPermissionVal per: ReadPermissionVal
}); });
const { nodes, edges, chatConfig } = await getAppLatestVersion(pluginId);
// Auto line // Auto line
workflowStreamResponse?.({ workflowStreamResponse?.({
@@ -64,27 +66,31 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => {
const chatHistories = getHistories(history, histories); const chatHistories = getHistories(history, histories);
const { files } = chatValue2RuntimePrompt(query); const { files } = chatValue2RuntimePrompt(query);
// Concat variables // Rewrite children app variables
const systemVariables = filterSystemVariables(variables); const systemVariables = filterSystemVariables(variables);
const childrenRunVariables = { const childrenRunVariables = {
...systemVariables, ...systemVariables,
...childrenAppVariables ...childrenAppVariables,
histories: chatHistories,
appId: String(appData._id)
}; };
const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlow({ const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlow({
...props, ...props,
app: appData, runningAppInfo: {
runtimeNodes: storeNodes2RuntimeNodes( id: String(appData._id),
appData.modules, teamId: String(appData.teamId),
getWorkflowEntryNodeIds(appData.modules) tmbId: String(appData.tmbId)
), },
runtimeEdges: initWorkflowEdgeStatus(appData.edges), runtimeNodes: storeNodes2RuntimeNodes(nodes, getWorkflowEntryNodeIds(nodes)),
runtimeEdges: initWorkflowEdgeStatus(edges),
histories: chatHistories, histories: chatHistories,
variables: childrenRunVariables,
query: runtimePrompt2ChatsValue({ query: runtimePrompt2ChatsValue({
files, files,
text: userChatInput text: userChatInput
}), }),
variables: childrenRunVariables chatConfig
}); });
const completeMessages = chatHistories.concat([ const completeMessages = chatHistories.concat([

View File

@@ -15,7 +15,7 @@ type Response = DispatchNodeResultType<{}>;
export const dispatchCustomFeedback = (props: Record<string, any>): Response => { export const dispatchCustomFeedback = (props: Record<string, any>): Response => {
const { const {
app: { _id: appId }, runningAppInfo: { id: appId },
chatId, chatId,
responseChatItemId: chatItemId, responseChatItemId: chatItemId,
stream, stream,

View File

@@ -42,7 +42,7 @@ const UNDEFINED_SIGN = 'UNDEFINED_SIGN';
export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<HttpResponse> => { export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<HttpResponse> => {
let { let {
app: { _id: appId }, runningAppInfo: { id: appId },
chatId, chatId,
responseChatItemId, responseChatItemId,
variables, variables,

View File

@@ -44,7 +44,7 @@ ${content.slice(0, 100)}${content.length > 100 ? '......' : ''}
export const dispatchReadFiles = async (props: Props): Promise<Response> => { export const dispatchReadFiles = async (props: Props): Promise<Response> => {
const { const {
requestOrigin, requestOrigin,
teamId, runningAppInfo: { teamId },
histories, histories,
chatConfig, chatConfig,
params: { fileUrlList = [] } params: { fileUrlList = [] }

View File

@@ -21,7 +21,7 @@ const UNDEFINED_SIGN = 'UNDEFINED_SIGN';
export const dispatchLafRequest = async (props: LafRequestProps): Promise<LafResponse> => { export const dispatchLafRequest = async (props: LafRequestProps): Promise<LafResponse> => {
let { let {
app: { _id: appId }, runningAppInfo: { id: appId },
chatId, chatId,
responseChatItemId, responseChatItemId,
variables, variables,

View File

@@ -5,7 +5,10 @@ import {
WorkflowIOValueTypeEnum, WorkflowIOValueTypeEnum,
NodeOutputKeyEnum NodeOutputKeyEnum
} from '@fastgpt/global/core/workflow/constants'; } from '@fastgpt/global/core/workflow/constants';
import { RuntimeEdgeItemType } from '@fastgpt/global/core/workflow/runtime/type'; import {
RuntimeEdgeItemType,
SystemVariablesType
} from '@fastgpt/global/core/workflow/runtime/type';
import { responseWrite } from '../../../common/response'; import { responseWrite } from '../../../common/response';
import { NextApiResponse } from 'next'; import { NextApiResponse } from 'next';
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants'; import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
@@ -144,8 +147,9 @@ export const removeSystemVariable = (variables: Record<string, any>) => {
return copyVariables; return copyVariables;
}; };
export const filterSystemVariables = (variables: Record<string, any>) => { export const filterSystemVariables = (variables: Record<string, any>): SystemVariablesType => {
return { return {
userId: variables.userId,
appId: variables.appId, appId: variables.appId,
chatId: variables.chatId, chatId: variables.chatId,
responseChatItemId: variables.responseChatItemId, responseChatItemId: variables.responseChatItemId,

View File

@@ -1,5 +1,13 @@
import { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type'; import { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
import { countPromptTokens } from '../../common/string/tiktoken/index'; import { countPromptTokens } from '../../common/string/tiktoken/index';
import { getNanoid } from '@fastgpt/global/common/string/tools';
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import {
getPluginInputsFromStoreNodes,
getPluginRunContent
} from '@fastgpt/global/core/app/plugin/utils';
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
import { UserChatItemType } from '@fastgpt/global/core/chat/type';
/* filter search result */ /* filter search result */
export const filterSearchResultsByMaxChars = async ( export const filterSearchResultsByMaxChars = async (
@@ -23,3 +31,25 @@ export const filterSearchResultsByMaxChars = async (
return results.length === 0 ? list.slice(0, 1) : results; return results.length === 0 ? list.slice(0, 1) : results;
}; };
/* Get plugin runtime input user query */
export const getPluginRunUserQuery = (
nodes: StoreNodeItemType[],
variables: Record<string, any>
): UserChatItemType & { dataId: string } => {
return {
dataId: getNanoid(24),
obj: ChatRoleEnum.Human,
value: [
{
type: ChatItemValueTypeEnum.text,
text: {
content: getPluginRunContent({
pluginInputs: getPluginInputsFromStoreNodes(nodes),
variables
})
}
}
]
};
};

View File

@@ -25,11 +25,13 @@ export const authPluginByTmbId = async ({
}) => { }) => {
const { source } = await splitCombinePluginId(appId); const { source } = await splitCombinePluginId(appId);
if (source === PluginSourceEnum.personal) { if (source === PluginSourceEnum.personal) {
await authAppByTmbId({ const { app } = await authAppByTmbId({
appId, appId,
tmbId, tmbId,
per per
}); });
return app;
} }
}; };

View File

@@ -6,6 +6,7 @@ export type EditorVariablePickerType = {
required?: boolean; required?: boolean;
icon?: string; icon?: string;
valueType?: WorkflowIOValueTypeEnum; valueType?: WorkflowIOValueTypeEnum;
valueDesc?: string;
}; };
export type EditorVariableLabelPickerType = { export type EditorVariableLabelPickerType = {

View File

@@ -1,4 +1,11 @@
{ {
" i18nT('workflow": {
"use_user_id'),\n required": {
"workflow": {
"use_user_id": ""
}
}
},
"Code": "Code", "Code": "Code",
"about_xxx_question": "Questions about xxx", "about_xxx_question": "Questions about xxx",
"add_new_input": "Add input", "add_new_input": "Add input",
@@ -123,6 +130,7 @@
"select_another_application_to_call": "You can choose another application to call", "select_another_application_to_call": "You can choose another application to call",
"special_array_format": "Special array format, when the search result is empty, an empty array is returned.", "special_array_format": "Special array format, when the search result is empty, an empty array is returned.",
"start_with": "Start with", "start_with": "Start with",
"system_variables": "System variables",
"target_fields_description": "A target field is composed of 'description' and 'key', and multiple target fields can be extracted.", "target_fields_description": "A target field is composed of 'description' and 'key', and multiple target fields can be extracted.",
"template": { "template": {
"ai_chat": "LLM chat", "ai_chat": "LLM chat",
@@ -143,6 +151,7 @@
"trigger_after_application_completion": "will be triggered after the application has completely ended", "trigger_after_application_completion": "will be triggered after the application has completely ended",
"update_link_error": "Update link exception", "update_link_error": "Update link exception",
"update_specified_node_output_or_global_variable": "You can update the output value of the specified node or update global variables", "update_specified_node_output_or_global_variable": "You can update the output value of the specified node or update global variables",
"use_user_id": "User ID",
"user_question": "User issues", "user_question": "User issues",
"variable_picker_tips": "enter node name or variable name to search", "variable_picker_tips": "enter node name or variable name to search",
"variable_update": "variable update", "variable_update": "variable update",

View File

@@ -1,4 +1,11 @@
{ {
" i18nT('workflow": {
"use_user_id'),\n required": {
"workflow": {
"use_user_id": "使用者 ID"
}
}
},
"Code": "代码", "Code": "代码",
"about_xxx_question": "关于 xxx 的问题", "about_xxx_question": "关于 xxx 的问题",
"add_new_input": "新增输入", "add_new_input": "新增输入",
@@ -123,6 +130,7 @@
"select_another_application_to_call": "可以选择一个其他应用进行调用", "select_another_application_to_call": "可以选择一个其他应用进行调用",
"special_array_format": "特殊数组格式,搜索结果为空时,返回空数组。", "special_array_format": "特殊数组格式,搜索结果为空时,返回空数组。",
"start_with": "开始为", "start_with": "开始为",
"system_variables": "系统变量",
"target_fields_description": "由 '描述' 和 'key' 组成一个目标字段,可提取多个目标字段", "target_fields_description": "由 '描述' 和 'key' 组成一个目标字段,可提取多个目标字段",
"template": { "template": {
"ai_chat": "AI 对话", "ai_chat": "AI 对话",
@@ -143,6 +151,7 @@
"trigger_after_application_completion": "将在应用完全结束后触发", "trigger_after_application_completion": "将在应用完全结束后触发",
"update_link_error": "更新链接异常", "update_link_error": "更新链接异常",
"update_specified_node_output_or_global_variable": "可以更新指定节点的输出值或更新全局变量", "update_specified_node_output_or_global_variable": "可以更新指定节点的输出值或更新全局变量",
"use_user_id": "使用者 ID",
"user_question": "用户问题", "user_question": "用户问题",
"variable_picker_tips": "可输入节点名或变量名搜索", "variable_picker_tips": "可输入节点名或变量名搜索",
"variable_update": "变量更新", "variable_update": "变量更新",

48
pnpm-lock.yaml generated
View File

@@ -63,8 +63,8 @@ importers:
specifier: 14.2.5 specifier: 14.2.5
version: 14.2.5(@babel/core@7.24.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8) version: 14.2.5(@babel/core@7.24.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8)
openai: openai:
specifier: 4.53.0 specifier: 4.57.0
version: 4.53.0(encoding@0.1.13) version: 4.57.0(encoding@0.1.13)
openapi-types: openapi-types:
specifier: ^12.1.3 specifier: ^12.1.3
version: 12.1.3 version: 12.1.3
@@ -657,12 +657,6 @@ importers:
specifier: ^5.1.3 specifier: ^5.1.3
version: 5.5.3 version: 5.5.3
scripts/i18n:
devDependencies:
'@phenomnomnominal/tsquery':
specifier: ^6.1.3
version: 6.1.3(typescript@5.5.3)
scripts/icon: scripts/icon:
dependencies: dependencies:
express: express:
@@ -2820,11 +2814,6 @@ packages:
'@petamoriken/float16@3.8.7': '@petamoriken/float16@3.8.7':
resolution: {integrity: sha512-/Ri4xDDpe12NT6Ex/DRgHzLlobiQXEW/hmG08w1wj/YU7hLemk97c+zHQFp0iZQ9r7YqgLEXZR2sls4HxBf9NA==} resolution: {integrity: sha512-/Ri4xDDpe12NT6Ex/DRgHzLlobiQXEW/hmG08w1wj/YU7hLemk97c+zHQFp0iZQ9r7YqgLEXZR2sls4HxBf9NA==}
'@phenomnomnominal/tsquery@6.1.3':
resolution: {integrity: sha512-CEqpJ872StsxRmwv9ePCZ4BCisrJSlREUC5XxIRYxhvODt4aQoJFFmjTgaP6meyKiiXxxN/VWPZ58j4yHXRkmw==}
peerDependencies:
typescript: ^3 || ^4 || ^5
'@pkgjs/parseargs@0.11.0': '@pkgjs/parseargs@0.11.0':
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'} engines: {node: '>=14'}
@@ -3240,9 +3229,6 @@ packages:
'@types/eslint@8.56.10': '@types/eslint@8.56.10':
resolution: {integrity: sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==} resolution: {integrity: sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==}
'@types/esquery@1.5.4':
resolution: {integrity: sha512-yYO4Q8H+KJHKW1rEeSzHxcZi90durqYgWVfnh5K6ZADVBjBv2e1NEveYX5yT2bffgN7RqzH3k9930m+i2yBoMA==}
'@types/estree-jsx@1.0.5': '@types/estree-jsx@1.0.5':
resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==}
@@ -6978,9 +6964,14 @@ packages:
resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==}
engines: {node: '>=12'} engines: {node: '>=12'}
openai@4.53.0: openai@4.57.0:
resolution: {integrity: sha512-XoMaJsSLuedW5eoMEMmZbdNoXgML3ujcU5KfwRnC6rnbmZkHE2Q4J/SArwhqCxQRqJwHnQUj1LpiROmKPExZJA==} resolution: {integrity: sha512-JnwBSIYqiZ3jYjB5f2in8hQ0PRA092c6m+/6dYB0MzK0BEbn+0dioxZsPLBm5idJbg9xzLNOiGVm2OSuhZ+BdQ==}
hasBin: true hasBin: true
peerDependencies:
zod: ^3.23.8
peerDependenciesMeta:
zod:
optional: true
openapi-types@12.1.3: openapi-types@12.1.3:
resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==}
@@ -8661,10 +8652,6 @@ packages:
web-namespaces@2.0.1: web-namespaces@2.0.1:
resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==}
web-streams-polyfill@3.3.3:
resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
engines: {node: '>= 8'}
web-streams-polyfill@4.0.0-beta.3: web-streams-polyfill@4.0.0-beta.3:
resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==}
engines: {node: '>= 14'} engines: {node: '>= 14'}
@@ -11503,12 +11490,6 @@ snapshots:
'@petamoriken/float16@3.8.7': {} '@petamoriken/float16@3.8.7': {}
'@phenomnomnominal/tsquery@6.1.3(typescript@5.5.3)':
dependencies:
'@types/esquery': 1.5.4
esquery: 1.6.0
typescript: 5.5.3
'@pkgjs/parseargs@0.11.0': '@pkgjs/parseargs@0.11.0':
optional: true optional: true
@@ -11967,10 +11948,6 @@ snapshots:
'@types/estree': 1.0.5 '@types/estree': 1.0.5
'@types/json-schema': 7.0.15 '@types/json-schema': 7.0.15
'@types/esquery@1.5.4':
dependencies:
'@types/estree': 1.0.5
'@types/estree-jsx@1.0.5': '@types/estree-jsx@1.0.5':
dependencies: dependencies:
'@types/estree': 1.0.5 '@types/estree': 1.0.5
@@ -16845,16 +16822,17 @@ snapshots:
dependencies: dependencies:
mimic-fn: 4.0.0 mimic-fn: 4.0.0
openai@4.53.0(encoding@0.1.13): openai@4.57.0(encoding@0.1.13):
dependencies: dependencies:
'@types/node': 18.19.40 '@types/node': 18.19.40
'@types/node-fetch': 2.6.11 '@types/node-fetch': 2.6.11
'@types/qs': 6.9.15
abort-controller: 3.0.0 abort-controller: 3.0.0
agentkeepalive: 4.5.0 agentkeepalive: 4.5.0
form-data-encoder: 1.7.2 form-data-encoder: 1.7.2
formdata-node: 4.4.1 formdata-node: 4.4.1
node-fetch: 2.7.0(encoding@0.1.13) node-fetch: 2.7.0(encoding@0.1.13)
web-streams-polyfill: 3.3.3 qs: 6.12.3
transitivePeerDependencies: transitivePeerDependencies:
- encoding - encoding
@@ -18699,8 +18677,6 @@ snapshots:
web-namespaces@2.0.1: {} web-namespaces@2.0.1: {}
web-streams-polyfill@3.3.3: {}
web-streams-polyfill@4.0.0-beta.3: {} web-streams-polyfill@4.0.0-beta.3: {}
web-worker@1.3.0: {} web-worker@1.3.0: {}

View File

@@ -48,7 +48,10 @@ const Markdown = ({
const formatSource = useMemo(() => { const formatSource = useMemo(() => {
const formatSource = source const formatSource = source
.replace(/(http[s]?:\/\/[^\s。]+)([。,])/g, '$1 $2') // Follow the link with a space .replace(
/([\u4e00-\u9fa5\u3000-\u303f])([\w\u0020-\u007e])|([a-zA-Z0-9\u0020-\u007e])([\u4e00-\u9fa5\u3000-\u303f])/g,
'$1$3 $2$4'
) // Chinese and english chars separated by space
.replace(/\n*(\[QUOTE SIGN\]\(.*\))/g, '$1'); .replace(/\n*(\[QUOTE SIGN\]\(.*\))/g, '$1');
return formatSource; return formatSource;

View File

@@ -13,10 +13,7 @@ import { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
import { removeEmptyUserInput } from '@fastgpt/global/core/chat/utils'; import { removeEmptyUserInput } from '@fastgpt/global/core/chat/utils';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { import { updatePluginInputByVariables } from '@fastgpt/global/core/workflow/utils';
removePluginInputVariables,
updatePluginInputByVariables
} from '@fastgpt/global/core/workflow/utils';
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt'; import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
import { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type'; import { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type';
@@ -89,7 +86,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
// Plugin need to replace inputs // Plugin need to replace inputs
if (isPlugin) { if (isPlugin) {
runtimeNodes = updatePluginInputByVariables(runtimeNodes, variables); runtimeNodes = updatePluginInputByVariables(runtimeNodes, variables);
variables = removePluginInputVariables(variables, runtimeNodes); variables = {};
} else { } else {
if (!userInput) { if (!userInput) {
throw new Error('Params Error'); throw new Error('Params Error');
@@ -109,10 +106,13 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
res, res,
requestOrigin: req.headers.origin, requestOrigin: req.headers.origin,
mode: 'test', mode: 'test',
teamId, runningAppInfo: {
tmbId, id: appId,
teamId,
tmbId
},
uid: tmbId,
user, user,
app,
runtimeNodes, runtimeNodes,
runtimeEdges: initWorkflowEdgeStatus(edges, chatMessages), runtimeEdges: initWorkflowEdgeStatus(edges, chatMessages),
variables, variables,

View File

@@ -43,10 +43,13 @@ async function handler(
res, res,
requestOrigin: req.headers.origin, requestOrigin: req.headers.origin,
mode: 'debug', mode: 'debug',
teamId, runningAppInfo: {
tmbId, id: appId,
teamId,
tmbId
},
uid: tmbId,
user, user,
app,
runtimeNodes: nodes, runtimeNodes: nodes,
runtimeEdges: edges, runtimeEdges: edges,
variables, variables,

View File

@@ -52,18 +52,12 @@ import { NextAPI } from '@/service/middleware/entry';
import { getAppLatestVersion } from '@fastgpt/service/core/app/controller'; import { getAppLatestVersion } from '@fastgpt/service/core/app/controller';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { import { updatePluginInputByVariables } from '@fastgpt/global/core/workflow/utils';
removePluginInputVariables,
updatePluginInputByVariables
} from '@fastgpt/global/core/workflow/utils';
import { getNanoid } from '@fastgpt/global/common/string/tools'; 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'; import { getSystemTime } from '@fastgpt/global/common/time/timezone';
import { rewriteNodeOutputByHistories } from '@fastgpt/global/core/workflow/runtime/utils'; import { rewriteNodeOutputByHistories } from '@fastgpt/global/core/workflow/runtime/utils';
import { getWorkflowResponseWrite } from '@fastgpt/service/core/workflow/dispatch/utils'; import { getWorkflowResponseWrite } from '@fastgpt/service/core/workflow/dispatch/utils';
import { getPluginRunUserQuery } from '@fastgpt/service/core/workflow/utils';
type FastGptWebChatProps = { type FastGptWebChatProps = {
chatId?: string; // undefined: get histories from messages, '': new chat, 'xxxxx': get histories from db chatId?: string; // undefined: get histories from messages, '': new chat, 'xxxxx': get histories from db
@@ -193,21 +187,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
// Get obj=Human history // Get obj=Human history
const userQuestion: UserChatItemType = (() => { const userQuestion: UserChatItemType = (() => {
if (isPlugin) { if (isPlugin) {
return { return getPluginRunUserQuery(app.modules, variables);
dataId: getNanoid(24),
obj: ChatRoleEnum.Human,
value: [
{
type: ChatItemValueTypeEnum.text,
text: {
content: getPluginRunContent({
pluginInputs: getPluginInputsFromStoreNodes(app.modules),
variables
})
}
}
]
};
} }
const latestHumanChat = chatMessages.pop() as UserChatItemType | undefined; const latestHumanChat = chatMessages.pop() as UserChatItemType | undefined;
@@ -246,8 +226,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
if (isPlugin) { if (isPlugin) {
// Assign values to runtimeNodes using variables // Assign values to runtimeNodes using variables
runtimeNodes = updatePluginInputByVariables(runtimeNodes, variables); runtimeNodes = updatePluginInputByVariables(runtimeNodes, variables);
// Remove pluginInput fields from variables (they are not global variables) // Plugin runtime does not need global variables(It has been injected into the pluginInputNode)
variables = removePluginInputVariables(variables, runtimeNodes); variables = {};
} }
runtimeNodes = rewriteNodeOutputByHistories(newHistories, runtimeNodes); runtimeNodes = rewriteNodeOutputByHistories(newHistories, runtimeNodes);
@@ -266,9 +246,14 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
requestOrigin: req.headers.origin, requestOrigin: req.headers.origin,
mode: 'chat', mode: 'chat',
user, user,
teamId: String(teamId),
tmbId: String(tmbId), runningAppInfo: {
app, id: String(app._id),
teamId: String(app.teamId),
tmbId: String(app.tmbId)
},
uid: String(outLinkUserId || tmbId),
chatId, chatId,
responseChatItemId, responseChatItemId,
runtimeNodes, runtimeNodes,

View File

@@ -11,7 +11,8 @@ import {
Td, Td,
Tbody, Tbody,
useDisclosure, useDisclosure,
Link Link,
HStack
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import { useLoading } from '@fastgpt/web/hooks/useLoading'; import { useLoading } from '@fastgpt/web/hooks/useLoading';
@@ -66,26 +67,24 @@ const FeiShu = ({ appId }: { appId: string }) => {
return ( return (
<Box position={'relative'} pt={3} px={5} minH={'50vh'}> <Box position={'relative'} pt={3} px={5} minH={'50vh'}>
<Flex justifyContent={'space-between'} flexDirection="row"> <Flex justifyContent={'space-between'} flexDirection="row">
<Flex alignItems={'center'}> <HStack>
<Box fontWeight={'bold'} fontSize={['md', 'lg']}> <Box fontWeight={'bold'} fontSize={['md', 'lg']}>
{t('common:core.app.publish.Fei shu bot publish')} {t('common:core.app.publish.Fei shu bot publish')}
</Box> </Box>
{feConfigs?.docUrl && ( {feConfigs?.docUrl && (
<Link <Link
href={feConfigs.openAPIDocUrl || getDocPath('/docs/course/feishu')} href={feConfigs.openAPIDocUrl || getDocPath('/docs/course/feishu')}
target={'_blank'} target={'_blank'}
ml={2}
color={'primary.500'} color={'primary.500'}
fontSize={'sm'} fontSize={'sm'}
> >
<Flex alignItems={'center'}> <Flex alignItems={'center'}>
<MyIcon name="book" mr="1" /> <MyIcon name="book" mr="1" w={'1rem'} />
{t('common:common.Read document')} {t('common:common.Read document')}
</Flex> </Flex>
</Link> </Link>
)} )}
</Flex> </HStack>
<Button <Button
variant={'primary'} variant={'primary'}
colorScheme={'blue'} colorScheme={'blue'}

View File

@@ -11,7 +11,8 @@ import {
Td, Td,
Tbody, Tbody,
useDisclosure, useDisclosure,
Link Link,
HStack
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import { useLoading } from '@fastgpt/web/hooks/useLoading'; import { useLoading } from '@fastgpt/web/hooks/useLoading';
@@ -67,7 +68,7 @@ const OffiAccount = ({ appId }: { appId: string }) => {
return ( return (
<Box position={'relative'} pt={3} px={5} minH={'50vh'}> <Box position={'relative'} pt={3} px={5} minH={'50vh'}>
<Flex justifyContent={'space-between'} flexDirection="row"> <Flex justifyContent={'space-between'} flexDirection="row">
<Flex alignItems={'center'}> <HStack>
<Box fontWeight={'bold'} fontSize={['md', 'lg']}> <Box fontWeight={'bold'} fontSize={['md', 'lg']}>
{t('publish:official_account.name')} {t('publish:official_account.name')}
</Box> </Box>
@@ -81,12 +82,12 @@ const OffiAccount = ({ appId }: { appId: string }) => {
fontSize={'sm'} fontSize={'sm'}
> >
<Flex alignItems={'center'}> <Flex alignItems={'center'}>
<MyIcon name="book" mr="1" /> <MyIcon name="book" mr="1" w={'1rem'} />
{t('common:common.Read document')} {t('common:common.Read document')}
</Flex> </Flex>
</Link> </Link>
)} )}
</Flex> </HStack>
<Button <Button
variant={'primary'} variant={'primary'}
colorScheme={'blue'} colorScheme={'blue'}

View File

@@ -28,7 +28,7 @@ import SettingLLMModel from '@/components/core/ai/SettingLLMModel';
import type { SettingAIDataType } from '@fastgpt/global/core/app/type.d'; import type { SettingAIDataType } from '@fastgpt/global/core/app/type.d';
import DeleteIcon, { hoverDeleteStyles } from '@fastgpt/web/components/common/Icon/delete'; import DeleteIcon, { hoverDeleteStyles } from '@fastgpt/web/components/common/Icon/delete';
import { TTSTypeEnum } from '@/web/core/app/constants'; import { TTSTypeEnum } from '@/web/core/app/constants';
import { getSystemVariables } from '@/web/core/app/utils'; import { workflowSystemVariables } from '@/web/core/app/utils';
import { useI18n } from '@/web/context/I18n'; import { useI18n } from '@/web/context/I18n';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { AppContext } from '@/pages/app/detail/components/context'; import { AppContext } from '@/pages/app/detail/components/context';
@@ -109,10 +109,11 @@ const EditForm = ({
const formatVariables = useMemo( const formatVariables = useMemo(
() => () =>
formatEditorVariablePickerIcon([ formatEditorVariablePickerIcon([
...getSystemVariables(t), ...workflowSystemVariables,
...(appForm.chatConfig.variables || []) ...(appForm.chatConfig.variables || [])
]).map((item) => ({ ]).map((item) => ({
...item, ...item,
label: t(item.label as any),
parent: { parent: {
id: 'VARIABLE_NODE_ID', id: 'VARIABLE_NODE_ID',
label: t('common:core.module.Variable'), label: t('common:core.module.Variable'),

View File

@@ -276,14 +276,13 @@ export function RenderHttpProps({
const variables = useMemo(() => { const variables = useMemo(() => {
const globalVariables = getWorkflowGlobalVariables({ const globalVariables = getWorkflowGlobalVariables({
nodes: nodeList, nodes: nodeList,
chatConfig: appDetail.chatConfig, chatConfig: appDetail.chatConfig
t
}); });
const nodeVariables = formatEditorVariablePickerIcon(getNodeDynamicInputs(nodeId)); const nodeVariables = formatEditorVariablePickerIcon(getNodeDynamicInputs(nodeId));
return [...nodeVariables, ...globalVariables]; return [...nodeVariables, ...globalVariables];
}, [appDetail.chatConfig, getNodeDynamicInputs, nodeId, nodeList, t]); }, [appDetail.chatConfig, getNodeDynamicInputs, nodeId, nodeList]);
const variableText = useMemo(() => { const variableText = useMemo(() => {
return variables return variables

View File

@@ -348,10 +348,9 @@ const ConditionSelect = ({
return getRefData({ return getRefData({
variable, variable,
nodeList, nodeList,
chatConfig: appDetail.chatConfig, chatConfig: appDetail.chatConfig
t
}); });
}, [appDetail.chatConfig, nodeList, t, variable]); }, [appDetail.chatConfig, nodeList, variable]);
const conditionList = useMemo(() => { const conditionList = useMemo(() => {
if (valueType === WorkflowIOValueTypeEnum.string) return stringConditionList; if (valueType === WorkflowIOValueTypeEnum.string) return stringConditionList;

View File

@@ -100,8 +100,7 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
const { valueType } = getRefData({ const { valueType } = getRefData({
variable: updateItem.variable, variable: updateItem.variable,
nodeList, nodeList,
chatConfig: appDetail.chatConfig, chatConfig: appDetail.chatConfig
t
}); });
const renderTypeData = menuList.find((item) => item.renderType === updateItem.renderType); const renderTypeData = menuList.find((item) => item.renderType === updateItem.renderType);
const handleUpdate = (newValue: ReferenceValueProps | string) => { const handleUpdate = (newValue: ReferenceValueProps | string) => {

View File

@@ -1,4 +1,4 @@
import React from 'react'; import React, { useMemo } from 'react';
import { NodeProps } from 'reactflow'; import { NodeProps } from 'reactflow';
import NodeCard from './render/NodeCard'; import NodeCard from './render/NodeCard';
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node.d'; import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node.d';
@@ -9,14 +9,17 @@ import { useTranslation } from 'next-i18next';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { WorkflowContext } from '../../context'; import { WorkflowContext } from '../../context';
import { useCreation } from 'ahooks'; import { useCreation } from 'ahooks';
import { getWorkflowGlobalVariables } from '@/web/core/workflow/utils';
import { FlowNodeOutputItemType } from '@fastgpt/global/core/workflow/type/io'; import { FlowNodeOutputItemType } from '@fastgpt/global/core/workflow/type/io';
import { import { FlowNodeOutputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
chatHistoryValueDesc,
FlowNodeOutputTypeEnum
} from '@fastgpt/global/core/workflow/node/constant';
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants'; import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
import { AppContext } from '@/pages/app/detail/components/context'; import { AppContext } from '@/pages/app/detail/components/context';
import { workflowSystemVariables } from '@/web/core/app/utils';
import {
formatEditorVariablePickerIcon,
getAppChatConfig,
getGuideModule
} from '@fastgpt/global/core/workflow/utils';
import MyDivider from '@fastgpt/web/components/common/MyDivider';
const NodeStart = ({ data, selected }: NodeProps<FlowNodeItemType>) => { const NodeStart = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -24,36 +27,42 @@ const NodeStart = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList); const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
const { appDetail } = useContextSelector(AppContext, (v) => v); const { appDetail } = useContextSelector(AppContext, (v) => v);
const variablesOutputs = useCreation(() => { const customGlobalVariables = useCreation(() => {
const variables = getWorkflowGlobalVariables({ const globalVariables = formatEditorVariablePickerIcon(
nodes: nodeList, getAppChatConfig({
chatConfig: appDetail.chatConfig, chatConfig: appDetail.chatConfig,
t systemConfigNode: getGuideModule(nodeList),
}); isPublicFetch: true
})?.variables || []
);
return variables.map<FlowNodeOutputItemType>((item) => { return globalVariables.map<FlowNodeOutputItemType>((item) => {
if (item.valueType === WorkflowIOValueTypeEnum.chatHistory) {
return {
id: item.key,
type: FlowNodeOutputTypeEnum.static,
key: item.key,
required: item.required,
valueType: item.valueType,
valueDesc: chatHistoryValueDesc,
label: item.label
};
}
return { return {
id: item.key, id: item.key,
type: FlowNodeOutputTypeEnum.static, type: FlowNodeOutputTypeEnum.static,
key: item.key, key: item.key,
required: item.required, required: item.required,
valueType: item.valueType || WorkflowIOValueTypeEnum.any, valueType: item.valueType || WorkflowIOValueTypeEnum.any,
label: item.label label: t(item.label as any),
valueDesc: item.valueDesc
}; };
}); });
}, [nodeList, appDetail.chatConfig, t]); }, [nodeList, appDetail.chatConfig, t]);
const systemVariables = useMemo(
() =>
workflowSystemVariables.map((item) => ({
id: item.key,
type: FlowNodeOutputTypeEnum.static,
key: item.key,
required: item.required,
valueType: item.valueType || WorkflowIOValueTypeEnum.any,
label: t(item.label as any),
valueDesc: item.valueDesc
})),
[t]
);
return ( return (
<NodeCard <NodeCard
minW={'240px'} minW={'240px'}
@@ -71,7 +80,14 @@ const NodeStart = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
</Container> </Container>
<Container> <Container>
<IOTitle text={t('common:core.module.Variable')} /> <IOTitle text={t('common:core.module.Variable')} />
<RenderOutput nodeId={nodeId} flowOutputList={variablesOutputs} /> {customGlobalVariables.length > 0 && (
<>
<RenderOutput nodeId={nodeId} flowOutputList={customGlobalVariables} />
<MyDivider />
</>
)}
<RenderOutput nodeId={nodeId} flowOutputList={systemVariables} />
</Container> </Container>
</NodeCard> </NodeCard>
); );

View File

@@ -21,8 +21,7 @@ const JsonEditor = ({ inputs = [], item, nodeId }: RenderInputProps) => {
const variables = useCreation(() => { const variables = useCreation(() => {
const globalVariables = getWorkflowGlobalVariables({ const globalVariables = getWorkflowGlobalVariables({
nodes: nodeList, nodes: nodeList,
chatConfig: appDetail.chatConfig, chatConfig: appDetail.chatConfig
t
}); });
const nodeVariables = formatEditorVariablePickerIcon(getNodeDynamicInputs(nodeId)); const nodeVariables = formatEditorVariablePickerIcon(getNodeDynamicInputs(nodeId));

View File

@@ -54,12 +54,11 @@ const SettingQuotePrompt = (props: RenderInputProps) => {
const variables = useCreation(() => { const variables = useCreation(() => {
const globalVariables = getWorkflowGlobalVariables({ const globalVariables = getWorkflowGlobalVariables({
nodes: nodeList, nodes: nodeList,
chatConfig: appDetail.chatConfig, chatConfig: appDetail.chatConfig
t
}); });
return globalVariables; return globalVariables;
}, [nodeList, t]); }, [nodeList]);
const [selectTemplateData, setSelectTemplateData] = useState<{ const [selectTemplateData, setSelectTemplateData] = useState<{
title: string; title: string;

View File

@@ -48,7 +48,7 @@ const OutputLabel = ({ nodeId, output }: { nodeId: string; output: FlowNodeOutpu
)} )}
</Box> </Box>
); );
}, [output.type, output.key, t, label, description, valueType, nodeId]); }, [output.type, output.key, t, label, description, valueType, valueDesc, nodeId]);
return Render; return Render;
}; };

View File

@@ -134,12 +134,12 @@ const RenderOutput = ({
); );
return ( return (
<> <>
{renderOutputs.map((output) => { {renderOutputs.map((output, i) => {
return output.label ? ( return output.label ? (
<FormLabel <FormLabel
key={output.key} key={output.key}
required={output.required} required={output.required}
_notLast={{ mb: 5 }} mb={i === renderOutputs.length - 1 ? 0 : 5}
position={'relative'} position={'relative'}
> >
<OutputLabel nodeId={nodeId} output={output} /> <OutputLabel nodeId={nodeId} output={output} />

View File

@@ -35,9 +35,12 @@ export const getScheduleTriggerApp = async () => {
chatId: getNanoid(), chatId: getNanoid(),
user, user,
mode: 'chat', mode: 'chat',
teamId: String(app.teamId), runningAppInfo: {
tmbId: String(app.tmbId), id: String(app._id),
app, teamId: String(app.teamId),
tmbId: String(app.tmbId)
},
uid: String(app.tmbId),
runtimeNodes: storeNodes2RuntimeNodes(app.modules, getWorkflowEntryNodeIds(app.modules)), runtimeNodes: storeNodes2RuntimeNodes(app.modules, getWorkflowEntryNodeIds(app.modules)),
runtimeEdges: initWorkflowEdgeStatus(app.edges), runtimeEdges: initWorkflowEdgeStatus(app.edges),
variables: {}, variables: {},

View File

@@ -6,6 +6,7 @@ import {
} from '@fastgpt/global/core/app/type'; } from '@fastgpt/global/core/app/type';
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node.d'; import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node.d';
import { import {
chatHistoryValueDesc,
FlowNodeInputTypeEnum, FlowNodeInputTypeEnum,
FlowNodeTypeEnum FlowNodeTypeEnum
} from '@fastgpt/global/core/workflow/node/constant'; } from '@fastgpt/global/core/workflow/node/constant';
@@ -14,7 +15,6 @@ import { NodeInputKeyEnum, WorkflowIOValueTypeEnum } from '@fastgpt/global/core/
import { getNanoid } from '@fastgpt/global/common/string/tools'; import { getNanoid } from '@fastgpt/global/common/string/tools';
import { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge'; import { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
import { EditorVariablePickerType } from '@fastgpt/web/components/common/Textarea/PromptEditor/type'; import { EditorVariablePickerType } from '@fastgpt/web/components/common/Textarea/PromptEditor/type';
import { TFunction } from 'next-i18next';
import { ToolModule } from '@fastgpt/global/core/workflow/template/system/tools'; import { ToolModule } from '@fastgpt/global/core/workflow/template/system/tools';
import { useDatasetStore } from '../dataset/store/dataset'; import { useDatasetStore } from '../dataset/store/dataset';
import { import {
@@ -25,6 +25,7 @@ import { SystemConfigNode } from '@fastgpt/global/core/workflow/template/system/
import { AiChatModule } from '@fastgpt/global/core/workflow/template/system/aiChat'; import { AiChatModule } from '@fastgpt/global/core/workflow/template/system/aiChat';
import { DatasetSearchModule } from '@fastgpt/global/core/workflow/template/system/datasetSearch'; import { DatasetSearchModule } from '@fastgpt/global/core/workflow/template/system/datasetSearch';
import { ReadFilesNodes } from '@fastgpt/global/core/workflow/template/system/readFiles'; import { ReadFilesNodes } from '@fastgpt/global/core/workflow/template/system/readFiles';
import { i18nT } from '@fastgpt/web/i18n/utils';
type WorkflowType = { type WorkflowType = {
nodes: StoreNodeItemType[]; nodes: StoreNodeItemType[];
@@ -519,38 +520,43 @@ export function form2AppWorkflow(
}; };
} }
export const getSystemVariables = (t: TFunction): EditorVariablePickerType[] => { export const workflowSystemVariables: EditorVariablePickerType[] = [
return [ {
{ key: 'userId',
key: 'appId', label: i18nT('workflow:use_user_id'),
label: t('common:core.module.http.AppId'), required: true,
required: true, valueType: WorkflowIOValueTypeEnum.string
valueType: WorkflowIOValueTypeEnum.string },
}, {
{ key: 'appId',
key: 'chatId', label: i18nT('common:core.module.http.AppId'),
label: t('common:core.module.http.ChatId'), required: true,
valueType: WorkflowIOValueTypeEnum.string valueType: WorkflowIOValueTypeEnum.string
}, },
{ {
key: 'responseChatItemId', key: 'chatId',
label: t('common:core.module.http.ResponseChatItemId'), label: i18nT('common:core.module.http.ChatId'),
valueType: WorkflowIOValueTypeEnum.string valueType: WorkflowIOValueTypeEnum.string
}, },
{ {
key: 'histories', key: 'responseChatItemId',
label: t('common:core.module.http.Histories'), label: i18nT('common:core.module.http.ResponseChatItemId'),
required: true, valueType: WorkflowIOValueTypeEnum.string
valueType: WorkflowIOValueTypeEnum.chatHistory },
}, {
{ key: 'histories',
key: 'cTime', label: i18nT('common:core.module.http.Histories'),
label: t('common:core.module.http.Current time'), required: true,
required: true, valueType: WorkflowIOValueTypeEnum.chatHistory,
valueType: WorkflowIOValueTypeEnum.string valueDesc: chatHistoryValueDesc
} },
]; {
}; key: 'cTime',
label: i18nT('common:core.module.http.Current time'),
required: true,
valueType: WorkflowIOValueTypeEnum.string
}
];
export const getAppQGuideCustomURL = (appDetail: AppDetailType | AppSchema): string => { export const getAppQGuideCustomURL = (appDetail: AppDetailType | AppSchema): string => {
return ( return (

View File

@@ -50,7 +50,7 @@ export const getGlobalVariableNode = ({
outputs: [] outputs: []
}; };
const globalVariables = getWorkflowGlobalVariables({ nodes, chatConfig, t }); const globalVariables = getWorkflowGlobalVariables({ nodes, chatConfig });
const variableNode: FlowNodeItemType = { const variableNode: FlowNodeItemType = {
nodeId: VARIABLE_NODE_ID, nodeId: VARIABLE_NODE_ID,

View File

@@ -24,7 +24,6 @@ import {
getAppChatConfig, getAppChatConfig,
getGuideModule getGuideModule
} from '@fastgpt/global/core/workflow/utils'; } from '@fastgpt/global/core/workflow/utils';
import { getSystemVariables } from '../app/utils';
import { TFunction } from 'next-i18next'; import { TFunction } from 'next-i18next';
import { import {
FlowNodeInputItemType, FlowNodeInputItemType,
@@ -36,6 +35,7 @@ import { VariableConditionEnum } from '@fastgpt/global/core/workflow/template/sy
import { AppChatConfigType } from '@fastgpt/global/core/app/type'; import { AppChatConfigType } from '@fastgpt/global/core/app/type';
import { cloneDeep, isEqual } from 'lodash'; import { cloneDeep, isEqual } from 'lodash';
import { getInputComponentProps } from '@fastgpt/global/core/workflow/node/io/utils'; import { getInputComponentProps } from '@fastgpt/global/core/workflow/node/io/utils';
import { workflowSystemVariables } from '../app/utils';
export const nodeTemplate2FlowNode = ({ export const nodeTemplate2FlowNode = ({
template, template,
@@ -203,13 +203,11 @@ export const computedNodeInputReference = ({
export const getRefData = ({ export const getRefData = ({
variable, variable,
nodeList, nodeList,
chatConfig, chatConfig
t
}: { }: {
variable?: ReferenceValueProps; variable?: ReferenceValueProps;
nodeList: FlowNodeItemType[]; nodeList: FlowNodeItemType[];
chatConfig: AppChatConfigType; chatConfig: AppChatConfigType;
t: TFunction;
}) => { }) => {
if (!variable) if (!variable)
return { return {
@@ -218,7 +216,7 @@ export const getRefData = ({
}; };
const node = nodeList.find((node) => node.nodeId === variable[0]); const node = nodeList.find((node) => node.nodeId === variable[0]);
const systemVariables = getWorkflowGlobalVariables({ nodes: nodeList, chatConfig, t }); const systemVariables = getWorkflowGlobalVariables({ nodes: nodeList, chatConfig });
if (!node) { if (!node) {
const globalVariable = systemVariables.find((item) => item.key === variable?.[1]); const globalVariable = systemVariables.find((item) => item.key === variable?.[1]);
@@ -362,12 +360,10 @@ export const filterSensitiveNodesData = (nodes: StoreNodeItemType[]) => {
/* get workflowStart output to global variables */ /* get workflowStart output to global variables */
export const getWorkflowGlobalVariables = ({ export const getWorkflowGlobalVariables = ({
nodes, nodes,
chatConfig, chatConfig
t
}: { }: {
nodes: FlowNodeItemType[]; nodes: FlowNodeItemType[];
chatConfig: AppChatConfigType; chatConfig: AppChatConfigType;
t: TFunction;
}): EditorVariablePickerType[] => { }): EditorVariablePickerType[] => {
const globalVariables = formatEditorVariablePickerIcon( const globalVariables = formatEditorVariablePickerIcon(
getAppChatConfig({ getAppChatConfig({
@@ -377,9 +373,7 @@ export const getWorkflowGlobalVariables = ({
})?.variables || [] })?.variables || []
); );
const systemVariables = getSystemVariables(t); return [...globalVariables, ...workflowSystemVariables];
return [...globalVariables, ...systemVariables];
}; };
export type CombinedItemType = Partial<FlowNodeInputItemType> & Partial<FlowNodeOutputItemType>; export type CombinedItemType = Partial<FlowNodeInputItemType> & Partial<FlowNodeOutputItemType>;