Skip to content

Commit 32006ba

Browse files
newfish-cmykc121914yu
authored andcommitted
add manual create http toolset (#5743)
* add manual create http toolset * optimize code * optimize * fix * fix
1 parent 1ab2e92 commit 32006ba

File tree

21 files changed

+1215
-189
lines changed

21 files changed

+1215
-189
lines changed

packages/global/core/app/httpTools/utils.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ import { i18nT } from '../../../../web/i18n/utils';
1313
export const getHTTPToolSetRuntimeNode = ({
1414
name,
1515
avatar,
16-
baseUrl = '',
17-
customHeaders = '',
18-
apiSchemaStr = '',
16+
baseUrl,
17+
customHeaders,
18+
apiSchemaStr,
1919
toolList = [],
2020
headerSecret
2121
}: {
@@ -34,12 +34,11 @@ export const getHTTPToolSetRuntimeNode = ({
3434
intro: 'HTTP Tools',
3535
toolConfig: {
3636
httpToolSet: {
37-
baseUrl,
3837
toolList,
39-
headerSecret,
40-
customHeaders,
41-
apiSchemaStr,
42-
toolId: ''
38+
...(baseUrl !== undefined && { baseUrl }),
39+
...(apiSchemaStr !== undefined && { apiSchemaStr }),
40+
...(customHeaders !== undefined && { customHeaders }),
41+
...(headerSecret !== undefined && { headerSecret })
4342
}
4443
},
4544
inputs: [],

packages/global/core/app/type.d.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { FlowNodeTemplateType, StoreNodeItemType } from '../workflow/type/n
22
import type { AppTypeEnum } from './constants';
33
import { PermissionTypeEnum } from '../../support/permission/constant';
44
import type {
5+
ContentTypes,
56
NodeInputKeyEnum,
67
VariableInputEnum,
78
WorkflowIOValueTypeEnum
@@ -127,6 +128,16 @@ export type HttpToolConfigType = {
127128
outputSchema: JSONSchemaOutputType;
128129
path: string;
129130
method: string;
131+
132+
// manual
133+
staticParams?: Array<{ key: string; value: string }>;
134+
staticHeaders?: Array<{ key: string; value: string }>;
135+
staticBody?: {
136+
type: ContentTypes;
137+
content?: string;
138+
formData?: Array<{ key: string; value: string }>;
139+
};
140+
headerSecret?: StoreSecretValueType;
130141
};
131142

132143
/* app chat config type */

packages/global/core/workflow/constants.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,19 @@ export enum ContentTypes {
477477
raw = 'raw-text'
478478
}
479479

480+
export const contentTypeMap = {
481+
[ContentTypes.none]: '',
482+
[ContentTypes.formData]: '',
483+
[ContentTypes.xWwwFormUrlencoded]: 'application/x-www-form-urlencoded',
484+
[ContentTypes.json]: 'application/json',
485+
[ContentTypes.xml]: 'application/xml',
486+
[ContentTypes.raw]: 'text/plain'
487+
};
488+
489+
// http request methods
490+
export const HTTP_METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'] as const;
491+
export type HttpMethod = (typeof HTTP_METHODS)[number];
492+
480493
export const ArrayTypeMap: Record<WorkflowIOValueTypeEnum, WorkflowIOValueTypeEnum> = {
481494
[WorkflowIOValueTypeEnum.string]: WorkflowIOValueTypeEnum.arrayString,
482495
[WorkflowIOValueTypeEnum.number]: WorkflowIOValueTypeEnum.arrayNumber,

packages/global/core/workflow/type/node.d.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,10 @@ export type NodeToolConfigType = {
5252
}[];
5353
};
5454
httpToolSet?: {
55-
toolId: string;
56-
baseUrl: string;
5755
toolList: HttpToolConfigType[];
58-
apiSchemaStr: string;
59-
customHeaders: string;
56+
baseUrl?: string;
57+
apiSchemaStr?: string;
58+
customHeaders?: string;
6059
headerSecret?: StoreSecretValueType;
6160
};
6261
httpTool?: {

packages/service/core/app/http.ts

Lines changed: 112 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { getSecretValue } from '../../common/secret/utils';
33
import axios from 'axios';
44
import { getErrText } from '@fastgpt/global/common/error/utils';
55
import type { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
6+
import type { HttpToolConfigType } from '@fastgpt/global/core/app/type';
7+
import { contentTypeMap, ContentTypes } from '@fastgpt/global/core/workflow/constants';
68

79
export type RunHTTPToolParams = {
810
baseUrl: string;
@@ -11,48 +13,140 @@ export type RunHTTPToolParams = {
1113
params: Record<string, any>;
1214
headerSecret?: StoreSecretValueType;
1315
customHeaders?: Record<string, string>;
16+
staticParams?: HttpToolConfigType['staticParams'];
17+
staticHeaders?: HttpToolConfigType['staticHeaders'];
18+
staticBody?: HttpToolConfigType['staticBody'];
1419
};
1520

1621
export type RunHTTPToolResult = RequireOnlyOne<{
1722
data?: any;
1823
errorMsg?: string;
1924
}>;
2025

21-
export async function runHTTPTool({
26+
const buildHttpRequest = ({
27+
method,
28+
params,
29+
headerSecret,
30+
customHeaders,
31+
staticParams,
32+
staticHeaders,
33+
staticBody
34+
}: Omit<RunHTTPToolParams, 'baseUrl' | 'toolPath'>) => {
35+
const body = (() => {
36+
if (!staticBody || staticBody.type === ContentTypes.none) {
37+
return ['POST', 'PUT', 'PATCH'].includes(method.toUpperCase()) ? params : undefined;
38+
}
39+
40+
if (staticBody.type === ContentTypes.json) {
41+
const staticContent = staticBody.content ? JSON.parse(staticBody.content) : {};
42+
return { ...staticContent, ...params };
43+
}
44+
45+
if (staticBody.type === ContentTypes.formData) {
46+
const formData = new (require('form-data'))();
47+
staticBody.formData?.forEach(({ key, value }) => {
48+
formData.append(key, value);
49+
});
50+
Object.entries(params).forEach(([key, value]) => {
51+
formData.append(key, value);
52+
});
53+
return formData;
54+
}
55+
56+
if (staticBody.type === ContentTypes.xWwwFormUrlencoded) {
57+
const urlencoded = new URLSearchParams();
58+
staticBody.formData?.forEach(({ key, value }) => {
59+
urlencoded.append(key, value);
60+
});
61+
Object.entries(params).forEach(([key, value]) => {
62+
urlencoded.append(key, String(value));
63+
});
64+
return urlencoded.toString();
65+
}
66+
67+
if (staticBody.type === ContentTypes.xml || staticBody.type === ContentTypes.raw) {
68+
return staticBody.content || '';
69+
}
70+
71+
return undefined;
72+
})();
73+
74+
const contentType = contentTypeMap[staticBody?.type || ContentTypes.none];
75+
const headers = {
76+
...(contentType && { 'Content-Type': contentType }),
77+
...(customHeaders || {}),
78+
...(headerSecret ? getSecretValue({ storeSecret: headerSecret }) : {}),
79+
...(staticHeaders?.reduce(
80+
(acc, { key, value }) => {
81+
acc[key] = value;
82+
return acc;
83+
},
84+
{} as Record<string, string>
85+
) || {})
86+
};
87+
88+
const queryParams = (() => {
89+
const staticParamsObj =
90+
staticParams?.reduce(
91+
(acc, { key, value }) => {
92+
acc[key] = value;
93+
return acc;
94+
},
95+
{} as Record<string, any>
96+
) || {};
97+
98+
const mergedParams =
99+
method.toUpperCase() === 'GET' || staticParams
100+
? { ...staticParamsObj, ...params }
101+
: staticParamsObj;
102+
103+
return Object.keys(mergedParams).length > 0 ? mergedParams : undefined;
104+
})();
105+
106+
return {
107+
headers,
108+
body,
109+
queryParams
110+
};
111+
};
112+
113+
export const runHTTPTool = async ({
22114
baseUrl,
23115
toolPath,
24116
method = 'POST',
25117
params,
26118
headerSecret,
27-
customHeaders
28-
}: RunHTTPToolParams): Promise<RunHTTPToolResult> {
119+
customHeaders,
120+
staticParams,
121+
staticHeaders,
122+
staticBody
123+
}: RunHTTPToolParams): Promise<RunHTTPToolResult> => {
29124
try {
30-
const headers = {
31-
'Content-Type': 'application/json',
32-
...(customHeaders || {}),
33-
...(headerSecret ? getSecretValue({ storeSecret: headerSecret }) : {})
34-
};
125+
const { headers, body, queryParams } = buildHttpRequest({
126+
method,
127+
params,
128+
headerSecret,
129+
customHeaders,
130+
staticParams,
131+
staticHeaders,
132+
staticBody
133+
});
35134

36135
const { data } = await axios({
37136
method: method.toUpperCase(),
38137
baseURL: baseUrl.startsWith('https://') ? baseUrl : `https://${baseUrl}`,
39138
url: toolPath,
40139
headers,
41-
data: params,
42-
params,
140+
data: body,
141+
params: queryParams,
43142
timeout: 300000,
44143
httpsAgent: new (require('https').Agent)({
45144
rejectUnauthorized: false
46145
})
47146
});
48147

49-
return {
50-
data
51-
};
148+
return { data };
52149
} catch (error: any) {
53-
console.log(error);
54-
return {
55-
errorMsg: getErrText(error)
56-
};
150+
return { errorMsg: getErrText(error) };
57151
}
58-
}
152+
};

packages/service/core/workflow/dispatch/child/runTool.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -236,16 +236,19 @@ export const dispatchRunTool = async (props: RunToolProps): Promise<RunToolRespo
236236
}
237237

238238
const { data, errorMsg } = await runHTTPTool({
239-
baseUrl: baseUrl,
239+
baseUrl: baseUrl || '',
240240
toolPath: httpTool.path,
241241
method: httpTool.method,
242242
params,
243-
headerSecret,
243+
headerSecret: httpTool.headerSecret || headerSecret,
244244
customHeaders: customHeaders
245245
? typeof customHeaders === 'string'
246246
? JSON.parse(customHeaders)
247247
: customHeaders
248-
: undefined
248+
: undefined,
249+
staticParams: httpTool.staticParams,
250+
staticHeaders: httpTool.staticHeaders,
251+
staticBody: httpTool.staticBody
249252
});
250253

251254
if (errorMsg) {

packages/service/core/workflow/dispatch/tools/http468.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { getErrText } from '@fastgpt/global/common/error/utils';
22
import {
3+
contentTypeMap,
34
ContentTypes,
45
NodeInputKeyEnum,
56
NodeOutputKeyEnum,
@@ -59,15 +60,6 @@ type HttpResponse = DispatchNodeResultType<
5960

6061
const UNDEFINED_SIGN = 'UNDEFINED_SIGN';
6162

62-
const contentTypeMap = {
63-
[ContentTypes.none]: '',
64-
[ContentTypes.formData]: '',
65-
[ContentTypes.xWwwFormUrlencoded]: 'application/x-www-form-urlencoded',
66-
[ContentTypes.json]: 'application/json',
67-
[ContentTypes.xml]: 'application/xml',
68-
[ContentTypes.raw]: 'text/plain'
69-
};
70-
7163
export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<HttpResponse> => {
7264
let {
7365
runningAppInfo: { id: appId, teamId, tmbId },

packages/web/components/common/Radio/LeftRadio.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const LeftRadio = <T = any,>({
2525
align = 'center',
2626
px = 3.5,
2727
py = 4,
28+
gridGap = [3, 5],
2829
defaultBg = 'myGray.50',
2930
activeBg = 'primary.50',
3031
onChange,
@@ -75,7 +76,7 @@ const LeftRadio = <T = any,>({
7576
);
7677

7778
return (
78-
<Grid gridGap={[3, 5]} fontSize={['sm', 'md']} {...props}>
79+
<Grid gridGap={gridGap} fontSize={['sm', 'md']} {...props}>
7980
{list.map((item) => {
8081
const isActive = value === item.value;
8182
return (
@@ -131,7 +132,7 @@ const LeftRadio = <T = any,>({
131132
lineHeight={1}
132133
color={'myGray.900'}
133134
>
134-
<Box>{t(item.title as any)}</Box>
135+
<Box mb={1}>{t(item.title as any)}</Box>
135136
{!!item.tooltip && <QuestionTip label={item.tooltip} color={'myGray.600'} />}
136137
</HStack>
137138
) : (

packages/web/i18n/en/app.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
{
2+
"Add_tool": "Add tool",
23
"AutoOptimize": "Automatic optimization",
34
"Click_to_delete_this_field": "Click to delete this field",
5+
"Custom_params": "Custom parameters",
6+
"Edit_tool": "Edit tool",
47
"Filed_is_deprecated": "This field is deprecated",
8+
"HTTPTools_Create_Type": "Create Type",
9+
"HTTPTools_Create_Type_Tip": "Modification is not supported after selection",
510
"HTTP_tools_list_with_number": "Tool list: {{total}}",
611
"Index": "Index",
712
"MCP_tools_debug": "debug",
@@ -30,6 +35,7 @@
3035
"Selected": "Selected",
3136
"Start_config": "Start configuration",
3237
"Team_Tags": "Team tags",
38+
"Tool_name": "Tool name",
3339
"ai_point_price": "Billing",
3440
"ai_settings": "AI Configuration",
3541
"all_apps": "All Applications",
@@ -283,6 +289,7 @@
283289
"tool_detail": "Tool details",
284290
"tool_input_param_tip": "This plugin requires configuration of related information to run properly.",
285291
"tool_not_active": "This tool has not been activated yet",
292+
"tool_params_description_tips": "The description of parameter functions, if used as tool invocation parameters, affects the model tool invocation effect.",
286293
"tool_run_free": "This tool runs without points consumption",
287294
"tool_tip": "When executed as a tool, is this field used as a tool response result?",
288295
"tool_type_tools": "tool",

packages/web/i18n/zh-CN/app.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
{
2+
"Add_tool": "添加工具",
23
"AutoOptimize": "自动优化",
34
"Click_to_delete_this_field": "点击删除该字段",
5+
"Custom_params": "自定义参数",
6+
"Edit_tool": "编辑工具",
47
"Filed_is_deprecated": "该字段已弃用",
8+
"HTTPTools_Create_Type": "创建方式",
9+
"HTTPTools_Create_Type_Tip": "选择后不支持修改",
510
"HTTP_tools_detail": "查看详情",
611
"HTTP_tools_list_with_number": "工具列表: {{total}}",
712
"Index": "索引",
@@ -31,6 +36,8 @@
3136
"Selected": "已选择",
3237
"Start_config": "开始配置",
3338
"Team_Tags": "团队标签",
39+
"Tool_description": "工具描述",
40+
"Tool_name": "工具名称",
3441
"ai_point_price": "AI积分计费",
3542
"ai_settings": "AI 配置",
3643
"all_apps": "全部应用",
@@ -90,6 +97,7 @@
9097
"document_upload": "文档上传",
9198
"edit_app": "应用详情",
9299
"edit_info": "编辑信息",
100+
"edit_param": "编辑参数",
93101
"execute_time": "执行时间",
94102
"export_config_successful": "已复制配置,自动过滤部分敏感信息,请注意检查是否仍有敏感数据",
95103
"export_configs": "导出配置",
@@ -297,6 +305,7 @@
297305
"tool_detail": "工具详情",
298306
"tool_input_param_tip": "该插件正常运行需要配置相关信息",
299307
"tool_not_active": "该工具尚未激活",
308+
"tool_params_description_tips": "参数功能的描述,若作为工具调用参数,影响模型工具调用效果",
300309
"tool_run_free": "该工具运行无积分消耗",
301310
"tool_tip": "作为工具执行时,该字段是否作为工具响应结果",
302311
"tool_type_tools": "工具",

0 commit comments

Comments
 (0)