Chore/Update issue templates and add new tools (#4687)

* Enhancement: Update issue templates and add new tools

- Updated bug report template to include a default label of 'bug'.
- Updated feature request template to include a default label of 'enhancement'.
- Added new credential class for Agentflow API.
- Enhanced Agent and HTTP nodes to improve tool management and error handling.
- Added deprecation badges to several agent and chain classes.
- Introduced new tools for handling requests (GET, POST, DELETE, PUT) with improved error handling.
- Added new chatflows and agentflows for various use cases, including document QnA and translation.
- Updated UI components for better handling of agent flows and marketplace interactions.
- Refactored utility functions for improved functionality and clarity.

* Refactor: Remove beta badge and streamline template title assignment

- Removed the 'BETA' badge from the ExtractMetadataRetriever class.
- Simplified the title assignment in the agentflowv2 generator by using a variable instead of inline string manipulation.
This commit is contained in:
Henry Heng
2025-06-19 18:11:24 +01:00
committed by GitHub
parent 15dd28356b
commit a107aa7a77
86 changed files with 9942 additions and 12634 deletions
@@ -1,7 +1,19 @@
import { INode, INodeData, INodeParams } from '../../../src/Interface'
import { getBaseClasses } from '../../../src/utils'
import { getBaseClasses, stripHTMLFromToolInput } from '../../../src/utils'
import { RequestParameters, desc, RequestsPostTool } from './core'
const codeExample = `{
"name": {
"type": "string",
"required": true,
"description": "Name of the item"
},
"date": {
"type": "string",
"description": "Date of the item"
}
}`
class RequestsPost_Tools implements INode {
label: string
name: string
@@ -16,62 +28,119 @@ class RequestsPost_Tools implements INode {
constructor() {
this.label = 'Requests Post'
this.name = 'requestsPost'
this.version = 1.0
this.version = 2.0
this.type = 'RequestsPost'
this.icon = 'requestspost.svg'
this.icon = 'post.png'
this.category = 'Tools'
this.description = 'Execute HTTP POST requests'
this.baseClasses = [this.type, ...getBaseClasses(RequestsPostTool)]
this.inputs = [
{
label: 'URL',
name: 'url',
name: 'requestsPostUrl',
type: 'string',
description:
'Agent will make call to this exact URL. If not specified, agent will try to figure out itself from AIPlugin if provided',
additionalParams: true,
optional: true
acceptVariable: true
},
{
label: 'Body',
name: 'body',
type: 'json',
description:
'JSON body for the POST request. If not specified, agent will try to figure out itself from AIPlugin if provided',
label: 'Name',
name: 'requestsPostName',
type: 'string',
default: 'requests_post',
description: 'Name of the tool',
additionalParams: true,
optional: true
},
{
label: 'Description',
name: 'description',
name: 'requestsPostDescription',
type: 'string',
rows: 4,
default: desc,
description: 'Acts like a prompt to tell agent when it should use this tool',
description: 'Describe to LLM when it should use this tool',
additionalParams: true,
optional: true
},
{
label: 'Headers',
name: 'headers',
type: 'json',
name: 'requestsPostHeaders',
type: 'string',
rows: 4,
acceptVariable: true,
additionalParams: true,
optional: true
optional: true,
placeholder: `{
"Authorization": "Bearer <token>"
}`
},
{
label: 'Body',
name: 'requestPostBody',
type: 'string',
rows: 4,
description: 'JSON body for the POST request. This will override the body generated by the LLM',
additionalParams: true,
acceptVariable: true,
optional: true,
placeholder: `{
"name": "John Doe",
"age": 30
}`
},
{
label: 'Body Schema',
name: 'requestsPostBodySchema',
type: 'code',
description: 'Description of the available body params to enable LLM to figure out which body params to use',
placeholder: `{
"name": {
"type": "string",
"required": true,
"description": "Name of the item"
},
"date": {
"type": "string",
"description": "Date of the item"
}
}`,
optional: true,
hideCodeExecute: true,
additionalParams: true,
codeExample: codeExample
},
{
label: 'Max Output Length',
name: 'requestsPostMaxOutputLength',
type: 'number',
description: 'Max length of the output. Remove this if you want to return the entire response',
default: '2000',
step: 1,
optional: true,
additionalParams: true
}
]
}
async init(nodeData: INodeData): Promise<any> {
const headers = nodeData.inputs?.headers as string
const url = nodeData.inputs?.url as string
const description = nodeData.inputs?.description as string
const body = nodeData.inputs?.body as string
const headers = (nodeData.inputs?.headers as string) || (nodeData.inputs?.requestsPostHeaders as string)
const url = (nodeData.inputs?.url as string) || (nodeData.inputs?.requestsPostUrl as string)
const name = (nodeData.inputs?.name as string) || (nodeData.inputs?.requestsPostName as string)
const description = (nodeData.inputs?.description as string) || (nodeData.inputs?.requestsPostDescription as string)
const body = (nodeData.inputs?.body as string) || (nodeData.inputs?.requestPostBody as string)
const bodySchema = nodeData.inputs?.requestsPostBodySchema as string
const maxOutputLength = (nodeData.inputs?.maxOutputLength as string) || (nodeData.inputs?.requestsPostMaxOutputLength as string)
const obj: RequestParameters = {}
if (url) obj.url = url
if (url) obj.url = stripHTMLFromToolInput(url)
if (description) obj.description = description
if (name)
obj.name = name
.toLowerCase()
.replace(/ /g, '_')
.replace(/[^a-z0-9_-]/g, '')
if (bodySchema) obj.bodySchema = stripHTMLFromToolInput(bodySchema)
if (maxOutputLength) obj.maxOutputLength = parseInt(maxOutputLength, 10)
if (headers) {
const parsedHeaders = typeof headers === 'object' ? headers : JSON.parse(headers)
const parsedHeaders = typeof headers === 'object' ? headers : JSON.parse(stripHTMLFromToolInput(headers))
obj.headers = parsedHeaders
}
if (body) {
@@ -1,12 +1,8 @@
import { Tool } from '@langchain/core/tools'
import { z } from 'zod'
import fetch from 'node-fetch'
import { DynamicStructuredTool } from '../OpenAPIToolkit/core'
export const desc = `Use this when you want to POST to a website.
Input should be a json string with two keys: "url" and "data".
The value of "url" should be a string, and the value of "data" should be a dictionary of
key-value pairs you want to POST to the url as a JSON body.
Be careful to always use double quotes for strings in the json string
The output will be the text response of the POST request.`
export const desc = `Use this when you want to execute a POST request to create or update a resource.`
export interface Headers {
[key: string]: string
@@ -21,52 +17,129 @@ export interface RequestParameters {
body?: Body
url?: string
description?: string
name?: string
bodySchema?: string
maxOutputLength?: number
}
export class RequestsPostTool extends Tool {
name = 'requests_post'
// Base schema for POST request
const createRequestsPostSchema = (bodySchema?: string) => {
// If bodySchema is provided, parse it and add dynamic body params
if (bodySchema) {
try {
const parsedSchema = JSON.parse(bodySchema)
const bodyParamsObject: Record<string, z.ZodTypeAny> = {}
Object.entries(parsedSchema).forEach(([key, config]: [string, any]) => {
let zodType: z.ZodTypeAny = z.string()
// Handle different types
if (config.type === 'number') {
zodType = z.number()
} else if (config.type === 'boolean') {
zodType = z.boolean()
} else if (config.type === 'object') {
zodType = z.record(z.any())
} else if (config.type === 'array') {
zodType = z.array(z.any())
}
// Add description
if (config.description) {
zodType = zodType.describe(config.description)
}
// Make optional if not required
if (!config.required) {
zodType = zodType.optional()
}
bodyParamsObject[key] = zodType
})
if (Object.keys(bodyParamsObject).length > 0) {
return z.object({
body: z.object(bodyParamsObject).describe('Request body parameters')
})
}
} catch (error) {
console.warn('Failed to parse bodySchema:', error)
}
}
// Fallback to generic body
return z.object({
body: z.record(z.any()).optional().describe('Optional body data to include in the request')
})
}
export class RequestsPostTool extends DynamicStructuredTool {
url = ''
description = desc
maxOutputLength = Infinity
headers = {}
body = {}
bodySchema?: string
constructor(args?: RequestParameters) {
super()
const schema = createRequestsPostSchema(args?.bodySchema)
const toolInput = {
name: args?.name || 'requests_post',
description: args?.description || desc,
schema: schema,
baseUrl: '',
method: 'POST',
headers: args?.headers || {}
}
super(toolInput)
this.url = args?.url ?? this.url
this.headers = args?.headers ?? this.headers
this.body = args?.body ?? this.body
this.description = args?.description ?? this.description
this.maxOutputLength = args?.maxOutputLength ?? this.maxOutputLength
this.bodySchema = args?.bodySchema
}
/** @ignore */
async _call(input: string) {
async _call(arg: any): Promise<string> {
const params = { ...arg }
try {
let inputUrl = ''
let inputBody = {}
if (Object.keys(this.body).length || this.url) {
if (this.url) inputUrl = this.url
if (Object.keys(this.body).length) inputBody = this.body
} else {
const { url, data } = JSON.parse(input)
inputUrl = url
inputBody = data
const inputUrl = this.url
if (!inputUrl) {
throw new Error('URL is required for POST request')
}
if (process.env.DEBUG === 'true') console.info(`Making POST API call to ${inputUrl} with body ${JSON.stringify(inputBody)}`)
let inputBody = {
...this.body
}
if (this.bodySchema && params.body && Object.keys(params.body).length > 0) {
inputBody = {
...inputBody,
...params.body
}
}
const requestHeaders = {
'Content-Type': 'application/json',
...(params.headers || {}),
...this.headers
}
const res = await fetch(inputUrl, {
method: 'POST',
headers: this.headers,
headers: requestHeaders,
body: JSON.stringify(inputBody)
})
if (!res.ok) {
throw new Error(`HTTP Error ${res.status}: ${res.statusText}`)
}
const text = await res.text()
return text.slice(0, this.maxOutputLength)
} catch (error) {
return `${error}`
throw new Error(`Failed to make POST request: ${error instanceof Error ? error.message : 'Unknown error'}`)
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

@@ -1,7 +0,0 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 27V20H6.5C7.60457 20 8.5 20.8954 8.5 22C8.5 23.1046 7.60457 24 6.5 24H4" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M27 27V20M25 20H29" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M22.5644 20.4399C21.6769 19.7608 19 19.6332 19 21.7961C19 24.1915 23 22.5657 23 25.0902C23 26.9875 20.33 27.5912 19 26.3537" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11 23.5C11 20.7 12.6667 20 13.5 20C14.3333 20 16 20.7 16 23.5C16 26.3 14.3333 27 13.5 27C12.6667 27 11 26.3 11 23.5Z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M19.1112 15.2076L17.482 13.3556C15.4506 14.3228 13.0464 14.0464 11.477 12.477C10.1962 11.1962 9.77656 9.35939 10.1913 7.62299C10.3492 6.9619 11.1601 6.82676 11.6407 7.30737L13.5196 9.18628C14.1962 9.86283 15.3416 9.81433 16.078 9.07795C16.8143 8.34157 16.8628 7.19616 16.1863 6.51961L14.3074 4.64071C13.8268 4.16009 13.9619 3.34916 14.623 3.19127C16.3594 2.77656 18.1962 3.19622 19.477 4.477C21.0464 6.04639 21.3228 8.45065 20.3556 10.482L22.2076 12.1112" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB