parent
c9d8b8716b
commit
f5cedb2460
|
|
@ -1,9 +1,10 @@
|
|||
import { load } from 'js-yaml'
|
||||
import { BaseLanguageModel } from '@langchain/core/language_models/base'
|
||||
import { OpenApiToolkit } from 'langchain/agents'
|
||||
import { JsonSpec, JsonObject } from './core'
|
||||
import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface'
|
||||
import { getCredentialData, getCredentialParam, getFileFromStorage } from '../../../src'
|
||||
import { getFileFromStorage, getVars, IDatabaseEntity, IVariable } from '../../../src'
|
||||
import $RefParser from '@apidevtools/json-schema-ref-parser'
|
||||
import { z, ZodSchema, ZodTypeAny } from 'zod'
|
||||
import { defaultCode, DynamicStructuredTool, howToUseCode } from './core'
|
||||
import { DataSource } from 'typeorm'
|
||||
|
||||
class OpenAPIToolkit_Tools implements INode {
|
||||
label: string
|
||||
|
|
@ -20,69 +21,265 @@ class OpenAPIToolkit_Tools implements INode {
|
|||
constructor() {
|
||||
this.label = 'OpenAPI Toolkit'
|
||||
this.name = 'openAPIToolkit'
|
||||
this.version = 1.0
|
||||
this.version = 2.0
|
||||
this.type = 'OpenAPIToolkit'
|
||||
this.icon = 'openapi.svg'
|
||||
this.category = 'Tools'
|
||||
this.description = 'Load OpenAPI specification'
|
||||
this.credential = {
|
||||
label: 'Connect Credential',
|
||||
name: 'credential',
|
||||
type: 'credential',
|
||||
description: 'Only needed if the YAML OpenAPI Spec requires authentication',
|
||||
optional: true,
|
||||
credentialNames: ['openAPIAuth']
|
||||
}
|
||||
this.description = 'Load OpenAPI specification, and converts each API endpoint to a tool'
|
||||
this.inputs = [
|
||||
{
|
||||
label: 'Language Model',
|
||||
name: 'model',
|
||||
type: 'BaseLanguageModel'
|
||||
},
|
||||
{
|
||||
label: 'YAML File',
|
||||
name: 'yamlFile',
|
||||
type: 'file',
|
||||
fileType: '.yaml'
|
||||
},
|
||||
{
|
||||
label: 'Return Direct',
|
||||
name: 'returnDirect',
|
||||
description: 'Return the output of the tool directly to the user',
|
||||
type: 'boolean',
|
||||
optional: true
|
||||
},
|
||||
{
|
||||
label: 'Headers',
|
||||
name: 'headers',
|
||||
type: 'json',
|
||||
description: 'Request headers to be sent with the API request. For example, {"Authorization": "Bearer token"}',
|
||||
additionalParams: true,
|
||||
optional: true
|
||||
},
|
||||
{
|
||||
label: 'Custom Code',
|
||||
name: 'customCode',
|
||||
type: 'code',
|
||||
hint: {
|
||||
label: 'How to use',
|
||||
value: howToUseCode
|
||||
},
|
||||
codeExample: defaultCode,
|
||||
description: `Custom code to return the output of the tool. The code should be a function that takes in the input and returns a string`,
|
||||
hideCodeExecute: true,
|
||||
default: defaultCode,
|
||||
additionalParams: true
|
||||
}
|
||||
]
|
||||
this.baseClasses = [this.type, 'Tool']
|
||||
}
|
||||
|
||||
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
|
||||
const model = nodeData.inputs?.model as BaseLanguageModel
|
||||
const toolReturnDirect = nodeData.inputs?.returnDirect as boolean
|
||||
const yamlFileBase64 = nodeData.inputs?.yamlFile as string
|
||||
const customCode = nodeData.inputs?.customCode as string
|
||||
const _headers = nodeData.inputs?.headers as string
|
||||
|
||||
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
|
||||
const openAPIToken = getCredentialParam('openAPIToken', credentialData, nodeData)
|
||||
const headers = typeof _headers === 'object' ? _headers : _headers ? JSON.parse(_headers) : {}
|
||||
|
||||
let data: JsonObject
|
||||
let data
|
||||
if (yamlFileBase64.startsWith('FILE-STORAGE::')) {
|
||||
const file = yamlFileBase64.replace('FILE-STORAGE::', '')
|
||||
const chatflowid = options.chatflowid
|
||||
const fileData = await getFileFromStorage(file, chatflowid)
|
||||
const utf8String = fileData.toString('utf-8')
|
||||
|
||||
data = load(utf8String) as JsonObject
|
||||
data = load(utf8String)
|
||||
} else {
|
||||
const splitDataURI = yamlFileBase64.split(',')
|
||||
splitDataURI.pop()
|
||||
const bf = Buffer.from(splitDataURI.pop() || '', 'base64')
|
||||
const utf8String = bf.toString('utf-8')
|
||||
data = load(utf8String) as JsonObject
|
||||
data = load(utf8String)
|
||||
}
|
||||
if (!data) {
|
||||
throw new Error('Failed to load OpenAPI spec')
|
||||
}
|
||||
|
||||
const headers: ICommonObject = {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
if (openAPIToken) headers.Authorization = `Bearer ${openAPIToken}`
|
||||
const toolkit = new OpenApiToolkit(new JsonSpec(data), model, headers)
|
||||
const _data: any = await $RefParser.dereference(data)
|
||||
|
||||
return toolkit.tools
|
||||
const baseUrl = _data.servers[0]?.url
|
||||
if (!baseUrl) {
|
||||
throw new Error('OpenAPI spec does not contain a server URL')
|
||||
}
|
||||
|
||||
const appDataSource = options.appDataSource as DataSource
|
||||
const databaseEntities = options.databaseEntities as IDatabaseEntity
|
||||
const variables = await getVars(appDataSource, databaseEntities, nodeData)
|
||||
|
||||
const flow = { chatflowId: options.chatflowid }
|
||||
|
||||
const tools = getTools(_data.paths, baseUrl, headers, variables, flow, toolReturnDirect, customCode)
|
||||
return tools
|
||||
}
|
||||
}
|
||||
|
||||
const jsonSchemaToZodSchema = (schema: any, requiredList: string[], keyName: string): ZodSchema<any> => {
|
||||
if (schema.properties) {
|
||||
// Handle object types by recursively processing properties
|
||||
const zodShape: Record<string, ZodTypeAny> = {}
|
||||
for (const key in schema.properties) {
|
||||
zodShape[key] = jsonSchemaToZodSchema(schema.properties[key], requiredList, key)
|
||||
}
|
||||
return z.object(zodShape)
|
||||
} else if (schema.oneOf) {
|
||||
// Handle oneOf by mapping each option to a Zod schema
|
||||
const zodSchemas = schema.oneOf.map((subSchema: any) => jsonSchemaToZodSchema(subSchema, requiredList, keyName))
|
||||
return z.union(zodSchemas)
|
||||
} else if (schema.enum) {
|
||||
// Handle enum types
|
||||
return requiredList.includes(keyName)
|
||||
? z.enum(schema.enum).describe(schema?.description ?? keyName)
|
||||
: z
|
||||
.enum(schema.enum)
|
||||
.describe(schema?.description ?? keyName)
|
||||
.optional()
|
||||
} else if (schema.type === 'string') {
|
||||
return requiredList.includes(keyName)
|
||||
? z.string({ required_error: `${keyName} required` }).describe(schema?.description ?? keyName)
|
||||
: z
|
||||
.string()
|
||||
.describe(schema?.description ?? keyName)
|
||||
.optional()
|
||||
} else if (schema.type === 'array') {
|
||||
return z.array(jsonSchemaToZodSchema(schema.items, requiredList, keyName))
|
||||
} else if (schema.type === 'boolean') {
|
||||
return requiredList.includes(keyName)
|
||||
? z.number({ required_error: `${keyName} required` }).describe(schema?.description ?? keyName)
|
||||
: z
|
||||
.number()
|
||||
.describe(schema?.description ?? keyName)
|
||||
.optional()
|
||||
} else if (schema.type === 'number') {
|
||||
return requiredList.includes(keyName)
|
||||
? z.boolean({ required_error: `${keyName} required` }).describe(schema?.description ?? keyName)
|
||||
: z
|
||||
.boolean()
|
||||
.describe(schema?.description ?? keyName)
|
||||
.optional()
|
||||
}
|
||||
|
||||
// Fallback to unknown type if unrecognized
|
||||
return z.unknown()
|
||||
}
|
||||
|
||||
const extractParameters = (param: ICommonObject, paramZodObj: ICommonObject) => {
|
||||
const paramSchema = param.schema
|
||||
const paramName = param.name
|
||||
const paramDesc = param.description || param.name
|
||||
|
||||
if (paramSchema.type === 'string') {
|
||||
if (param.required) {
|
||||
paramZodObj[paramName] = z.string({ required_error: `${paramName} required` }).describe(paramDesc)
|
||||
} else {
|
||||
paramZodObj[paramName] = z.string().describe(paramDesc).optional()
|
||||
}
|
||||
} else if (paramSchema.type === 'number') {
|
||||
if (param.required) {
|
||||
paramZodObj[paramName] = z.number({ required_error: `${paramName} required` }).describe(paramDesc)
|
||||
} else {
|
||||
paramZodObj[paramName] = z.number().describe(paramDesc).optional()
|
||||
}
|
||||
} else if (paramSchema.type === 'boolean') {
|
||||
if (param.required) {
|
||||
paramZodObj[paramName] = z.boolean({ required_error: `${paramName} required` }).describe(paramDesc)
|
||||
} else {
|
||||
paramZodObj[paramName] = z.boolean().describe(paramDesc).optional()
|
||||
}
|
||||
}
|
||||
|
||||
return paramZodObj
|
||||
}
|
||||
|
||||
const getTools = (
|
||||
paths: any[],
|
||||
baseUrl: string,
|
||||
headers: ICommonObject,
|
||||
variables: IVariable[],
|
||||
flow: ICommonObject,
|
||||
returnDirect: boolean,
|
||||
customCode?: string
|
||||
) => {
|
||||
const tools = []
|
||||
for (const path in paths) {
|
||||
// example of path: "/engines"
|
||||
const methods = paths[path]
|
||||
for (const method in methods) {
|
||||
// example of method: "get"
|
||||
const spec = methods[method]
|
||||
const toolName = spec.operationId
|
||||
const toolDesc = spec.description || spec.summary || toolName
|
||||
|
||||
let zodObj: ICommonObject = {}
|
||||
if (spec.parameters) {
|
||||
// Get parameters with in = path
|
||||
let paramZodObjPath: ICommonObject = {}
|
||||
for (const param of spec.parameters.filter((param: any) => param.in === 'path')) {
|
||||
paramZodObjPath = extractParameters(param, paramZodObjPath)
|
||||
}
|
||||
|
||||
// Get parameters with in = query
|
||||
let paramZodObjQuery: ICommonObject = {}
|
||||
for (const param of spec.parameters.filter((param: any) => param.in === 'query')) {
|
||||
paramZodObjQuery = extractParameters(param, paramZodObjQuery)
|
||||
}
|
||||
|
||||
// Combine path and query parameters
|
||||
zodObj = {
|
||||
...zodObj,
|
||||
PathParameters: z.object(paramZodObjPath),
|
||||
QueryParameters: z.object(paramZodObjQuery)
|
||||
}
|
||||
}
|
||||
|
||||
if (spec.requestBody) {
|
||||
let content: any = {}
|
||||
if (spec.requestBody.content['application/json']) {
|
||||
content = spec.requestBody.content['application/json']
|
||||
} else if (spec.requestBody.content['application/x-www-form-urlencoded']) {
|
||||
content = spec.requestBody.content['application/x-www-form-urlencoded']
|
||||
} else if (spec.requestBody.content['multipart/form-data']) {
|
||||
content = spec.requestBody.content['multipart/form-data']
|
||||
} else if (spec.requestBody.content['text/plain']) {
|
||||
content = spec.requestBody.content['text/plain']
|
||||
}
|
||||
const requestBodySchema = content.schema
|
||||
if (requestBodySchema) {
|
||||
const requiredList = requestBodySchema.required || []
|
||||
const requestBodyZodObj = jsonSchemaToZodSchema(requestBodySchema, requiredList, 'properties')
|
||||
zodObj = {
|
||||
...zodObj,
|
||||
RequestBody: requestBodyZodObj
|
||||
}
|
||||
} else {
|
||||
zodObj = {
|
||||
...zodObj,
|
||||
input: z.string().describe('Query input').optional()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!spec.parameters && !spec.requestBody) {
|
||||
zodObj = {
|
||||
input: z.string().describe('Query input').optional()
|
||||
}
|
||||
}
|
||||
|
||||
const toolObj = {
|
||||
name: toolName,
|
||||
description: toolDesc,
|
||||
schema: z.object(zodObj),
|
||||
baseUrl: `${baseUrl}${path}`,
|
||||
method: method,
|
||||
headers,
|
||||
customCode
|
||||
}
|
||||
|
||||
const dynamicStructuredTool = new DynamicStructuredTool(toolObj)
|
||||
dynamicStructuredTool.setVariables(variables)
|
||||
dynamicStructuredTool.setFlowObject(flow)
|
||||
dynamicStructuredTool.returnDirect = returnDirect
|
||||
tools.push(dynamicStructuredTool)
|
||||
}
|
||||
}
|
||||
return tools
|
||||
}
|
||||
|
||||
module.exports = { nodeClass: OpenAPIToolkit_Tools }
|
||||
|
|
|
|||
|
|
@ -1,140 +1,256 @@
|
|||
import jsonpointer from 'jsonpointer'
|
||||
import { Serializable } from '@langchain/core/load/serializable'
|
||||
import { Tool, ToolParams } from '@langchain/core/tools'
|
||||
import { z } from 'zod'
|
||||
import { RequestInit } from 'node-fetch'
|
||||
import { NodeVM } from '@flowiseai/nodevm'
|
||||
import { RunnableConfig } from '@langchain/core/runnables'
|
||||
import { StructuredTool, ToolParams } from '@langchain/core/tools'
|
||||
import { CallbackManagerForToolRun, Callbacks, CallbackManager, parseCallbackConfigArg } from '@langchain/core/callbacks/manager'
|
||||
import { availableDependencies, defaultAllowBuiltInDep, prepareSandboxVars } from '../../../src/utils'
|
||||
import { ICommonObject } from '../../../src/Interface'
|
||||
|
||||
export type Json = string | number | boolean | null | { [key: string]: Json } | Json[]
|
||||
interface HttpRequestObject {
|
||||
PathParameters?: Record<string, any>
|
||||
QueryParameters?: Record<string, any>
|
||||
RequestBody?: Record<string, any>
|
||||
}
|
||||
|
||||
export type JsonObject = { [key: string]: Json }
|
||||
export const defaultCode = `const fetch = require('node-fetch');
|
||||
const url = $url;
|
||||
const options = $options;
|
||||
|
||||
/**
|
||||
* Represents a JSON object in the LangChain framework. Provides methods
|
||||
* to get keys and values from the JSON object.
|
||||
*/
|
||||
export class JsonSpec extends Serializable {
|
||||
lc_namespace = ['langchain', 'tools', 'json']
|
||||
try {
|
||||
const response = await fetch(url, options);
|
||||
const resp = await response.json();
|
||||
return JSON.stringify(resp);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return '';
|
||||
}
|
||||
`
|
||||
export const howToUseCode = `- **Libraries:**
|
||||
You can use any libraries imported in Flowise.
|
||||
|
||||
obj: JsonObject
|
||||
- **Tool Input Arguments:**
|
||||
Tool input arguments are available as the following variables:
|
||||
- \`$PathParameters\`
|
||||
- \`$QueryParameters\`
|
||||
- \`$RequestBody\`
|
||||
|
||||
maxValueLength = 4000
|
||||
- **HTTP Requests:**
|
||||
By default, you can get the following values for making HTTP requests:
|
||||
- \`$url\`
|
||||
- \`$options\`
|
||||
|
||||
constructor(obj: JsonObject, max_value_length = 4000) {
|
||||
super(...arguments)
|
||||
this.obj = obj
|
||||
this.maxValueLength = max_value_length
|
||||
- **Default Flow Config:**
|
||||
You can access the default flow configuration using these variables:
|
||||
- \`$flow.sessionId\`
|
||||
- \`$flow.chatId\`
|
||||
- \`$flow.chatflowId\`
|
||||
- \`$flow.input\`
|
||||
- \`$flow.state\`
|
||||
|
||||
- **Custom Variables:**
|
||||
You can get custom variables using the syntax:
|
||||
- \`$vars.<variable-name>\`
|
||||
|
||||
- **Return Value:**
|
||||
The function must return a **string** value at the end.
|
||||
|
||||
\`\`\`js
|
||||
${defaultCode}
|
||||
\`\`\`
|
||||
`
|
||||
|
||||
const getUrl = (baseUrl: string, requestObject: HttpRequestObject) => {
|
||||
let url = baseUrl
|
||||
|
||||
// Add PathParameters to URL if present
|
||||
if (requestObject.PathParameters) {
|
||||
for (const [key, value] of Object.entries(requestObject.PathParameters)) {
|
||||
url = url.replace(`{${key}}`, encodeURIComponent(String(value)))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all keys at a given path in the JSON object.
|
||||
* @param input The path to the keys in the JSON object, provided as a string in JSON pointer syntax.
|
||||
* @returns A string containing all keys at the given path, separated by commas.
|
||||
*/
|
||||
public getKeys(input: string): string {
|
||||
const pointer = jsonpointer.compile(input)
|
||||
const res = pointer.get(this.obj) as Json
|
||||
if (typeof res === 'object' && !Array.isArray(res) && res !== null) {
|
||||
return Object.keys(res)
|
||||
.map((i) => i.replaceAll('~', '~0').replaceAll('/', '~1'))
|
||||
.join(', ')
|
||||
}
|
||||
|
||||
throw new Error(`Value at ${input} is not a dictionary, get the value directly instead.`)
|
||||
// Add QueryParameters to URL if present
|
||||
if (requestObject.QueryParameters) {
|
||||
const queryParams = new URLSearchParams(requestObject.QueryParameters as Record<string, string>)
|
||||
url += `?${queryParams.toString()}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the value at a given path in the JSON object.
|
||||
* @param input The path to the value in the JSON object, provided as a string in JSON pointer syntax.
|
||||
* @returns The value at the given path in the JSON object, as a string. If the value is a large dictionary or exceeds the maximum length, a message is returned instead.
|
||||
*/
|
||||
public getValue(input: string): string {
|
||||
const pointer = jsonpointer.compile(input)
|
||||
const res = pointer.get(this.obj) as Json
|
||||
return url
|
||||
}
|
||||
|
||||
if (res === null || res === undefined) {
|
||||
throw new Error(`Value at ${input} is null or undefined.`)
|
||||
}
|
||||
class ToolInputParsingException extends Error {
|
||||
output?: string
|
||||
|
||||
const str = typeof res === 'object' ? JSON.stringify(res) : res.toString()
|
||||
if (typeof res === 'object' && !Array.isArray(res) && str.length > this.maxValueLength) {
|
||||
return `Value is a large dictionary, should explore its keys directly.`
|
||||
}
|
||||
|
||||
if (str.length > this.maxValueLength) {
|
||||
return `${str.slice(0, this.maxValueLength)}...`
|
||||
}
|
||||
return str
|
||||
constructor(message: string, output?: string) {
|
||||
super(message)
|
||||
this.output = output
|
||||
}
|
||||
}
|
||||
|
||||
export interface JsonToolFields extends ToolParams {
|
||||
jsonSpec: JsonSpec
|
||||
export interface BaseDynamicToolInput extends ToolParams {
|
||||
name: string
|
||||
description: string
|
||||
returnDirect?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* A tool in the LangChain framework that lists all keys at a given path
|
||||
* in a JSON object.
|
||||
*/
|
||||
export class JsonListKeysTool extends Tool {
|
||||
static lc_name() {
|
||||
return 'JsonListKeysTool'
|
||||
}
|
||||
export interface DynamicStructuredToolInput<
|
||||
// eslint-disable-next-line
|
||||
T extends z.ZodObject<any, any, any, any> = z.ZodObject<any, any, any, any>
|
||||
> extends BaseDynamicToolInput {
|
||||
func?: (input: z.infer<T>, runManager?: CallbackManagerForToolRun) => Promise<string>
|
||||
schema: T
|
||||
baseUrl: string
|
||||
method: string
|
||||
headers: ICommonObject
|
||||
customCode?: string
|
||||
}
|
||||
|
||||
name = 'json_list_keys'
|
||||
export class DynamicStructuredTool<
|
||||
// eslint-disable-next-line
|
||||
T extends z.ZodObject<any, any, any, any> = z.ZodObject<any, any, any, any>
|
||||
> extends StructuredTool {
|
||||
name: string
|
||||
|
||||
jsonSpec: JsonSpec
|
||||
description: string
|
||||
|
||||
constructor(jsonSpec: JsonSpec)
|
||||
baseUrl: string
|
||||
|
||||
constructor(fields: JsonToolFields)
|
||||
method: string
|
||||
|
||||
constructor(fields: JsonSpec | JsonToolFields) {
|
||||
if (!('jsonSpec' in fields)) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
fields = { jsonSpec: fields }
|
||||
}
|
||||
headers: ICommonObject
|
||||
|
||||
customCode?: string
|
||||
|
||||
func: DynamicStructuredToolInput['func']
|
||||
|
||||
// @ts-ignore
|
||||
schema: T
|
||||
private variables: any[]
|
||||
private flowObj: any
|
||||
|
||||
constructor(fields: DynamicStructuredToolInput<T>) {
|
||||
super(fields)
|
||||
|
||||
this.jsonSpec = fields.jsonSpec
|
||||
this.name = fields.name
|
||||
this.description = fields.description
|
||||
this.func = fields.func
|
||||
this.returnDirect = fields.returnDirect ?? this.returnDirect
|
||||
this.schema = fields.schema
|
||||
this.baseUrl = fields.baseUrl
|
||||
this.method = fields.method
|
||||
this.headers = fields.headers
|
||||
this.customCode = fields.customCode
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
async _call(input: string) {
|
||||
try {
|
||||
return this.jsonSpec.getKeys(input)
|
||||
} catch (error) {
|
||||
return `${error}`
|
||||
async call(
|
||||
arg: z.output<T>,
|
||||
configArg?: RunnableConfig | Callbacks,
|
||||
tags?: string[],
|
||||
flowConfig?: { sessionId?: string; chatId?: string; input?: string; state?: ICommonObject }
|
||||
): Promise<string> {
|
||||
const config = parseCallbackConfigArg(configArg)
|
||||
if (config.runName === undefined) {
|
||||
config.runName = this.name
|
||||
}
|
||||
}
|
||||
|
||||
description = `Can be used to list all keys at a given path.
|
||||
Before calling this you should be SURE that the path to this exists.
|
||||
The input is a text representation of the path to the json as json pointer syntax (e.g. /key1/0/key2).`
|
||||
}
|
||||
|
||||
/**
|
||||
* A tool in the LangChain framework that retrieves the value at a given
|
||||
* path in a JSON object.
|
||||
*/
|
||||
export class JsonGetValueTool extends Tool {
|
||||
static lc_name() {
|
||||
return 'JsonGetValueTool'
|
||||
}
|
||||
|
||||
name = 'json_get_value'
|
||||
|
||||
constructor(public jsonSpec: JsonSpec) {
|
||||
super()
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
async _call(input: string) {
|
||||
let parsed
|
||||
try {
|
||||
return this.jsonSpec.getValue(input)
|
||||
} catch (error) {
|
||||
return `${error}`
|
||||
parsed = await this.schema.parseAsync(arg)
|
||||
} catch (e) {
|
||||
throw new ToolInputParsingException(`Received tool input did not match expected schema`, JSON.stringify(arg))
|
||||
}
|
||||
const callbackManager_ = await CallbackManager.configure(
|
||||
config.callbacks,
|
||||
this.callbacks,
|
||||
config.tags || tags,
|
||||
this.tags,
|
||||
config.metadata,
|
||||
this.metadata,
|
||||
{ verbose: this.verbose }
|
||||
)
|
||||
const runManager = await callbackManager_?.handleToolStart(
|
||||
this.toJSON(),
|
||||
typeof parsed === 'string' ? parsed : JSON.stringify(parsed),
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
config.runName
|
||||
)
|
||||
let result
|
||||
try {
|
||||
result = await this._call(parsed, runManager, flowConfig)
|
||||
} catch (e) {
|
||||
await runManager?.handleToolError(e)
|
||||
throw e
|
||||
}
|
||||
if (result && typeof result !== 'string') {
|
||||
result = JSON.stringify(result)
|
||||
}
|
||||
await runManager?.handleToolEnd(result)
|
||||
return result
|
||||
}
|
||||
|
||||
description = `Can be used to see value in string format at a given path.
|
||||
Before calling this you should be SURE that the path to this exists.
|
||||
The input is a text representation of the path to the json as json pointer syntax (e.g. /key1/0/key2).`
|
||||
// @ts-ignore
|
||||
protected async _call(
|
||||
arg: z.output<T>,
|
||||
_?: CallbackManagerForToolRun,
|
||||
flowConfig?: { sessionId?: string; chatId?: string; input?: string; state?: ICommonObject }
|
||||
): Promise<string> {
|
||||
let sandbox: any = {}
|
||||
if (typeof arg === 'object' && Object.keys(arg).length) {
|
||||
for (const item in arg) {
|
||||
sandbox[`$${item}`] = arg[item]
|
||||
}
|
||||
}
|
||||
|
||||
sandbox['$vars'] = prepareSandboxVars(this.variables)
|
||||
|
||||
// inject flow properties
|
||||
if (this.flowObj) {
|
||||
sandbox['$flow'] = { ...this.flowObj, ...flowConfig }
|
||||
}
|
||||
|
||||
const callOptions: RequestInit = {
|
||||
method: this.method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...this.headers
|
||||
}
|
||||
}
|
||||
if (arg.RequestBody && this.method.toUpperCase() !== 'GET') {
|
||||
callOptions.body = JSON.stringify(arg.RequestBody)
|
||||
}
|
||||
sandbox['$options'] = callOptions
|
||||
|
||||
const completeUrl = getUrl(this.baseUrl, arg)
|
||||
sandbox['$url'] = completeUrl
|
||||
|
||||
const builtinDeps = process.env.TOOL_FUNCTION_BUILTIN_DEP
|
||||
? defaultAllowBuiltInDep.concat(process.env.TOOL_FUNCTION_BUILTIN_DEP.split(','))
|
||||
: defaultAllowBuiltInDep
|
||||
const externalDeps = process.env.TOOL_FUNCTION_EXTERNAL_DEP ? process.env.TOOL_FUNCTION_EXTERNAL_DEP.split(',') : []
|
||||
const deps = availableDependencies.concat(externalDeps)
|
||||
|
||||
const options = {
|
||||
console: 'inherit',
|
||||
sandbox,
|
||||
require: {
|
||||
external: { modules: deps },
|
||||
builtin: builtinDeps
|
||||
}
|
||||
} as any
|
||||
|
||||
const vm = new NodeVM(options)
|
||||
const response = await vm.run(`module.exports = async function() {${this.customCode || defaultCode}}()`, __dirname)
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
setVariables(variables: any[]) {
|
||||
this.variables = variables
|
||||
}
|
||||
|
||||
setFlowObject(flow: any) {
|
||||
this.flowObj = flow
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
},
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"dependencies": {
|
||||
"@apidevtools/json-schema-ref-parser": "^11.7.0",
|
||||
"@aws-sdk/client-bedrock-runtime": "3.422.0",
|
||||
"@aws-sdk/client-dynamodb": "^3.360.0",
|
||||
"@aws-sdk/client-s3": "^3.427.0",
|
||||
|
|
|
|||
65089
pnpm-lock.yaml
65089
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue