mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-28 17:01:00 +03:00
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:
@@ -1,7 +1,21 @@
|
||||
import { INode, INodeData, INodeParams } from '../../../src/Interface'
|
||||
import { getBaseClasses } from '../../../src/utils'
|
||||
import { getBaseClasses, stripHTMLFromToolInput } from '../../../src/utils'
|
||||
import { desc, RequestParameters, RequestsGetTool } from './core'
|
||||
|
||||
const codeExample = `{
|
||||
"id": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"description": "ID of the item to get. /:id"
|
||||
},
|
||||
"limit": {
|
||||
"type": "string",
|
||||
"in": "query",
|
||||
"description": "Limit the number of items to get. ?limit=10"
|
||||
}
|
||||
}`
|
||||
|
||||
class RequestsGet_Tools implements INode {
|
||||
label: string
|
||||
name: string
|
||||
@@ -16,52 +30,107 @@ class RequestsGet_Tools implements INode {
|
||||
constructor() {
|
||||
this.label = 'Requests Get'
|
||||
this.name = 'requestsGet'
|
||||
this.version = 1.0
|
||||
this.version = 2.0
|
||||
this.type = 'RequestsGet'
|
||||
this.icon = 'requestsget.svg'
|
||||
this.icon = 'get.png'
|
||||
this.category = 'Tools'
|
||||
this.description = 'Execute HTTP GET requests'
|
||||
this.baseClasses = [this.type, ...getBaseClasses(RequestsGetTool)]
|
||||
this.inputs = [
|
||||
{
|
||||
label: 'URL',
|
||||
name: 'url',
|
||||
name: 'requestsGetUrl',
|
||||
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',
|
||||
acceptVariable: true
|
||||
},
|
||||
{
|
||||
label: 'Name',
|
||||
name: 'requestsGetName',
|
||||
type: 'string',
|
||||
default: 'requests_get',
|
||||
description: 'Name of the tool',
|
||||
additionalParams: true,
|
||||
optional: true
|
||||
},
|
||||
{
|
||||
label: 'Description',
|
||||
name: 'description',
|
||||
name: 'requestsGetDescription',
|
||||
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: 'requestsGetHeaders',
|
||||
type: 'string',
|
||||
rows: 4,
|
||||
acceptVariable: true,
|
||||
additionalParams: true,
|
||||
optional: true
|
||||
optional: true,
|
||||
placeholder: `{
|
||||
"Authorization": "Bearer <token>"
|
||||
}`
|
||||
},
|
||||
{
|
||||
label: 'Query Params Schema',
|
||||
name: 'requestsGetQueryParamsSchema',
|
||||
type: 'code',
|
||||
description: 'Description of the available query params to enable LLM to figure out which query params to use',
|
||||
placeholder: `{
|
||||
"id": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"description": "ID of the item to get. /:id"
|
||||
},
|
||||
"limit": {
|
||||
"type": "string",
|
||||
"in": "query",
|
||||
"description": "Limit the number of items to get. ?limit=10"
|
||||
}
|
||||
}`,
|
||||
optional: true,
|
||||
hideCodeExecute: true,
|
||||
additionalParams: true,
|
||||
codeExample: codeExample
|
||||
},
|
||||
{
|
||||
label: 'Max Output Length',
|
||||
name: 'requestsGetMaxOutputLength',
|
||||
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 headers = (nodeData.inputs?.headers as string) || (nodeData.inputs?.requestsGetHeaders as string)
|
||||
const url = (nodeData.inputs?.url as string) || (nodeData.inputs?.requestsGetUrl as string)
|
||||
const description = (nodeData.inputs?.description as string) || (nodeData.inputs?.requestsGetDescription as string)
|
||||
const name = (nodeData.inputs?.name as string) || (nodeData.inputs?.requestsGetName as string)
|
||||
const queryParamsSchema =
|
||||
(nodeData.inputs?.queryParamsSchema as string) || (nodeData.inputs?.requestsGetQueryParamsSchema as string)
|
||||
const maxOutputLength = nodeData.inputs?.requestsGetMaxOutputLength 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 (queryParamsSchema) obj.queryParamsSchema = queryParamsSchema
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { z } from 'zod'
|
||||
import fetch from 'node-fetch'
|
||||
import { Tool } from '@langchain/core/tools'
|
||||
import { DynamicStructuredTool } from '../OpenAPIToolkit/core'
|
||||
|
||||
export const desc = `A portal to the internet. Use this when you need to get specific content from a website.
|
||||
Input should be a url (i.e. https://www.google.com). The output will be the text response of the GET request.`
|
||||
export const desc = `Use this when you need to execute a GET request to get data from a website.`
|
||||
|
||||
export interface Headers {
|
||||
[key: string]: string
|
||||
@@ -11,36 +11,173 @@ export interface Headers {
|
||||
export interface RequestParameters {
|
||||
headers?: Headers
|
||||
url?: string
|
||||
name?: string
|
||||
queryParamsSchema?: string
|
||||
description?: string
|
||||
maxOutputLength?: number
|
||||
}
|
||||
|
||||
export class RequestsGetTool extends Tool {
|
||||
name = 'requests_get'
|
||||
// Base schema for GET request
|
||||
const createRequestsGetSchema = (queryParamsSchema?: string) => {
|
||||
// If queryParamsSchema is provided, parse it and add dynamic query params
|
||||
if (queryParamsSchema) {
|
||||
try {
|
||||
const parsedSchema = JSON.parse(queryParamsSchema)
|
||||
const queryParamsObject: 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.string().transform((val) => Number(val))
|
||||
} else if (config.type === 'boolean') {
|
||||
zodType = z.string().transform((val) => val === 'true')
|
||||
}
|
||||
|
||||
// Add description
|
||||
if (config.description) {
|
||||
zodType = zodType.describe(config.description)
|
||||
}
|
||||
|
||||
// Make optional if not required
|
||||
if (!config.required) {
|
||||
zodType = zodType.optional()
|
||||
}
|
||||
|
||||
queryParamsObject[key] = zodType
|
||||
})
|
||||
|
||||
if (Object.keys(queryParamsObject).length > 0) {
|
||||
return z.object({
|
||||
queryParams: z.object(queryParamsObject).optional().describe('Query parameters for the request')
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to parse queryParamsSchema:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to generic query params
|
||||
return z.object({
|
||||
queryParams: z.record(z.string()).optional().describe('Optional query parameters to include in the request')
|
||||
})
|
||||
}
|
||||
|
||||
export class RequestsGetTool extends DynamicStructuredTool {
|
||||
url = ''
|
||||
description = desc
|
||||
maxOutputLength = 2000
|
||||
maxOutputLength = Infinity
|
||||
headers = {}
|
||||
queryParamsSchema?: string
|
||||
|
||||
constructor(args?: RequestParameters) {
|
||||
super()
|
||||
const schema = createRequestsGetSchema(args?.queryParamsSchema)
|
||||
|
||||
const toolInput = {
|
||||
name: args?.name || 'requests_get',
|
||||
description: args?.description || desc,
|
||||
schema: schema,
|
||||
baseUrl: '',
|
||||
method: 'GET',
|
||||
headers: args?.headers || {}
|
||||
}
|
||||
super(toolInput)
|
||||
this.url = args?.url ?? this.url
|
||||
this.headers = args?.headers ?? this.headers
|
||||
this.description = args?.description ?? this.description
|
||||
this.maxOutputLength = args?.maxOutputLength ?? this.maxOutputLength
|
||||
this.queryParamsSchema = args?.queryParamsSchema
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
async _call(input: string) {
|
||||
const inputUrl = !this.url ? input : this.url
|
||||
async _call(arg: any): Promise<string> {
|
||||
const params = { ...arg }
|
||||
|
||||
if (process.env.DEBUG === 'true') console.info(`Making GET API call to ${inputUrl}`)
|
||||
const inputUrl = this.url
|
||||
if (!inputUrl) {
|
||||
throw new Error('URL is required for GET request')
|
||||
}
|
||||
|
||||
const res = await fetch(inputUrl, {
|
||||
headers: this.headers
|
||||
})
|
||||
const requestHeaders = {
|
||||
...(params.headers || {}),
|
||||
...this.headers
|
||||
}
|
||||
|
||||
const text = await res.text()
|
||||
return text.slice(0, this.maxOutputLength)
|
||||
// Process URL and query parameters based on schema
|
||||
let finalUrl = inputUrl
|
||||
const queryParams: Record<string, string> = {}
|
||||
|
||||
if (this.queryParamsSchema && params.queryParams && Object.keys(params.queryParams).length > 0) {
|
||||
try {
|
||||
const parsedSchema = JSON.parse(this.queryParamsSchema)
|
||||
const pathParams: Array<{ key: string; value: string }> = []
|
||||
|
||||
Object.entries(params.queryParams).forEach(([key, value]) => {
|
||||
const paramConfig = parsedSchema[key]
|
||||
if (paramConfig && value !== undefined && value !== null) {
|
||||
if (paramConfig.in === 'path') {
|
||||
// Check if URL contains path parameter placeholder
|
||||
const pathPattern = new RegExp(`:${key}\\b`, 'g')
|
||||
if (finalUrl.includes(`:${key}`)) {
|
||||
// Replace path parameters in URL (e.g., /:id -> /123)
|
||||
finalUrl = finalUrl.replace(pathPattern, encodeURIComponent(String(value)))
|
||||
} else {
|
||||
// Collect path parameters to append to URL
|
||||
pathParams.push({ key, value: String(value) })
|
||||
}
|
||||
} else if (paramConfig.in === 'query') {
|
||||
// Add to query parameters
|
||||
queryParams[key] = String(value)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Append path parameters to URL if any exist
|
||||
if (pathParams.length > 0) {
|
||||
let urlPath = finalUrl
|
||||
// Remove trailing slash if present
|
||||
if (urlPath.endsWith('/')) {
|
||||
urlPath = urlPath.slice(0, -1)
|
||||
}
|
||||
// Append each path parameter
|
||||
pathParams.forEach(({ value }) => {
|
||||
urlPath += `/${encodeURIComponent(value)}`
|
||||
})
|
||||
finalUrl = urlPath
|
||||
}
|
||||
|
||||
// Add query parameters to URL if any exist
|
||||
if (Object.keys(queryParams).length > 0) {
|
||||
const url = new URL(finalUrl)
|
||||
Object.entries(queryParams).forEach(([key, value]) => {
|
||||
url.searchParams.append(key, value)
|
||||
})
|
||||
finalUrl = url.toString()
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to process queryParamsSchema:', error)
|
||||
}
|
||||
} else if (params.queryParams && Object.keys(params.queryParams).length > 0) {
|
||||
// Fallback: treat all parameters as query parameters if no schema is defined
|
||||
const url = new URL(finalUrl)
|
||||
Object.entries(params.queryParams).forEach(([key, value]) => {
|
||||
url.searchParams.append(key, String(value))
|
||||
})
|
||||
finalUrl = url.toString()
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(finalUrl, {
|
||||
headers: requestHeaders
|
||||
})
|
||||
|
||||
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) {
|
||||
throw new Error(`Failed to make GET request: ${error instanceof Error ? error.message : 'Unknown error'}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 7.5 KiB |
@@ -1,6 +0,0 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.5 20.5C10.5 20.5 10 20 9 20C7.067 20 6 21.567 6 23.5C6 25.433 7.067 27 9 27C10 27 10.7037 26.4812 11 26V24H10" stroke="#110000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M18.5 20H14V27H18.5M14 23.5H17.5" stroke="black" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M23.5 27V20M21 20H26" 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.1 KiB |
Reference in New Issue
Block a user