import type { Item, RunnableNodeConfig } from "../types"; import type { NodeBackedToolConfigOptions, ZodSchemaAny } from "./AiHost"; import { AgentConfigInspector } from "./AgentConfigInspectorFactory"; import { NodeBackedToolConfig } from "./NodeBackedToolConfig"; class AgentToolFactoryImpl { asTool< TNodeConfig extends RunnableNodeConfig, TInputSchema extends ZodSchemaAny, TOutputSchema extends ZodSchemaAny, >( node: TNodeConfig, options: Readonly<{ name?: string } & NodeBackedToolConfigOptions>, ): NodeBackedToolConfig { return new NodeBackedToolConfig( options.name ?? node.name ?? "tool", node, this.withDefaultAgentInputMapper(node, options), ); } private withDefaultAgentInputMapper< TNodeConfig extends RunnableNodeConfig, TInputSchema extends ZodSchemaAny, TOutputSchema extends ZodSchemaAny, >( node: TNodeConfig, options: Readonly<{ name?: string } & NodeBackedToolConfigOptions>, ): Readonly<{ name?: string } & NodeBackedToolConfigOptions> { if (options.mapInput || !AgentConfigInspector.isAgentNodeConfig(node)) { return options; } return { ...options, mapInput: ({ input, item }) => this.mergeAgentToolInputWithCurrentItem(input, item) as never, }; } private mergeAgentToolInputWithCurrentItem(input: unknown, item: Item): unknown { if (!this.isMergeableRecord(input) || !this.isMergeableRecord(item.json)) { return input; } return { ...item.json, ...input, }; } private isMergeableRecord(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value); } } export const AgentToolFactory = new AgentToolFactoryImpl();