Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: 飞书话题群
url: https://oss.laf.run/otnvvf-imgs/1719505774252.jpg
url: https://oss.laf.run/otnvvf-imgs/feishu3.png
about: FastGPT 全是问题群
25 changes: 13 additions & 12 deletions docSite/content/zh-cn/docs/development/upgrading/4811.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,16 @@ weight: 813
6. 新增 - 支持 Openai o1 模型,需增加模型的 `defaultConfig` 配置,覆盖 `temperature`、`max_tokens` 和 `stream`配置,o1 不支持 stream 模式, 详细可重新拉取 `config.json` 配置文件查看。
7. 新增 - AI 对话节点知识库引用,支持配置 role=system 和 role=user,已配置的过自定义提示词的节点将会保持 user 模式,其余用户将转成 system 模式。
8. 新增 - 插件支持上传系统文件。
9. 新增 - 支持工作流嵌套子应用时,可以设置`非流模式`,同时简易模式也可以选择工作流作为插件了,简易模式调用子应用时,都将强制使用非流模式。
10. 新增 - 调试模式下,子应用调用,支持返回详细运行数据。
11. 新增 - 保留所有模式下子应用嵌套调用的日志。
12. 优化 - 工作流嵌套层级限制 20 层,避免因编排不合理导致的无限死循环。
13. 优化 - 工作流 handler 性能优化。
14. 优化 - 工作流快捷键,避免调试测试时也会触发。
15. 优化 - 流输出,切换 tab 时仍可以继续输出。
16. 优化 - 完善外部文件知识库相关 API
17. 修复 - 知识库选择权限问题。
18. 修复 - 空 chatId 发起对话,首轮携带用户选择时会异常。
19. 修复 - createDataset 接口,intro 为赋值。
20. 修复 - 对话框渲染性能问题。
9. 新增 - 插件输出,支持指定字段作为工具响应。
10. 新增 - 支持工作流嵌套子应用时,可以设置`非流模式`,同时简易模式也可以选择工作流作为插件了,简易模式调用子应用时,都将强制使用非流模式。
11. 新增 - 调试模式下,子应用调用,支持返回详细运行数据。
12. 新增 - 保留所有模式下子应用嵌套调用的日志。
13. 优化 - 工作流嵌套层级限制 20 层,避免因编排不合理导致的无限死循环。
14. 优化 - 工作流 handler 性能优化。
15. 优化 - 工作流快捷键,避免调试测试时也会触发。
16. 优化 - 流输出,切换 tab 时仍可以继续输出。
17. 优化 - 完善外部文件知识库相关 API
18. 修复 - 知识库选择权限问题。
19. 修复 - 空 chatId 发起对话,首轮携带用户选择时会异常。
20. 修复 - createDataset 接口,intro 为赋值。
21. 修复 - 对话框渲染性能问题。
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ export const FormInputNode: FlowNodeTemplateType = {
id: NodeOutputKeyEnum.formInputResult,
key: NodeOutputKeyEnum.formInputResult,
required: true,
label: i18nT('app:workflow.form_input_result'),
description: i18nT('app:workflow.form_input_result_tip'),
label: i18nT('workflow:form_input_result'),
description: i18nT('workflow:form_input_result_tip'),
valueType: WorkflowIOValueTypeEnum.object,
type: FlowNodeOutputTypeEnum.static
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
UserInputFormItemType,
UserInputInteractive
} from '@fastgpt/global/core/workflow/template/system/interactive/type';
import { addLog } from '../../../../common/system/log';

type Props = ModuleDispatchProps<{
[NodeInputKeyEnum.description]: string;
Expand Down Expand Up @@ -48,7 +49,8 @@ export const dispatchFormInput = async (props: Props): Promise<FormInputResponse
try {
return JSON.parse(text);
} catch (error) {
return text;
addLog.warn('formInput error', { error });
return {};
}
})();

Expand Down
38 changes: 16 additions & 22 deletions packages/service/core/workflow/dispatch/plugin/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,13 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi

const plugin = await getChildAppRuntimeById(pluginId);

const outputFilterMap = plugin.nodes
.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginOutput)!
.inputs.reduce(
(acc, cur) => {
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;
},
{} as Record<string, boolean>
);
}, {}) ?? {};

const runtimeNodes = storeNodes2RuntimeNodes(
plugin.nodes,
Expand Down Expand Up @@ -125,13 +123,12 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
moduleLogo: plugin.avatar,
totalPoints: usagePoints,
pluginOutput: output?.pluginOutput,
pluginDetail:
pluginData && pluginData.permission.hasWritePer // Not system plugin
? flowResponses.filter((item) => {
const filterArr = [FlowNodeTypeEnum.pluginOutput];
return !filterArr.includes(item.moduleType as any);
})
: undefined
pluginDetail: pluginData?.permission?.hasWritePer // Not system plugin
? flowResponses.filter((item) => {
const filterArr = [FlowNodeTypeEnum.pluginOutput];
return !filterArr.includes(item.moduleType as any);
})
: undefined
},
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [
{
Expand All @@ -143,14 +140,11 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
[DispatchNodeResponseKeyEnum.toolResponses]: output?.pluginOutput
? Object.keys(output.pluginOutput)
.filter((key) => outputFilterMap[key])
.reduce(
(acc, key) => {
acc[key] = output.pluginOutput![key];
return acc;
},
{} as Record<string, any>
)
: {},
.reduce<Record<string, any>>((acc, key) => {
acc[key] = output.pluginOutput![key];
return acc;
}, {})
: null,
...(output ? output.pluginOutput : {})
};
};
2 changes: 0 additions & 2 deletions packages/web/i18n/en/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,6 @@
"workflow.file_url": "Document Link",
"workflow.form_input": "Form input",
"workflow.form_input_description_placeholder": "For example: \nAdd your information",
"workflow.form_input_result": "Full input result",
"workflow.form_input_result_tip": "An object of full result",
"workflow.form_input_tip": " This module can configure multiple inputs to guide users in entering specific content.",
"workflow.input_description_tip": "You can add a description to explain to users what they need to input",
"workflow.read_files": "Document Parsing",
Expand Down
1 change: 0 additions & 1 deletion packages/web/i18n/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,6 @@
"core.chat.response.Read complete response tips": "Click to View Detailed Process",
"core.chat.response.Tool call tokens": "Tool Call Tokens Consumption",
"core.chat.response.context total length": "Total Context Length",
"core.chat.response.form_input_result": "Form input result",
"core.chat.response.loop_input": "Loop Input Array",
"core.chat.response.loop_input_element": "Loop Input Element",
"core.chat.response.loop_output": "Loop Output Array",
Expand Down
8 changes: 7 additions & 1 deletion packages/web/i18n/en/workflow.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
"field_required": "Required",
"field_used_as_tool_input": "Used as Tool Call Parameter",
"filter_description": "Currently supports filtering by tags and creation time. Fill in the format as follows:\n{\n \"tags\": {\n \"$and\": [\"Tag 1\",\"Tag 2\"],\n \"$or\": [\"When there are $and tags, and is effective, or is not effective\"]\n },\n \"createTime\": {\n \"$gte\": \"YYYY-MM-DD HH:mm format, collection creation time greater than this time\",\n \"$lte\": \"YYYY-MM-DD HH:mm format, collection creation time less than this time, can be used with $gte\"\n }\n}",
"form_input_result": "User complete input result",
"form_input_result_tip": "an object containing the complete result",
"full_field_extraction": "Full Field Extraction",
"full_field_extraction_description": "Returns true when all fields are fully extracted (success includes model extraction or using default values)",
"full_response_data": "Full Response Data",
Expand Down Expand Up @@ -79,7 +81,7 @@
"is_equal_to": "Is Equal To",
"is_not_empty": "Is Not Empty",
"is_not_equal": "Is Not Equal",
"is_tool_output": "Whether to use this field as tool output when the tool is called",
"is_tool_output_label": "as tool response",
"judgment_result": "Judgment Result",
"knowledge_base_reference": "Dataset Reference",
"knowledge_base_search_merge": "Dataset Search Merge",
Expand Down Expand Up @@ -112,6 +114,7 @@
"plugin.Instruction_Tip": "You can configure an instruction to explain the purpose of the plugin. This instruction will be displayed each time the plugin is used. Supports standard Markdown syntax.",
"plugin.Instructions": "Instructions",
"plugin_input": "Plugin Input",
"plugin_output_tool": "When the plug-in is executed as a tool, whether this field responds as a result of the tool",
"question_classification": "Question Classification",
"question_optimization": "Question Optimization",
"quote_content_placeholder": "The structure of the reference content can be customized to better suit different scenarios. \nSome variables can be used for template configuration\n\n{{q}} - main content\n\n{{a}} - auxiliary data\n\n{{source}} - source name\n\n{{sourceId}} - source ID\n\n{{index}} - nth reference",
Expand Down Expand Up @@ -157,6 +160,9 @@
"update_link_error": "Error updating link",
"update_specified_node_output_or_global_variable": "Can update the output value of a specified node or update global variables",
"use_user_id": "User ID",
"user_form_input_config": "Form configuration",
"user_form_input_description": "describe",
"user_form_input_name": "Name",
"user_question": "User Question",
"user_question_tool_desc": "User input questions (questions need to be improved)",
"value_type": "Value type",
Expand Down
4 changes: 1 addition & 3 deletions packages/web/i18n/zh/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,6 @@
"workflow.file_url": "文档链接",
"workflow.form_input": "表单输入",
"workflow.form_input_description_placeholder": "例如:\n补充您的信息",
"workflow.form_input_result": "完整输入结果",
"workflow.form_input_result_tip": "一个包含完整结果的对象",
"workflow.form_input_tip": "该模块可以配置多种输入,引导用户输入特定内容。",
"workflow.input_description_tip": "你可以添加一段说明文字,用以向用户说明需要输入的内容",
"workflow.read_files": "文档解析",
Expand All @@ -156,7 +154,7 @@
"workflow.select_description": "说明文字",
"workflow.select_description_placeholder": "例如: \n冰箱里是否有西红柿?",
"workflow.select_description_tip": "你可以添加一段说明文字,用以向用户说明每个选项代表的含义。",
"workflow.select_result": "选择的结果",
"workflow.select_result": "选择结果",
"workflow.template.communication": "通信",
"workflow.user_file_input": "文件链接",
"workflow.user_file_input_desc": "用户上传的文档和图片链接",
Expand Down
1 change: 0 additions & 1 deletion packages/web/i18n/zh/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,6 @@
"core.chat.response.Read complete response tips": "点击查看详细流程",
"core.chat.response.Tool call tokens": "工具调用 tokens 消耗",
"core.chat.response.context total length": "上下文总长度",
"core.chat.response.form_input_result": "表单输入结果",
"core.chat.response.loop_input": "输入数组",
"core.chat.response.loop_input_element": "输入数组元素",
"core.chat.response.loop_output": "输出数组",
Expand Down
8 changes: 7 additions & 1 deletion packages/web/i18n/zh/workflow.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
"field_required": "必填",
"field_used_as_tool_input": "作为工具调用参数",
"filter_description": "目前支持标签和创建时间过滤,需按照以下格式填写:\n{\n \"tags\": {\n \"$and\": [\"标签 1\",\"标签 2\"],\n \"$or\": [\"有 $and 标签时,and 生效,or 不生效\"]\n },\n \"createTime\": {\n \"$gte\": \"YYYY-MM-DD HH:mm 格式即可,集合的创建时间大于该时间\",\n \"$lte\": \"YYYY-MM-DD HH:mm 格式即可,集合的创建时间小于该时间,可和 $gte 共同使用\"\n }\n}",
"form_input_result": "用户完整输入结果",
"form_input_result_tip": "一个包含完整结果的对象",
"full_field_extraction": "字段完全提取",
"full_field_extraction_description": "提取字段全部填充时返回 true (模型提取或使用默认值均属于成功)",
"full_response_data": "完整响应数据",
Expand Down Expand Up @@ -79,7 +81,7 @@
"is_equal_to": "等于",
"is_not_empty": "不为空",
"is_not_equal": "不等于",
"is_tool_output": "是否在工具调用时,将该字段作为工具输出",
"is_tool_output_label": "作为工具响应",
"judgment_result": "判断结果",
"knowledge_base_reference": "知识库引用",
"knowledge_base_search_merge": "知识库搜索引用合并",
Expand Down Expand Up @@ -112,6 +114,7 @@
"plugin.Instruction_Tip": "可以配置一段说明,以解释该插件的用途。每次使用插件前,会显示该段说明。支持标准 Markdown 语法。",
"plugin.Instructions": "使用说明",
"plugin_input": "插件输入",
"plugin_output_tool": "插件作为工具执行时,该字段是否作为工具响应结果",
"question_classification": "问题分类",
"question_optimization": "问题优化",
"quote_content_placeholder": "可以自定义引用内容的结构,以更好的适配不同场景。可以使用一些变量来进行模板配置\n{{q}} - 主要内容\n{{a}} - 辅助数据\n{{source}} - 来源名\n{{sourceId}} - 来源ID\n{{index}} - 第 n 个引用",
Expand Down Expand Up @@ -157,6 +160,9 @@
"update_link_error": "更新链接异常",
"update_specified_node_output_or_global_variable": "可以更新指定节点的输出值或更新全局变量",
"use_user_id": "使用者 ID",
"user_form_input_config": "表单配置",
"user_form_input_description": "描述",
"user_form_input_name": "标题",
"user_question": "用户问题",
"user_question_tool_desc": "用户输入的问题(问题需要完善)",
"value_type": "数据类型",
Expand Down
18 changes: 6 additions & 12 deletions projects/app/src/components/core/chat/components/AIResponseBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -299,18 +299,12 @@ const AIResponseBox = ({ value, isLastResponseValue, isChatting }: props) => {
);
if (value.type === ChatItemValueTypeEnum.tool && value.tools)
return <RenderTool showAnimation={isChatting} tools={value.tools} />;
if (
value.type === ChatItemValueTypeEnum.interactive &&
value.interactive &&
value.interactive.type === 'userSelect'
)
return <RenderUserSelectInteractive interactive={value.interactive} />;
if (
value.type === ChatItemValueTypeEnum.interactive &&
value.interactive &&
value.interactive?.type === 'userInput'
)
return <RenderUserFormInteractive interactive={value.interactive} />;
if (value.type === ChatItemValueTypeEnum.interactive && value.interactive) {
if (value.interactive.type === 'userSelect')
return <RenderUserSelectInteractive interactive={value.interactive} />;
if (value.interactive?.type === 'userInput')
return <RenderUserFormInteractive interactive={value.interactive} />;
}
};

export default React.memo(AIResponseBox);
Original file line number Diff line number Diff line change
Expand Up @@ -354,10 +354,7 @@ export const WholeResponseContent = ({
/>

{/* form input */}
<Row
label={t('common:core.chat.response.form_input_result')}
value={activeModule?.formInputResult}
/>
<Row label={t('workflow:form_input_result')} value={activeModule?.formInputResult} />
</Box>
) : null;
};
Expand Down
18 changes: 13 additions & 5 deletions projects/app/src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,17 @@ import { useInitApp } from '@/web/context/useInitApp';
import { useTranslation } from 'next-i18next';
import '@/web/styles/reset.scss';
import NextHead from '@/components/common/NextHead';
import { useEffect } from 'react';
import { ReactElement, useEffect } from 'react';
import { NextPage } from 'next';

function App({ Component, pageProps }: AppProps) {
type NextPageWithLayout = NextPage & {
setLayout?: (page: ReactElement) => JSX.Element;
};
type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout;
};

function App({ Component, pageProps }: AppPropsWithLayout) {
const { feConfigs, scripts, title } = useInitApp();
const { t } = useTranslation();

Expand All @@ -30,6 +38,8 @@ function App({ Component, pageProps }: AppProps) {
);
}, []);

const setLayout = Component.setLayout || ((page) => <>{page}</>);

return (
<>
<NextHead
Expand All @@ -46,9 +56,7 @@ function App({ Component, pageProps }: AppProps) {
<QueryClientContext>
<I18nContextProvider>
<ChakraUIContext>
<Layout>
<Component {...pageProps} />
</Layout>
<Layout>{setLayout(<Component {...pageProps} />)}</Layout>
</ChakraUIContext>
</I18nContextProvider>
</QueryClientContext>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React, { useCallback, useMemo } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import { BezierEdge, getBezierPath, EdgeLabelRenderer, EdgeProps } from 'reactflow';
import { Box, Flex } from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { NodeOutputKeyEnum, RuntimeEdgeStatusEnum } from '@fastgpt/global/core/workflow/constants';
import { useContextSelector } from 'use-context-selector';
import { WorkflowContext } from '../../context';
import { useThrottleEffect } from 'ahooks';

const ButtonEdge = (props: EdgeProps) => {
const { nodes, nodeList, setEdges, workflowDebugData, hoverEdgeId } = useContextSelector(
Expand All @@ -28,13 +29,11 @@ const ButtonEdge = (props: EdgeProps) => {
style
} = props;

// If parentNode is folded, the edge will not be displayed
const parentNode = useMemo(() => {
for (const node of nodeList) {
if ((node.nodeId === source || node.nodeId === target) && node.parentNodeId) {
return nodeList.find((parent) => parent.nodeId === node.parentNodeId);
}
}
return undefined;
return nodeList.find(
(node) => (node.nodeId === source || node.nodeId === target) && node.parentNodeId
);
}, [nodeList, source, target]);

const defaultZIndex = useMemo(
Expand All @@ -52,12 +51,20 @@ const ButtonEdge = (props: EdgeProps) => {
[setEdges]
);

const highlightEdge = useMemo(() => {
const connectNode = nodes.find((node) => {
return node.selected && (node.id === props.source || node.id === props.target);
});
return !!(connectNode || selected);
}, [nodes, props.source, props.target, selected]);
// Selected edge or source/target node selected
const [highlightEdge, setHighlightEdge] = useState(false);
useThrottleEffect(
() => {
const connectNode = nodes.find((node) => {
return node.selected && (node.id === props.source || node.id === props.target);
});
setHighlightEdge(!!connectNode || !!selected);
},
[nodes, selected, props.source, props.target],
{
wait: 100
}
);

const [, labelX, labelY] = getBezierPath({
sourceX,
Expand Down
Loading
Loading