* feat: stop toolCall and rename some field. (#46) * perf: node delete tip;pay tip * fix: toolCall cannot save child answer * feat: stop tool * fix: team modal * fix feckbackMoal auth bug (#47) * 简单的支持提示词运行tool。优化workflow模板 (#49) * remove templates * fix: request body undefined * feat: prompt tool run * feat: workflow tamplates modal * perf: plugin start * 4.7 (#50) * fix docker-compose download url (#994) original code is a bad url with '404 NOT FOUND' return. fix docker-compose download url, add 'v' before docker-compose version * Update ai_settings.md (#1000) * Update configuration.md * Update configuration.md * Fix history in classifyQuestion and extract modules (#1012) * Fix history in classifyQuestion and extract modules * Add chatValue2RuntimePrompt import and update text formatting * flow controller to packages * fix: rerank select * modal ui * perf: modal code path * point not sufficient * feat: http url support variable * fix http key * perf: prompt * perf: ai setting modal * simple edit ui --------- Co-authored-by: entorick <entorick11@qq.com> Co-authored-by: liujianglc <liujianglc@163.com> Co-authored-by: Fengrui Liu <liufengrui.work@bytedance.com> * fix team share redirect to login (#51) * feat: support openapi import plugins (#48) * feat: support openapi import plugins * feat: import from url * fix: add body params parse * fix build * fix * fix * fix * tool box ui (#52) * fix: training queue * feat: simple edit tool select * perf: simple edit dataset prompt * fix: chatbox tool ux * feat: quote prompt module * perf: plugin tools sign * perf: model avatar * tool selector ui * feat: max histories * perf: http plugin import (#53) * perf: plugin http import * chatBox ui * perf: name * fix: Node template card (#54) * fix: ts * setting modal * package * package * feat: add plugins search (#57) * feat: add plugins search * perf: change http plugin header input * Yjl (#56) * perf: prompt tool call * perf: chat box ux * doc * doc * price tip * perf: tool selector * ui' * fix: vector queue * fix: empty tool and empty response * fix: empty msg * perf: pg index * perf: ui tip * doc * tool tip --------- Co-authored-by: yst <77910600+yu-and-liu@users.noreply.github.com> Co-authored-by: entorick <entorick11@qq.com> Co-authored-by: liujianglc <liujianglc@163.com> Co-authored-by: Fengrui Liu <liufengrui.work@bytedance.com> Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com>
517 lines
15 KiB
TypeScript
517 lines
15 KiB
TypeScript
import { getNanoid } from '../../../common/string/tools';
|
|
import { OpenApiJsonSchema } from './type';
|
|
import yaml from 'js-yaml';
|
|
import { OpenAPIV3 } from 'openapi-types';
|
|
import { PluginTypeEnum } from '../constants';
|
|
import { FlowNodeInputItemType, FlowNodeOutputItemType } from '../../module/node/type';
|
|
import { FlowNodeInputTypeEnum, FlowNodeOutputTypeEnum } from '../../module/node/constant';
|
|
import { ModuleIOValueTypeEnum } from '../../module/constants';
|
|
import { PluginInputModule } from '../../module/template/system/pluginInput';
|
|
import { PluginOutputModule } from '../../module/template/system/pluginOutput';
|
|
import { HttpModule468 } from '../../module/template/system/http468';
|
|
import { HttpParamAndHeaderItemType } from '../../module/api';
|
|
import { CreateOnePluginParams } from '../controller';
|
|
import { ModuleItemType } from '../../module/type';
|
|
import { HttpImgUrl } from '../../../common/file/image/constants';
|
|
|
|
export const str2OpenApiSchema = (yamlStr = ''): OpenApiJsonSchema => {
|
|
try {
|
|
const data: OpenAPIV3.Document = (() => {
|
|
try {
|
|
return JSON.parse(yamlStr);
|
|
} catch (jsonError) {
|
|
return yaml.load(yamlStr, { schema: yaml.FAILSAFE_SCHEMA });
|
|
}
|
|
})();
|
|
|
|
const serverPath = data.servers?.[0].url || '';
|
|
const pathData = Object.keys(data.paths)
|
|
.map((path) => {
|
|
const methodData: any = data.paths[path];
|
|
return Object.keys(methodData)
|
|
.filter((method) =>
|
|
['get', 'post', 'put', 'delete', 'patch'].includes(method.toLocaleLowerCase())
|
|
)
|
|
.map((method) => {
|
|
const methodInfo = methodData[method];
|
|
if (methodInfo.deprecated) return;
|
|
const result = {
|
|
path,
|
|
method,
|
|
name: methodInfo.operationId || path,
|
|
description: methodInfo.description,
|
|
params: methodInfo.parameters,
|
|
request: methodInfo?.requestBody
|
|
};
|
|
return result;
|
|
});
|
|
})
|
|
.flat()
|
|
.filter(Boolean) as OpenApiJsonSchema['pathData'];
|
|
|
|
return { pathData, serverPath };
|
|
} catch (err) {
|
|
throw new Error('Invalid Schema');
|
|
}
|
|
};
|
|
|
|
export const httpApiSchema2Plugins = ({
|
|
parentId,
|
|
apiSchemaStr = '',
|
|
customHeader = ''
|
|
}: {
|
|
parentId: string;
|
|
apiSchemaStr?: string;
|
|
customHeader?: string;
|
|
}): CreateOnePluginParams[] => {
|
|
const jsonSchema = str2OpenApiSchema(apiSchemaStr);
|
|
const baseUrl = jsonSchema.serverPath;
|
|
|
|
return jsonSchema.pathData.map((item) => {
|
|
const pluginOutputId = getNanoid();
|
|
const httpId = getNanoid();
|
|
const pluginOutputKey = 'result';
|
|
|
|
const properties = item.request?.content?.['application/json']?.schema?.properties;
|
|
const propsKeys = properties ? Object.keys(properties) : [];
|
|
|
|
const pluginInputs: FlowNodeInputItemType[] = [
|
|
...(item.params?.map((param: any) => {
|
|
return {
|
|
key: param.name,
|
|
valueType: ModuleIOValueTypeEnum.string,
|
|
label: param.name,
|
|
type: FlowNodeInputTypeEnum.target,
|
|
required: param.required,
|
|
description: param.description,
|
|
edit: true,
|
|
editField: {
|
|
key: true,
|
|
name: true,
|
|
description: true,
|
|
required: true,
|
|
dataType: true,
|
|
inputType: true,
|
|
isToolInput: true
|
|
},
|
|
connected: true,
|
|
toolDescription: param.description
|
|
};
|
|
}) || []),
|
|
...(propsKeys?.map((key) => {
|
|
const prop = properties[key];
|
|
return {
|
|
key,
|
|
valueType: ModuleIOValueTypeEnum.string,
|
|
label: key,
|
|
type: FlowNodeInputTypeEnum.target,
|
|
required: false,
|
|
description: prop.description,
|
|
edit: true,
|
|
editField: {
|
|
key: true,
|
|
name: true,
|
|
description: true,
|
|
required: true,
|
|
dataType: true,
|
|
inputType: true,
|
|
isToolInput: true
|
|
},
|
|
connected: true,
|
|
toolDescription: prop.description
|
|
};
|
|
}) || [])
|
|
];
|
|
|
|
const pluginOutputs: FlowNodeOutputItemType[] = [
|
|
...(item.params?.map((param: any) => {
|
|
return {
|
|
key: param.name,
|
|
valueType: ModuleIOValueTypeEnum.string,
|
|
label: param.name,
|
|
type: FlowNodeOutputTypeEnum.source,
|
|
edit: true,
|
|
targets: [
|
|
{
|
|
moduleId: httpId,
|
|
key: param.name
|
|
}
|
|
]
|
|
};
|
|
}) || []),
|
|
...(propsKeys?.map((key) => {
|
|
return {
|
|
key,
|
|
valueType: ModuleIOValueTypeEnum.string,
|
|
label: key,
|
|
type: FlowNodeOutputTypeEnum.source,
|
|
edit: true,
|
|
targets: [
|
|
{
|
|
moduleId: httpId,
|
|
key
|
|
}
|
|
]
|
|
};
|
|
}) || [])
|
|
];
|
|
|
|
const httpInputs: FlowNodeInputItemType[] = [
|
|
...(item.params?.map((param: any) => {
|
|
return {
|
|
key: param.name,
|
|
valueType: ModuleIOValueTypeEnum.string,
|
|
label: param.name,
|
|
type: FlowNodeInputTypeEnum.target,
|
|
description: param.description,
|
|
edit: true,
|
|
editField: {
|
|
key: true,
|
|
description: true,
|
|
dataType: true
|
|
},
|
|
connected: true
|
|
};
|
|
}) || []),
|
|
...(propsKeys?.map((key) => {
|
|
const prop = properties[key];
|
|
return {
|
|
key,
|
|
valueType: ModuleIOValueTypeEnum.string,
|
|
label: key,
|
|
type: FlowNodeInputTypeEnum.target,
|
|
description: prop.description,
|
|
edit: true,
|
|
editField: {
|
|
key: true,
|
|
description: true,
|
|
dataType: true
|
|
},
|
|
connected: true
|
|
};
|
|
}) || [])
|
|
];
|
|
|
|
/* http node setting */
|
|
const httpNodeParams: HttpParamAndHeaderItemType[] = [];
|
|
const httpNodeHeaders: HttpParamAndHeaderItemType[] = [];
|
|
let httpNodeBody = '{}';
|
|
const requestUrl = `${baseUrl}${item.path}`;
|
|
|
|
if (item.params && item.params.length > 0) {
|
|
for (const param of item.params) {
|
|
if (param.in === 'header') {
|
|
httpNodeHeaders.push({
|
|
key: param.name,
|
|
type: param.schema?.type || ModuleIOValueTypeEnum.string,
|
|
value: `{{${param.name}}}`
|
|
});
|
|
} else if (param.in === 'body') {
|
|
httpNodeBody = JSON.stringify(
|
|
{ ...JSON.parse(httpNodeBody), [param.name]: `{{${param.name}}}` },
|
|
null,
|
|
2
|
|
);
|
|
} else if (param.in === 'query') {
|
|
httpNodeParams.push({
|
|
key: param.name,
|
|
type: param.schema?.type || ModuleIOValueTypeEnum.string,
|
|
value: `{{${param.name}}}`
|
|
});
|
|
}
|
|
}
|
|
}
|
|
if (item.request) {
|
|
const properties = item.request?.content?.['application/json']?.schema?.properties;
|
|
const keys = Object.keys(properties);
|
|
if (keys.length > 0) {
|
|
httpNodeBody = JSON.stringify(
|
|
keys.reduce((acc: any, key) => {
|
|
acc[key] = `{{${key}}}`;
|
|
return acc;
|
|
}, {}),
|
|
null,
|
|
2
|
|
);
|
|
}
|
|
}
|
|
if (customHeader) {
|
|
const headersObj = (() => {
|
|
try {
|
|
return JSON.parse(customHeader) as Record<string, string>;
|
|
} catch (err) {
|
|
return {};
|
|
}
|
|
})();
|
|
for (const key in headersObj) {
|
|
httpNodeHeaders.push({
|
|
key,
|
|
type: 'string',
|
|
// @ts-ignore
|
|
value: headersObj[key]
|
|
});
|
|
}
|
|
}
|
|
|
|
/* Combine complete modules */
|
|
const modules: ModuleItemType[] = [
|
|
{
|
|
moduleId: getNanoid(),
|
|
name: PluginInputModule.name,
|
|
intro: PluginInputModule.intro,
|
|
avatar: PluginInputModule.avatar,
|
|
flowType: PluginInputModule.flowType,
|
|
showStatus: PluginInputModule.showStatus,
|
|
position: {
|
|
x: 616.4226348688949,
|
|
y: -165.05298493910115
|
|
},
|
|
inputs: [
|
|
{
|
|
key: 'pluginStart',
|
|
type: 'hidden',
|
|
valueType: 'boolean',
|
|
label: '插件开始运行',
|
|
description:
|
|
'插件开始运行时,会输出一个 True 的标识。有时候,插件不会有额外的的输入,为了顺利的进入下一个阶段,你可以将该值连接到下一个节点的触发器中。',
|
|
showTargetInApp: true,
|
|
showTargetInPlugin: true,
|
|
connected: true
|
|
},
|
|
...pluginInputs
|
|
],
|
|
outputs: [
|
|
{
|
|
key: 'pluginStart',
|
|
label: '插件开始运行',
|
|
type: 'source',
|
|
valueType: 'boolean',
|
|
targets:
|
|
pluginOutputs.length === 0
|
|
? [
|
|
{
|
|
moduleId: httpId,
|
|
key: 'switch'
|
|
}
|
|
]
|
|
: []
|
|
},
|
|
...pluginOutputs
|
|
]
|
|
},
|
|
{
|
|
moduleId: pluginOutputId,
|
|
name: PluginOutputModule.name,
|
|
intro: PluginOutputModule.intro,
|
|
avatar: PluginOutputModule.avatar,
|
|
flowType: PluginOutputModule.flowType,
|
|
showStatus: PluginOutputModule.showStatus,
|
|
position: {
|
|
x: 1607.7142331269126,
|
|
y: -151.8669210746189
|
|
},
|
|
inputs: [
|
|
{
|
|
key: pluginOutputKey,
|
|
valueType: 'string',
|
|
label: pluginOutputKey,
|
|
type: 'target',
|
|
required: true,
|
|
description: '',
|
|
edit: true,
|
|
editField: {
|
|
key: true,
|
|
name: true,
|
|
description: true,
|
|
required: false,
|
|
dataType: true,
|
|
inputType: false
|
|
},
|
|
connected: true
|
|
}
|
|
],
|
|
outputs: [
|
|
{
|
|
key: pluginOutputKey,
|
|
valueType: 'string',
|
|
label: pluginOutputKey,
|
|
type: 'source',
|
|
edit: true,
|
|
targets: []
|
|
}
|
|
]
|
|
},
|
|
{
|
|
moduleId: httpId,
|
|
name: HttpModule468.name,
|
|
intro: HttpModule468.intro,
|
|
avatar: HttpModule468.avatar,
|
|
flowType: HttpModule468.flowType,
|
|
showStatus: true,
|
|
position: {
|
|
x: 1042.549746602742,
|
|
y: -447.77496332641647
|
|
},
|
|
inputs: [
|
|
{
|
|
key: 'switch',
|
|
type: 'target',
|
|
label: 'core.module.input.label.switch',
|
|
description: 'core.module.input.description.Trigger',
|
|
valueType: 'any',
|
|
showTargetInApp: true,
|
|
showTargetInPlugin: true,
|
|
connected: false
|
|
},
|
|
{
|
|
key: 'system_httpMethod',
|
|
type: 'custom',
|
|
valueType: 'string',
|
|
label: '',
|
|
value: item.method.toUpperCase(),
|
|
required: true,
|
|
showTargetInApp: false,
|
|
showTargetInPlugin: false,
|
|
connected: false
|
|
},
|
|
{
|
|
key: 'system_httpReqUrl',
|
|
type: 'hidden',
|
|
valueType: 'string',
|
|
label: '',
|
|
description: 'core.module.input.description.Http Request Url',
|
|
placeholder: 'https://api.ai.com/getInventory',
|
|
required: false,
|
|
showTargetInApp: false,
|
|
showTargetInPlugin: false,
|
|
value: requestUrl,
|
|
connected: false
|
|
},
|
|
{
|
|
key: 'system_httpHeader',
|
|
type: 'custom',
|
|
valueType: 'any',
|
|
value: httpNodeHeaders,
|
|
label: '',
|
|
description: 'core.module.input.description.Http Request Header',
|
|
placeholder: 'core.module.input.description.Http Request Header',
|
|
required: false,
|
|
showTargetInApp: false,
|
|
showTargetInPlugin: false,
|
|
connected: false
|
|
},
|
|
{
|
|
key: 'system_httpParams',
|
|
type: 'hidden',
|
|
valueType: 'any',
|
|
value: httpNodeParams,
|
|
label: '',
|
|
required: false,
|
|
showTargetInApp: false,
|
|
showTargetInPlugin: false,
|
|
connected: false
|
|
},
|
|
{
|
|
key: 'system_httpJsonBody',
|
|
type: 'hidden',
|
|
valueType: 'any',
|
|
value: httpNodeBody,
|
|
label: '',
|
|
required: false,
|
|
showTargetInApp: false,
|
|
showTargetInPlugin: false,
|
|
connected: false
|
|
},
|
|
{
|
|
key: 'DYNAMIC_INPUT_KEY',
|
|
type: 'target',
|
|
valueType: 'any',
|
|
label: 'core.module.inputType.dynamicTargetInput',
|
|
description: 'core.module.input.description.dynamic input',
|
|
required: false,
|
|
showTargetInApp: false,
|
|
showTargetInPlugin: true,
|
|
hideInApp: true,
|
|
connected: false
|
|
},
|
|
{
|
|
key: 'system_addInputParam',
|
|
type: 'addInputParam',
|
|
valueType: 'any',
|
|
label: '',
|
|
required: false,
|
|
showTargetInApp: false,
|
|
showTargetInPlugin: false,
|
|
editField: {
|
|
key: true,
|
|
description: true,
|
|
dataType: true
|
|
},
|
|
defaultEditField: {
|
|
label: '',
|
|
key: '',
|
|
description: '',
|
|
inputType: 'target',
|
|
valueType: 'string'
|
|
},
|
|
connected: false
|
|
},
|
|
...httpInputs
|
|
],
|
|
outputs: [
|
|
{
|
|
key: 'finish',
|
|
label: 'core.module.output.label.running done',
|
|
description: 'core.module.output.description.running done',
|
|
valueType: 'boolean',
|
|
type: 'source',
|
|
targets: []
|
|
},
|
|
{
|
|
key: 'httpRawResponse',
|
|
label: '原始响应',
|
|
description: 'HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。',
|
|
valueType: 'any',
|
|
type: 'source',
|
|
targets: [
|
|
{
|
|
moduleId: pluginOutputId,
|
|
key: pluginOutputKey
|
|
}
|
|
]
|
|
},
|
|
{
|
|
key: 'system_addOutputParam',
|
|
type: 'addOutputParam',
|
|
valueType: 'any',
|
|
label: '',
|
|
targets: [],
|
|
editField: {
|
|
key: true,
|
|
description: true,
|
|
dataType: true,
|
|
defaultValue: true
|
|
},
|
|
defaultEditField: {
|
|
label: '',
|
|
key: '',
|
|
description: '',
|
|
outputType: 'source',
|
|
valueType: 'string'
|
|
}
|
|
}
|
|
]
|
|
}
|
|
];
|
|
|
|
return {
|
|
name: item.name,
|
|
avatar: HttpImgUrl,
|
|
intro: item.description,
|
|
parentId,
|
|
type: PluginTypeEnum.http,
|
|
modules
|
|
};
|
|
});
|
|
};
|