4.8.8 test fix (#2149)

* perf: code comment

* feat: system plugin input guide

* perf: variable avatar

* feat: feishu webhook

* perf: select tool config tip

* perf: rename variable

* fix: per inherit error

* perf: docker-compose oneapi version and i18n

* perf: ui tip bug

* fix: ts

* perf: pg log

* perf: editor color

* perf: update init
This commit is contained in:
Archer
2024-07-26 10:23:44 +08:00
committed by GitHub
parent 2d016b7462
commit cd554f573e
59 changed files with 1648 additions and 506 deletions

View File

@@ -6,27 +6,29 @@ import { getAppChatConfig } from '../workflow/utils';
import { StoreNodeItemType } from '../workflow/type/node';
import { DatasetSearchModeEnum } from '../dataset/constants';
export const getDefaultAppForm = (): AppSimpleEditFormType => ({
aiSettings: {
model: 'gpt-4o-mini',
systemPrompt: '',
temperature: 0,
isResponseAnswerText: true,
maxHistories: 6,
maxToken: 4000
},
dataset: {
datasets: [],
similarity: 0.4,
limit: 1500,
searchMode: DatasetSearchModeEnum.embedding,
usingReRank: false,
datasetSearchUsingExtensionQuery: false,
datasetSearchExtensionBg: ''
},
selectedTools: [],
chatConfig: {}
});
export const getDefaultAppForm = (): AppSimpleEditFormType => {
return {
aiSettings: {
model: 'gpt-4o-mini',
systemPrompt: '',
temperature: 0,
isResponseAnswerText: true,
maxHistories: 6,
maxToken: 4000
},
dataset: {
datasets: [],
similarity: 0.4,
limit: 1500,
searchMode: DatasetSearchModeEnum.embedding,
usingReRank: false,
datasetSearchUsingExtensionQuery: true,
datasetSearchExtensionBg: ''
},
selectedTools: [],
chatConfig: {}
};
};
/* format app nodes to edit form */
export const appWorkflow2Form = ({

View File

@@ -6,6 +6,7 @@ export enum FlowNodeTemplateTypeEnum {
search = 'search',
multimodal = 'multimodal',
communication = 'communication',
other = 'other',
teamApp = 'teamApp'

View File

@@ -35,6 +35,7 @@ export type WorkflowTemplateType = {
avatar: string;
intro?: string;
author?: string;
inputExplanationUrl?: string;
version: string;
showStatus?: boolean;

View File

@@ -31,6 +31,7 @@ export type FlowNodeCommonType = {
avatar?: string;
name: string;
intro?: string; // template list intro
inputExplanationUrl?: string;
showStatus?: boolean; // chatting response step status
version: string;

View File

@@ -229,7 +229,7 @@ export const updatePluginInputByVariables = (
);
};
export const filterPluginInputVariables = (
export const removePluginInputVariables = (
variables: Record<string, any>,
nodes: RuntimeNodeItemType[]
) => {
@@ -268,6 +268,7 @@ export function replaceVariableLabel({
};
});
// Upstream node outputs
const nodeVariables = nodes
.map((node) => {
return node.outputs.map((output) => {
@@ -280,6 +281,7 @@ export function replaceVariableLabel({
})
.flat();
// Get runningNode inputs(Will be replaced with reference)
const customInputs = runningNode.inputs.flatMap((item) => {
if (Array.isArray(item.value)) {
return [
@@ -299,6 +301,7 @@ export function replaceVariableLabel({
const allVariables = [...globalVariables, ...nodeVariables, ...customInputs];
// Replace {{$xxx.xxx$}} to value
for (const key in allVariables) {
const val = allVariables[key];
const regex = new RegExp(`\\{\\{\\$(${val.nodeId}\\.${val.id})\\$\\}\\}`, 'g');

View File

@@ -7,7 +7,14 @@ import { cloneDeep } from 'lodash';
import { WorkerNameEnum, runWorker } from '@fastgpt/service/worker/utils';
// Run in main thread
const staticPluginList = ['getTime', 'fetchUrl', 'Doc2X', 'Doc2X/URLPDF2text', 'Doc2X/URLImg2text'];
const staticPluginList = [
'getTime',
'fetchUrl',
'Doc2X',
'Doc2X/URLPDF2text',
'Doc2X/URLImg2text',
'feishu'
];
// Run in worker thread (Have npm packages)
const packagePluginList = [
'mathExprVal',

View File

@@ -1,9 +1,10 @@
{
"author": "",
"author": "Menghuan1918",
"version": "488",
"name": "Doc2X 图像(URL)识别",
"avatar": "plugins/doc2x",
"intro": "将传入的图片(URL)发送至Doc2X进行解析返回带LaTeX公式的markdown格式的文本",
"inputExplanationUrl": "https://fael3z0zfze.feishu.cn/wiki/Rkc5witXWiJoi5kORd2cofh6nDg?fromScene=spaceOverview",
"showStatus": true,
"weight": 10,

View File

@@ -1,9 +1,10 @@
{
"author": "",
"author": "Menghuan1918",
"version": "488",
"name": "Doc2X PDF文件(URL)识别",
"avatar": "plugins/doc2x",
"intro": "将传入的PDF文件(URL)发送至Doc2X进行解析返回带LaTeX公式的markdown格式的文本",
"inputExplanationUrl": "https://fael3z0zfze.feishu.cn/wiki/Rkc5witXWiJoi5kORd2cofh6nDg?fromScene=spaceOverview",
"showStatus": true,
"weight": 10,

View File

@@ -1,5 +1,5 @@
{
"author": "",
"author": "Menghuan1918",
"version": "488",
"name": "Doc2X服务",
"avatar": "plugins/doc2x",

View File

@@ -5,7 +5,7 @@
"avatar": "core/workflow/template/duckduckgo",
"intro": "DuckDuckGo 服务,包含网络搜索、图片搜索、新闻搜索等。",
"showStatus": false,
"weight": 10,
"weight": 100,
"isTool": true,
"templateType": "tools",

View File

@@ -0,0 +1,443 @@
{
"author": "",
"version": "488",
"name": "飞书机器人 webhook",
"avatar": "/imgs/app/templates/feishu.svg",
"intro": "向飞书机器人发起 webhook 请求。",
"inputExplanationUrl": "https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot#f62e72d5",
"showStatus": false,
"weight": 10,
"isTool": true,
"templateType": "communication",
"workflow": {
"nodes": [
{
"nodeId": "pluginInput",
"name": "自定义插件输入",
"intro": "可以配置插件需要哪些输入,利用这些输入来运行插件",
"avatar": "core/workflow/template/workflowStart",
"flowNodeType": "pluginInput",
"showStatus": false,
"position": {
"x": 156.37657136084977,
"y": 90.73380846709256
},
"version": "481",
"inputs": [
{
"renderTypeList": ["reference"],
"selectedTypeIndex": 0,
"valueType": "string",
"canEdit": true,
"key": "content",
"label": "content",
"description": "需要发送的消息",
"required": true,
"toolDescription": "需要发送的消息"
},
{
"renderTypeList": ["input"],
"selectedTypeIndex": 0,
"valueType": "string",
"canEdit": true,
"key": "hook_url",
"label": "hook_url",
"description": "飞书机器人地址",
"required": true,
"defaultValue": ""
}
],
"outputs": [
{
"id": "query",
"valueType": "string",
"key": "content",
"label": "content",
"type": "hidden"
},
{
"id": "hook_url",
"valueType": "string",
"key": "hook_url",
"label": "hook_url",
"type": "hidden"
}
]
},
{
"nodeId": "pluginOutput",
"name": "自定义插件输出",
"intro": "自定义配置外部输出,使用插件时,仅暴露自定义配置的输出",
"avatar": "core/workflow/template/pluginOutput",
"flowNodeType": "pluginOutput",
"showStatus": false,
"position": {
"x": 2110.7223589692912,
"y": 120.17602722162474
},
"version": "481",
"inputs": [
{
"renderTypeList": ["reference"],
"valueType": "object",
"canEdit": true,
"key": "result",
"label": "result",
"description": "",
"value": ["vzreK6vHrPvZ", "httpRawResponse"]
}
],
"outputs": []
},
{
"nodeId": "vzreK6vHrPvZ",
"name": "HTTP 请求",
"intro": "可以发出一个 HTTP 请求,实现更为复杂的操作(联网搜索、数据库查询等)",
"avatar": "core/workflow/template/httpRequest",
"flowNodeType": "httpRequest468",
"showStatus": true,
"position": {
"x": 1363.4233257919495,
"y": -182.3490463845037
},
"version": "481",
"inputs": [
{
"key": "system_addInputParam",
"renderTypeList": ["addInputParam"],
"valueType": "dynamic",
"label": "",
"required": false,
"description": "core.module.input.description.HTTP Dynamic Input",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
}
},
{
"key": "system_httpMethod",
"renderTypeList": ["custom"],
"valueType": "string",
"label": "",
"value": "POST",
"required": true
},
{
"key": "system_httpReqUrl",
"renderTypeList": ["hidden"],
"valueType": "string",
"label": "",
"description": "core.module.input.description.Http Request Url",
"placeholder": "https://api.ai.com/getInventory",
"required": false,
"value": "{{url}}"
},
{
"key": "system_httpHeader",
"renderTypeList": ["custom"],
"valueType": "any",
"value": [],
"label": "",
"description": "core.module.input.description.Http Request Header",
"placeholder": "core.module.input.description.Http Request Header",
"required": false
},
{
"key": "system_httpParams",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": [],
"label": "",
"required": false
},
{
"key": "system_httpJsonBody",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": "{{content}}",
"label": "",
"required": false
},
{
"renderTypeList": ["reference"],
"valueType": "string",
"canEdit": true,
"key": "url",
"label": "url",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"required": true,
"value": ["pluginInput", "hook_url"]
},
{
"renderTypeList": ["reference"],
"valueType": "object",
"canEdit": true,
"key": "content",
"label": "content",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"required": true,
"value": ["qcJpBBVtXsGd", "system_rawResponse"]
}
],
"outputs": [
{
"id": "error",
"key": "error",
"label": "请求错误",
"description": "HTTP请求错误信息成功时返回空",
"valueType": "object",
"type": "static"
},
{
"id": "httpRawResponse",
"key": "httpRawResponse",
"label": "原始响应",
"required": true,
"description": "HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。",
"valueType": "any",
"type": "static"
},
{
"id": "system_addOutputParam",
"key": "system_addOutputParam",
"type": "dynamic",
"valueType": "dynamic",
"label": "",
"customFieldConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": false
}
}
]
},
{
"nodeId": "qcJpBBVtXsGd",
"name": "代码运行",
"intro": "执行一段简单的脚本代码,通常用于进行复杂的数据处理。",
"avatar": "core/workflow/template/codeRun",
"flowNodeType": "code",
"showStatus": true,
"position": {
"x": 805.8169457909617,
"y": -159.52218926716316
},
"version": "482",
"inputs": [
{
"key": "system_addInputParam",
"renderTypeList": ["addInputParam"],
"valueType": "dynamic",
"label": "",
"required": false,
"description": "这些变量会作为代码的运行的输入参数",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
}
},
{
"key": "codeType",
"renderTypeList": ["hidden"],
"label": "",
"value": "js"
},
{
"key": "code",
"renderTypeList": ["custom"],
"label": "",
"value": "function main({data1}){\n try{\n const parseData = JSON.parse(data1)\n if(typeof parseData === 'object') {\n return parseData\n }\n return {\n \"msg_type\": \"text\",\n content: {\n \"text\": data1\n }\n }\n } catch(err) {\n return {\n \"msg_type\": \"text\",\n content: {\n \"text\": data1\n }\n }\n }\n}"
},
{
"renderTypeList": ["reference"],
"valueType": "string",
"canEdit": true,
"key": "data1",
"label": "data1",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"required": true,
"value": ["pluginInput", "query"]
}
],
"outputs": [
{
"id": "system_rawResponse",
"key": "system_rawResponse",
"label": "完整响应数据",
"valueType": "object",
"type": "static"
},
{
"id": "error",
"key": "error",
"label": "运行错误",
"description": "代码运行错误信息,成功时返回空",
"valueType": "object",
"type": "static"
},
{
"id": "system_addOutputParam",
"key": "system_addOutputParam",
"type": "dynamic",
"valueType": "dynamic",
"label": "",
"customFieldConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": false
},
"description": "将代码中 return 的对象作为输出,传递给后续的节点。变量名需要对应 return 的 key"
},
{
"id": "qLUQfhG0ILRX",
"type": "dynamic",
"key": "content",
"valueType": "object",
"label": "content"
}
]
}
],
"edges": [
{
"source": "vzreK6vHrPvZ",
"target": "pluginOutput",
"sourceHandle": "vzreK6vHrPvZ-source-right",
"targetHandle": "pluginOutput-target-left"
},
{
"source": "pluginInput",
"target": "qcJpBBVtXsGd",
"sourceHandle": "pluginInput-source-right",
"targetHandle": "qcJpBBVtXsGd-target-left"
},
{
"source": "qcJpBBVtXsGd",
"target": "vzreK6vHrPvZ",
"sourceHandle": "qcJpBBVtXsGd-source-right",
"targetHandle": "vzreK6vHrPvZ-target-left"
}
]
}
}

View File

@@ -47,8 +47,6 @@ const addCommonMiddleware = (schema: mongoose.Schema) => {
if (duration > 1000) {
addLog.warn(`Slow operation ${duration}ms`, warnLogData);
} else if (duration > 3000) {
addLog.error(`Slow operation ${duration}ms`, warnLogData);
}
}
next();

View File

@@ -1,3 +1,4 @@
import { delay } from '@fastgpt/global/common/system/utils';
import { addLog } from '../system/log';
import { connectionMongo } from './index';
import type { Mongoose } from 'mongoose';
@@ -17,9 +18,11 @@ export async function connectMongo(): Promise<Mongoose> {
try {
connectionMongo.set('strictQuery', true);
connectionMongo.connection.on('error', (error) => {
connectionMongo.connection.on('error', async (error) => {
console.log('mongo error', error);
connectionMongo.disconnect();
await connectionMongo.disconnect();
await delay(1000);
connectMongo();
});
connectionMongo.connection.on('disconnected', () => {
console.log('mongo disconnected');
@@ -44,8 +47,10 @@ export async function connectMongo(): Promise<Mongoose> {
console.log('mongo connected');
} catch (error) {
connectionMongo.disconnect();
addLog.error('mongo connect error', error);
await connectionMongo.disconnect();
await delay(1000);
connectMongo();
}
return connectionMongo;

View File

@@ -10,6 +10,7 @@ import {
InsertVectorControllerProps
} from '../controller.d';
import dayjs from 'dayjs';
import { addLog } from '../../system/log';
export class PgVectorCtrl {
constructor() {}
@@ -38,9 +39,9 @@ export class PgVectorCtrl {
`CREATE INDEX CONCURRENTLY IF NOT EXISTS create_time_index ON ${DatasetVectorTableName} USING btree(createtime);`
);
console.log('init pg successful');
addLog.info('init pg successful');
} catch (error) {
console.log('init pg error', error);
addLog.error('init pg error', error);
}
};
insert = async (props: InsertVectorControllerProps): Promise<{ insertId: string }> => {

View File

@@ -82,6 +82,7 @@ export async function getPluginPreviewNode({ id }: { id: string }): Promise<Flow
avatar: plugin.avatar,
name: plugin.name,
intro: plugin.intro,
inputExplanationUrl: plugin.inputExplanationUrl,
showStatus: plugin.showStatus,
isTool: plugin.isTool,
version: plugin.version,

View File

@@ -262,13 +262,14 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
/* Inject data into module input */
function getNodeRunParams(node: RuntimeNodeItemType) {
if (node.flowNodeType === FlowNodeTypeEnum.pluginInput) {
// Format plugin input to object
return node.inputs.reduce<Record<string, any>>((acc, item) => {
acc[item.key] = valueTypeFormat(item.value, item.valueType);
return acc;
}, {});
}
// common nodes
// Dynamic input need to store a key.
const dynamicInput = node.inputs.find(
(item) => item.renderTypeList[0] === FlowNodeInputTypeEnum.addInputParam
);
@@ -281,14 +282,14 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
node.inputs.forEach((input) => {
if (input.key === dynamicInput?.key) return;
// replace {{}} variables
// replace {{xx}} variables
let value = replaceVariable(input.value, variables);
// replace {{$$}} variables
// replace {{$xx.xx$}} variables
value = replaceVariableLabel({
text: value,
nodes: runtimeNodes,
variables: variables,
variables,
runningNode: node
});
@@ -299,12 +300,11 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
variables
});
// concat dynamic inputs
// Dynamic input is stored in the dynamic key
if (input.canEdit && dynamicInput && params[dynamicInput.key]) {
params[dynamicInput.key][input.key] = valueTypeFormat(value, input.valueType);
}
// Not dynamic input
params[input.key] = valueTypeFormat(value, input.valueType);
});

View File

@@ -106,6 +106,7 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
acc[key] = valueTypeFormat(value, WorkflowIOValueTypeEnum.string);
return acc;
}, {});
const requestBody = await (() => {
if (!httpJsonBody) return {};
try {

View File

@@ -89,7 +89,13 @@ export default function Editor({
}, [value, variables, variableLabels]);
return (
<Box position={'relative'} width={'full'} h={`${height}px`} cursor={'text'}>
<Box
position={'relative'}
width={'full'}
h={`${height}px`}
cursor={'text'}
color={'myGray.700'}
>
<LexicalComposer initialConfig={initialConfig} key={key}>
<PlainTextPlugin
contentEditable={

View File

@@ -1,36 +1,40 @@
import { FlowNodeTemplateTypeEnum } from '@fastgpt/global/core/workflow/constants';
import { NodeTemplateListType } from '@fastgpt/global/core/workflow/type/node';
import { TFunction } from 'next-i18next';
import { i18nT } from '../../i18n/utils';
export const workflowNodeTemplateList = (t: TFunction): NodeTemplateListType => [
export const workflowNodeTemplateList = [
{
type: FlowNodeTemplateTypeEnum.systemInput,
label: t('common:core.module.template.System input module'),
label: i18nT('common:core.module.template.System input module'),
list: []
},
{
type: FlowNodeTemplateTypeEnum.ai,
label: t('common:core.module.template.AI function'),
list: []
},
{
type: FlowNodeTemplateTypeEnum.tools,
label: t('common:core.module.template.Tool module'),
label: i18nT('common:core.module.template.AI function'),
list: []
},
{
type: FlowNodeTemplateTypeEnum.search,
label: t('core.workflow.template.Search'),
label: i18nT('common:core.workflow.template.Search'),
list: []
},
{
type: FlowNodeTemplateTypeEnum.multimodal,
label: t('core.workflow.template.Multimodal'),
label: i18nT('common:core.workflow.template.Multimodal'),
list: []
},
{
type: FlowNodeTemplateTypeEnum.tools,
label: i18nT('common:core.module.template.Tool module'),
list: []
},
{
type: FlowNodeTemplateTypeEnum.communication,
label: i18nT('app:workflow.template.communication'),
list: []
},
{
type: FlowNodeTemplateTypeEnum.other,
label: t('common:common.Other'),
label: i18nT('common:common.Other'),
list: []
},
{

View File

@@ -1,6 +1,7 @@
{
"Run": "Run",
"ai_settings": "AI Settings",
"all_apps": "All Apps",
"app": {
"modules": {
"click to update": "click to update",
@@ -52,6 +53,7 @@
"template": {
"simple_robot": "Simple Robot"
},
"tool_input_param_tip": "Configure related information before the plugin runs properly",
"transition_to_workflow": "Transition to workflow",
"transition_to_workflow_create_new_placeholder": "Create a new application instead of modifying the current one",
"transition_to_workflow_create_new_tip": "After converting to workflow, it will not be able to convert back to simple mode, please confirm!",
@@ -71,5 +73,11 @@
},
"version": {
"Revert success": "Revert success"
},
"workflow": {
"Input guide": "Input guide",
"template": {
"communication": "Communication"
}
}
}

View File

@@ -1,6 +1,5 @@
{
"App": "App",
"all_apps": "All Apps",
"click_to_resume": "Resume",
"code_editor": "Code edit",
"Export": "Export",

View File

@@ -1,6 +1,7 @@
{
"Run": "运行",
"ai_settings": "AI 配置",
"all_apps": "全部应用",
"app": {
"modules": {
"click to update": "点击更新",
@@ -52,6 +53,7 @@
"template": {
"simple_robot": "简易机器人"
},
"tool_input_param_tip": "该插件正常运行需要配置相关信息",
"transition_to_workflow": "转成工作流",
"transition_to_workflow_create_new_placeholder": "创建一个新的应用,而不是修改当前应用",
"transition_to_workflow_create_new_tip": "转化成工作流后,将无法转化回简易模式,请确认!",
@@ -71,5 +73,11 @@
},
"version": {
"Revert success": "回滚成功"
},
"workflow": {
"Input guide": "填写说明",
"template": {
"communication": "通信"
}
}
}

View File

@@ -1,6 +1,5 @@
{
"App": "应用",
"all_apps": "全部应用",
"click_to_resume": "点击恢复",
"code_editor": "代码编辑",
"Export": "导出",