mirror of
https://github.com/labring/FastGPT.git
synced 2025-10-15 07:31:19 +00:00
add manual create http toolset (#5743)
* add manual create http toolset * optimize code * optimize * fix * fix
This commit is contained in:
@@ -13,9 +13,9 @@ import { i18nT } from '../../../../web/i18n/utils';
|
||||
export const getHTTPToolSetRuntimeNode = ({
|
||||
name,
|
||||
avatar,
|
||||
baseUrl = '',
|
||||
customHeaders = '',
|
||||
apiSchemaStr = '',
|
||||
baseUrl,
|
||||
customHeaders,
|
||||
apiSchemaStr,
|
||||
toolList = [],
|
||||
headerSecret
|
||||
}: {
|
||||
@@ -34,12 +34,11 @@ export const getHTTPToolSetRuntimeNode = ({
|
||||
intro: 'HTTP Tools',
|
||||
toolConfig: {
|
||||
httpToolSet: {
|
||||
baseUrl,
|
||||
toolList,
|
||||
headerSecret,
|
||||
customHeaders,
|
||||
apiSchemaStr,
|
||||
toolId: ''
|
||||
...(baseUrl !== undefined && { baseUrl }),
|
||||
...(apiSchemaStr !== undefined && { apiSchemaStr }),
|
||||
...(customHeaders !== undefined && { customHeaders }),
|
||||
...(headerSecret !== undefined && { headerSecret })
|
||||
}
|
||||
},
|
||||
inputs: [],
|
||||
|
11
packages/global/core/app/type.d.ts
vendored
11
packages/global/core/app/type.d.ts
vendored
@@ -2,6 +2,7 @@ import type { FlowNodeTemplateType, StoreNodeItemType } from '../workflow/type/n
|
||||
import type { AppTypeEnum } from './constants';
|
||||
import { PermissionTypeEnum } from '../../support/permission/constant';
|
||||
import type {
|
||||
ContentTypes,
|
||||
NodeInputKeyEnum,
|
||||
VariableInputEnum,
|
||||
WorkflowIOValueTypeEnum
|
||||
@@ -127,6 +128,16 @@ export type HttpToolConfigType = {
|
||||
outputSchema: JSONSchemaOutputType;
|
||||
path: string;
|
||||
method: string;
|
||||
|
||||
// manual
|
||||
staticParams?: Array<{ key: string; value: string }>;
|
||||
staticHeaders?: Array<{ key: string; value: string }>;
|
||||
staticBody?: {
|
||||
type: ContentTypes;
|
||||
content?: string;
|
||||
formData?: Array<{ key: string; value: string }>;
|
||||
};
|
||||
headerSecret?: StoreSecretValueType;
|
||||
};
|
||||
|
||||
/* app chat config type */
|
||||
|
@@ -477,6 +477,19 @@ export enum ContentTypes {
|
||||
raw = 'raw-text'
|
||||
}
|
||||
|
||||
export const contentTypeMap = {
|
||||
[ContentTypes.none]: '',
|
||||
[ContentTypes.formData]: '',
|
||||
[ContentTypes.xWwwFormUrlencoded]: 'application/x-www-form-urlencoded',
|
||||
[ContentTypes.json]: 'application/json',
|
||||
[ContentTypes.xml]: 'application/xml',
|
||||
[ContentTypes.raw]: 'text/plain'
|
||||
};
|
||||
|
||||
// http request methods
|
||||
export const HTTP_METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'] as const;
|
||||
export type HttpMethod = (typeof HTTP_METHODS)[number];
|
||||
|
||||
export const ArrayTypeMap: Record<WorkflowIOValueTypeEnum, WorkflowIOValueTypeEnum> = {
|
||||
[WorkflowIOValueTypeEnum.string]: WorkflowIOValueTypeEnum.arrayString,
|
||||
[WorkflowIOValueTypeEnum.number]: WorkflowIOValueTypeEnum.arrayNumber,
|
||||
|
7
packages/global/core/workflow/type/node.d.ts
vendored
7
packages/global/core/workflow/type/node.d.ts
vendored
@@ -52,11 +52,10 @@ export type NodeToolConfigType = {
|
||||
}[];
|
||||
};
|
||||
httpToolSet?: {
|
||||
toolId: string;
|
||||
baseUrl: string;
|
||||
toolList: HttpToolConfigType[];
|
||||
apiSchemaStr: string;
|
||||
customHeaders: string;
|
||||
baseUrl?: string;
|
||||
apiSchemaStr?: string;
|
||||
customHeaders?: string;
|
||||
headerSecret?: StoreSecretValueType;
|
||||
};
|
||||
httpTool?: {
|
||||
|
@@ -3,6 +3,8 @@ import { getSecretValue } from '../../common/secret/utils';
|
||||
import axios from 'axios';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import type { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
|
||||
import type { HttpToolConfigType } from '@fastgpt/global/core/app/type';
|
||||
import { contentTypeMap, ContentTypes } from '@fastgpt/global/core/workflow/constants';
|
||||
|
||||
export type RunHTTPToolParams = {
|
||||
baseUrl: string;
|
||||
@@ -11,6 +13,9 @@ export type RunHTTPToolParams = {
|
||||
params: Record<string, any>;
|
||||
headerSecret?: StoreSecretValueType;
|
||||
customHeaders?: Record<string, string>;
|
||||
staticParams?: HttpToolConfigType['staticParams'];
|
||||
staticHeaders?: HttpToolConfigType['staticHeaders'];
|
||||
staticBody?: HttpToolConfigType['staticBody'];
|
||||
};
|
||||
|
||||
export type RunHTTPToolResult = RequireOnlyOne<{
|
||||
@@ -18,41 +23,130 @@ export type RunHTTPToolResult = RequireOnlyOne<{
|
||||
errorMsg?: string;
|
||||
}>;
|
||||
|
||||
export async function runHTTPTool({
|
||||
const buildHttpRequest = ({
|
||||
method,
|
||||
params,
|
||||
headerSecret,
|
||||
customHeaders,
|
||||
staticParams,
|
||||
staticHeaders,
|
||||
staticBody
|
||||
}: Omit<RunHTTPToolParams, 'baseUrl' | 'toolPath'>) => {
|
||||
const body = (() => {
|
||||
if (!staticBody || staticBody.type === ContentTypes.none) {
|
||||
return ['POST', 'PUT', 'PATCH'].includes(method.toUpperCase()) ? params : undefined;
|
||||
}
|
||||
|
||||
if (staticBody.type === ContentTypes.json) {
|
||||
const staticContent = staticBody.content ? JSON.parse(staticBody.content) : {};
|
||||
return { ...staticContent, ...params };
|
||||
}
|
||||
|
||||
if (staticBody.type === ContentTypes.formData) {
|
||||
const formData = new (require('form-data'))();
|
||||
staticBody.formData?.forEach(({ key, value }) => {
|
||||
formData.append(key, value);
|
||||
});
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
formData.append(key, value);
|
||||
});
|
||||
return formData;
|
||||
}
|
||||
|
||||
if (staticBody.type === ContentTypes.xWwwFormUrlencoded) {
|
||||
const urlencoded = new URLSearchParams();
|
||||
staticBody.formData?.forEach(({ key, value }) => {
|
||||
urlencoded.append(key, value);
|
||||
});
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
urlencoded.append(key, String(value));
|
||||
});
|
||||
return urlencoded.toString();
|
||||
}
|
||||
|
||||
if (staticBody.type === ContentTypes.xml || staticBody.type === ContentTypes.raw) {
|
||||
return staticBody.content || '';
|
||||
}
|
||||
|
||||
return undefined;
|
||||
})();
|
||||
|
||||
const contentType = contentTypeMap[staticBody?.type || ContentTypes.none];
|
||||
const headers = {
|
||||
...(contentType && { 'Content-Type': contentType }),
|
||||
...(customHeaders || {}),
|
||||
...(headerSecret ? getSecretValue({ storeSecret: headerSecret }) : {}),
|
||||
...(staticHeaders?.reduce(
|
||||
(acc, { key, value }) => {
|
||||
acc[key] = value;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, string>
|
||||
) || {})
|
||||
};
|
||||
|
||||
const queryParams = (() => {
|
||||
const staticParamsObj =
|
||||
staticParams?.reduce(
|
||||
(acc, { key, value }) => {
|
||||
acc[key] = value;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, any>
|
||||
) || {};
|
||||
|
||||
const mergedParams =
|
||||
method.toUpperCase() === 'GET' || staticParams
|
||||
? { ...staticParamsObj, ...params }
|
||||
: staticParamsObj;
|
||||
|
||||
return Object.keys(mergedParams).length > 0 ? mergedParams : undefined;
|
||||
})();
|
||||
|
||||
return {
|
||||
headers,
|
||||
body,
|
||||
queryParams
|
||||
};
|
||||
};
|
||||
|
||||
export const runHTTPTool = async ({
|
||||
baseUrl,
|
||||
toolPath,
|
||||
method = 'POST',
|
||||
params,
|
||||
headerSecret,
|
||||
customHeaders
|
||||
}: RunHTTPToolParams): Promise<RunHTTPToolResult> {
|
||||
customHeaders,
|
||||
staticParams,
|
||||
staticHeaders,
|
||||
staticBody
|
||||
}: RunHTTPToolParams): Promise<RunHTTPToolResult> => {
|
||||
try {
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
...(customHeaders || {}),
|
||||
...(headerSecret ? getSecretValue({ storeSecret: headerSecret }) : {})
|
||||
};
|
||||
const { headers, body, queryParams } = buildHttpRequest({
|
||||
method,
|
||||
params,
|
||||
headerSecret,
|
||||
customHeaders,
|
||||
staticParams,
|
||||
staticHeaders,
|
||||
staticBody
|
||||
});
|
||||
|
||||
const { data } = await axios({
|
||||
method: method.toUpperCase(),
|
||||
baseURL: baseUrl.startsWith('https://') ? baseUrl : `https://${baseUrl}`,
|
||||
url: toolPath,
|
||||
headers,
|
||||
data: params,
|
||||
params,
|
||||
data: body,
|
||||
params: queryParams,
|
||||
timeout: 300000,
|
||||
httpsAgent: new (require('https').Agent)({
|
||||
rejectUnauthorized: false
|
||||
})
|
||||
});
|
||||
|
||||
return {
|
||||
data
|
||||
};
|
||||
return { data };
|
||||
} catch (error: any) {
|
||||
console.log(error);
|
||||
return {
|
||||
errorMsg: getErrText(error)
|
||||
};
|
||||
return { errorMsg: getErrText(error) };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@@ -236,16 +236,19 @@ export const dispatchRunTool = async (props: RunToolProps): Promise<RunToolRespo
|
||||
}
|
||||
|
||||
const { data, errorMsg } = await runHTTPTool({
|
||||
baseUrl: baseUrl,
|
||||
baseUrl: baseUrl || '',
|
||||
toolPath: httpTool.path,
|
||||
method: httpTool.method,
|
||||
params,
|
||||
headerSecret,
|
||||
headerSecret: httpTool.headerSecret || headerSecret,
|
||||
customHeaders: customHeaders
|
||||
? typeof customHeaders === 'string'
|
||||
? JSON.parse(customHeaders)
|
||||
: customHeaders
|
||||
: undefined
|
||||
: undefined,
|
||||
staticParams: httpTool.staticParams,
|
||||
staticHeaders: httpTool.staticHeaders,
|
||||
staticBody: httpTool.staticBody
|
||||
});
|
||||
|
||||
if (errorMsg) {
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import {
|
||||
contentTypeMap,
|
||||
ContentTypes,
|
||||
NodeInputKeyEnum,
|
||||
NodeOutputKeyEnum,
|
||||
@@ -59,15 +60,6 @@ type HttpResponse = DispatchNodeResultType<
|
||||
|
||||
const UNDEFINED_SIGN = 'UNDEFINED_SIGN';
|
||||
|
||||
const contentTypeMap = {
|
||||
[ContentTypes.none]: '',
|
||||
[ContentTypes.formData]: '',
|
||||
[ContentTypes.xWwwFormUrlencoded]: 'application/x-www-form-urlencoded',
|
||||
[ContentTypes.json]: 'application/json',
|
||||
[ContentTypes.xml]: 'application/xml',
|
||||
[ContentTypes.raw]: 'text/plain'
|
||||
};
|
||||
|
||||
export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<HttpResponse> => {
|
||||
let {
|
||||
runningAppInfo: { id: appId, teamId, tmbId },
|
||||
|
@@ -25,6 +25,7 @@ const LeftRadio = <T = any,>({
|
||||
align = 'center',
|
||||
px = 3.5,
|
||||
py = 4,
|
||||
gridGap = [3, 5],
|
||||
defaultBg = 'myGray.50',
|
||||
activeBg = 'primary.50',
|
||||
onChange,
|
||||
@@ -75,7 +76,7 @@ const LeftRadio = <T = any,>({
|
||||
);
|
||||
|
||||
return (
|
||||
<Grid gridGap={[3, 5]} fontSize={['sm', 'md']} {...props}>
|
||||
<Grid gridGap={gridGap} fontSize={['sm', 'md']} {...props}>
|
||||
{list.map((item) => {
|
||||
const isActive = value === item.value;
|
||||
return (
|
||||
@@ -131,7 +132,7 @@ const LeftRadio = <T = any,>({
|
||||
lineHeight={1}
|
||||
color={'myGray.900'}
|
||||
>
|
||||
<Box>{t(item.title as any)}</Box>
|
||||
<Box mb={1}>{t(item.title as any)}</Box>
|
||||
{!!item.tooltip && <QuestionTip label={item.tooltip} color={'myGray.600'} />}
|
||||
</HStack>
|
||||
) : (
|
||||
|
@@ -1,7 +1,12 @@
|
||||
{
|
||||
"Add_tool": "Add tool",
|
||||
"AutoOptimize": "Automatic optimization",
|
||||
"Click_to_delete_this_field": "Click to delete this field",
|
||||
"Custom_params": "Custom parameters",
|
||||
"Edit_tool": "Edit tool",
|
||||
"Filed_is_deprecated": "This field is deprecated",
|
||||
"HTTPTools_Create_Type": "Create Type",
|
||||
"HTTPTools_Create_Type_Tip": "Modification is not supported after selection",
|
||||
"HTTP_tools_list_with_number": "Tool list: {{total}}",
|
||||
"Index": "Index",
|
||||
"MCP_tools_debug": "debug",
|
||||
@@ -30,6 +35,7 @@
|
||||
"Selected": "Selected",
|
||||
"Start_config": "Start configuration",
|
||||
"Team_Tags": "Team tags",
|
||||
"Tool_name": "Tool name",
|
||||
"ai_point_price": "Billing",
|
||||
"ai_settings": "AI Configuration",
|
||||
"all_apps": "All Applications",
|
||||
@@ -283,6 +289,7 @@
|
||||
"tool_detail": "Tool details",
|
||||
"tool_input_param_tip": "This plugin requires configuration of related information to run properly.",
|
||||
"tool_not_active": "This tool has not been activated yet",
|
||||
"tool_params_description_tips": "The description of parameter functions, if used as tool invocation parameters, affects the model tool invocation effect.",
|
||||
"tool_run_free": "This tool runs without points consumption",
|
||||
"tool_tip": "When executed as a tool, is this field used as a tool response result?",
|
||||
"tool_type_tools": "tool",
|
||||
|
@@ -1,7 +1,12 @@
|
||||
{
|
||||
"Add_tool": "添加工具",
|
||||
"AutoOptimize": "自动优化",
|
||||
"Click_to_delete_this_field": "点击删除该字段",
|
||||
"Custom_params": "自定义参数",
|
||||
"Edit_tool": "编辑工具",
|
||||
"Filed_is_deprecated": "该字段已弃用",
|
||||
"HTTPTools_Create_Type": "创建方式",
|
||||
"HTTPTools_Create_Type_Tip": "选择后不支持修改",
|
||||
"HTTP_tools_detail": "查看详情",
|
||||
"HTTP_tools_list_with_number": "工具列表: {{total}}",
|
||||
"Index": "索引",
|
||||
@@ -31,6 +36,8 @@
|
||||
"Selected": "已选择",
|
||||
"Start_config": "开始配置",
|
||||
"Team_Tags": "团队标签",
|
||||
"Tool_description": "工具描述",
|
||||
"Tool_name": "工具名称",
|
||||
"ai_point_price": "AI积分计费",
|
||||
"ai_settings": "AI 配置",
|
||||
"all_apps": "全部应用",
|
||||
@@ -90,6 +97,7 @@
|
||||
"document_upload": "文档上传",
|
||||
"edit_app": "应用详情",
|
||||
"edit_info": "编辑信息",
|
||||
"edit_param": "编辑参数",
|
||||
"execute_time": "执行时间",
|
||||
"export_config_successful": "已复制配置,自动过滤部分敏感信息,请注意检查是否仍有敏感数据",
|
||||
"export_configs": "导出配置",
|
||||
@@ -297,6 +305,7 @@
|
||||
"tool_detail": "工具详情",
|
||||
"tool_input_param_tip": "该插件正常运行需要配置相关信息",
|
||||
"tool_not_active": "该工具尚未激活",
|
||||
"tool_params_description_tips": "参数功能的描述,若作为工具调用参数,影响模型工具调用效果",
|
||||
"tool_run_free": "该工具运行无积分消耗",
|
||||
"tool_tip": "作为工具执行时,该字段是否作为工具响应结果",
|
||||
"tool_type_tools": "工具",
|
||||
|
@@ -1,7 +1,11 @@
|
||||
{
|
||||
"Add_tool": "添加工具",
|
||||
"AutoOptimize": "自動優化",
|
||||
"Click_to_delete_this_field": "點擊刪除該字段",
|
||||
"Custom_params": "自定義參數",
|
||||
"Filed_is_deprecated": "該字段已棄用",
|
||||
"HTTPTools_Create_Type": "創建方式",
|
||||
"HTTPTools_Create_Type_Tip": "選擇後不支持修改",
|
||||
"HTTP_tools_list_with_number": "工具列表: {{total}}",
|
||||
"Index": "索引",
|
||||
"MCP_tools_debug": "偵錯",
|
||||
@@ -30,6 +34,8 @@
|
||||
"Selected": "已選擇",
|
||||
"Start_config": "開始配置",
|
||||
"Team_Tags": "團隊標籤",
|
||||
"Tool_description": "工具描述",
|
||||
"Tool_name": "工具名稱",
|
||||
"ai_point_price": "AI 積分計費",
|
||||
"ai_settings": "AI 設定",
|
||||
"all_apps": "所有應用程式",
|
||||
@@ -283,6 +289,7 @@
|
||||
"tool_detail": "工具詳情",
|
||||
"tool_input_param_tip": "這個外掛正常執行需要設定相關資訊",
|
||||
"tool_not_active": "該工具尚未激活",
|
||||
"tool_params_description_tips": "參數功能的描述,若作為工具調用參數,影響模型工具調用效果",
|
||||
"tool_run_free": "該工具運行無積分消耗",
|
||||
"tool_tip": "作為工具執行時,該字段是否作為工具響應結果",
|
||||
"tool_type_tools": "工具",
|
||||
|
Reference in New Issue
Block a user