144 lines
4.4 KiB
TypeScript
144 lines
4.4 KiB
TypeScript
import { z } from 'zod'
|
|
import { RunnableConfig } from '@langchain/core/runnables'
|
|
import { StructuredTool, ToolParams } from '@langchain/core/tools'
|
|
import { CallbackManagerForToolRun, Callbacks, CallbackManager, parseCallbackConfigArg } from '@langchain/core/callbacks/manager'
|
|
import { executeJavaScriptCode, createCodeExecutionSandbox } from '../../../src/utils'
|
|
import { ICommonObject } from '../../../src/Interface'
|
|
|
|
class ToolInputParsingException extends Error {
|
|
output?: string
|
|
|
|
constructor(message: string, output?: string) {
|
|
super(message)
|
|
this.output = output
|
|
}
|
|
}
|
|
|
|
export interface BaseDynamicToolInput extends ToolParams {
|
|
name: string
|
|
description: string
|
|
code: string
|
|
returnDirect?: boolean
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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
|
|
|
|
description: string
|
|
|
|
code: string
|
|
|
|
func: DynamicStructuredToolInput['func']
|
|
|
|
// @ts-ignore
|
|
schema: T
|
|
private variables: any[]
|
|
private flowObj: any
|
|
|
|
constructor(fields: DynamicStructuredToolInput<T>) {
|
|
super(fields)
|
|
this.name = fields.name
|
|
this.description = fields.description
|
|
this.code = fields.code
|
|
this.func = fields.func
|
|
this.returnDirect = fields.returnDirect ?? this.returnDirect
|
|
this.schema = fields.schema
|
|
}
|
|
|
|
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
|
|
}
|
|
let parsed
|
|
try {
|
|
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
|
|
}
|
|
|
|
// @ts-ignore
|
|
protected async _call(
|
|
arg: z.output<T>,
|
|
_?: CallbackManagerForToolRun,
|
|
flowConfig?: { sessionId?: string; chatId?: string; input?: string; state?: ICommonObject }
|
|
): Promise<string> {
|
|
// Create additional sandbox variables for tool arguments
|
|
const additionalSandbox: ICommonObject = {}
|
|
|
|
if (typeof arg === 'object' && Object.keys(arg).length) {
|
|
for (const item in arg) {
|
|
additionalSandbox[`$${item}`] = arg[item]
|
|
}
|
|
}
|
|
|
|
// Prepare flow object for sandbox
|
|
const flow = this.flowObj ? { ...this.flowObj, ...flowConfig } : {}
|
|
|
|
const sandbox = createCodeExecutionSandbox('', this.variables || [], flow, additionalSandbox)
|
|
|
|
let response = await executeJavaScriptCode(this.code, sandbox)
|
|
|
|
if (typeof response === 'object') {
|
|
response = JSON.stringify(response)
|
|
}
|
|
|
|
return response
|
|
}
|
|
|
|
setVariables(variables: any[]) {
|
|
this.variables = variables
|
|
}
|
|
|
|
setFlowObject(flow: any) {
|
|
this.flowObj = flow
|
|
}
|
|
}
|