From 527ddeca85c4b884c5a2b97861d8446e8b619807 Mon Sep 17 00:00:00 2001 From: bracesproul Date: Tue, 16 Jul 2024 15:49:27 -0700 Subject: [PATCH 01/12] core[patch]: Allow runnable tools to take single string inputs --- langchain-core/src/runnables/base.ts | 10 +++++----- .../src/runnables/tests/runnable_tools.test.ts | 13 +++++++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/langchain-core/src/runnables/base.ts b/langchain-core/src/runnables/base.ts index ac099057ffc3..cf76ee0e464d 100644 --- a/langchain-core/src/runnables/base.ts +++ b/langchain-core/src/runnables/base.ts @@ -1094,7 +1094,7 @@ export abstract class Runnable< asTool(fields: { name?: string; description?: string; - schema: z.ZodType; + schema: z.ZodType | z.ZodString; }): RunnableToolLike, RunOutput> { return convertRunnableToTool(this, fields); } @@ -2805,7 +2805,7 @@ export class RunnablePick< } export interface RunnableToolLikeArgs< - RunInput extends z.ZodType = z.ZodType, + RunInput extends z.ZodType | z.ZodString = z.ZodType, RunOutput = unknown > extends Omit, RunOutput>, "config"> { name: string; @@ -2818,7 +2818,7 @@ export interface RunnableToolLikeArgs< } export class RunnableToolLike< - RunInput extends z.ZodType = z.ZodType, + RunInput extends z.ZodType | z.ZodString = z.ZodType, RunOutput = unknown > extends RunnableBinding, RunOutput> { name: string; @@ -2861,7 +2861,7 @@ export function convertRunnableToTool( fields: { name?: string; description?: string; - schema: z.ZodType; + schema: z.ZodType | z.ZodString; } ): RunnableToolLike, RunOutput> { const name = fields.name ?? runnable.getName(); @@ -2870,7 +2870,7 @@ export function convertRunnableToTool( return new RunnableToolLike, RunOutput>({ name, description, - schema: fields.schema, + schema: fields.schema as z.ZodType, bound: runnable, }); } diff --git a/langchain-core/src/runnables/tests/runnable_tools.test.ts b/langchain-core/src/runnables/tests/runnable_tools.test.ts index 4c16aea077c7..a143510503c7 100644 --- a/langchain-core/src/runnables/tests/runnable_tools.test.ts +++ b/langchain-core/src/runnables/tests/runnable_tools.test.ts @@ -137,3 +137,16 @@ test("Runnable asTool uses Zod schema description if not provided", async () => expect(tool.description).toBe(description); }); + +test("Runnable asTool can accept a string zod schema", async () => { + const lambda = RunnableLambda.from((input) => { + return `${input}a`; + }).asTool({ + name: "string_tool", + description: "A tool that appends 'a' to the input string", + schema: z.string(), + }); + + const result = await lambda.invoke("b"); + expect(result).toBe("ba"); +}) From 22ad9322339392ff45b9fdc3350ae7bc5f5ce168 Mon Sep 17 00:00:00 2001 From: bracesproul Date: Tue, 16 Jul 2024 15:52:51 -0700 Subject: [PATCH 02/12] add test for tool func --- langchain-core/src/tools/tests/tools.test.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/langchain-core/src/tools/tests/tools.test.ts b/langchain-core/src/tools/tests/tools.test.ts index b514b99c1827..1bcb776c318f 100644 --- a/langchain-core/src/tools/tests/tools.test.ts +++ b/langchain-core/src/tools/tests/tools.test.ts @@ -99,3 +99,15 @@ test("Returns tool message if responseFormat is content_and_artifact and returns expect(toolResult.artifact).toEqual({ location: "San Francisco" }); expect(toolResult.name).toBe("weather"); }); + +test("Tool can accept single string input", async () => { + const stringTool = tool((input: string): string => { + return `${input}a`; + }, { + name: "string_tool", + description: "A tool that appends 'a' to the input string", + }); + + const result = await stringTool.invoke("b"); + expect(result).toBe("ba"); +}); \ No newline at end of file From 64dc64b3f515116280fd3589a30bb570642b51b4 Mon Sep 17 00:00:00 2001 From: bracesproul Date: Tue, 16 Jul 2024 15:53:07 -0700 Subject: [PATCH 03/12] chore: lint files --- .../src/runnables/tests/runnable_tools.test.ts | 2 +- langchain-core/src/tools/tests/tools.test.ts | 17 ++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/langchain-core/src/runnables/tests/runnable_tools.test.ts b/langchain-core/src/runnables/tests/runnable_tools.test.ts index a143510503c7..f4bc4a9bb492 100644 --- a/langchain-core/src/runnables/tests/runnable_tools.test.ts +++ b/langchain-core/src/runnables/tests/runnable_tools.test.ts @@ -149,4 +149,4 @@ test("Runnable asTool can accept a string zod schema", async () => { const result = await lambda.invoke("b"); expect(result).toBe("ba"); -}) +}); diff --git a/langchain-core/src/tools/tests/tools.test.ts b/langchain-core/src/tools/tests/tools.test.ts index 1bcb776c318f..b88662227a2f 100644 --- a/langchain-core/src/tools/tests/tools.test.ts +++ b/langchain-core/src/tools/tests/tools.test.ts @@ -101,13 +101,16 @@ test("Returns tool message if responseFormat is content_and_artifact and returns }); test("Tool can accept single string input", async () => { - const stringTool = tool((input: string): string => { - return `${input}a`; - }, { - name: "string_tool", - description: "A tool that appends 'a' to the input string", - }); + const stringTool = tool( + (input: string): string => { + return `${input}a`; + }, + { + name: "string_tool", + description: "A tool that appends 'a' to the input string", + } + ); const result = await stringTool.invoke("b"); expect(result).toBe("ba"); -}); \ No newline at end of file +}); From 8c906ae572b2a317fc326545333c1228020f7929 Mon Sep 17 00:00:00 2001 From: bracesproul Date: Tue, 16 Jul 2024 15:58:55 -0700 Subject: [PATCH 04/12] cr --- langchain-core/src/runnables/base.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/langchain-core/src/runnables/base.ts b/langchain-core/src/runnables/base.ts index cf76ee0e464d..cca8ae35a30a 100644 --- a/langchain-core/src/runnables/base.ts +++ b/langchain-core/src/runnables/base.ts @@ -1094,7 +1094,7 @@ export abstract class Runnable< asTool(fields: { name?: string; description?: string; - schema: z.ZodType | z.ZodString; + schema: z.ZodType; }): RunnableToolLike, RunOutput> { return convertRunnableToTool(this, fields); } @@ -2861,7 +2861,7 @@ export function convertRunnableToTool( fields: { name?: string; description?: string; - schema: z.ZodType | z.ZodString; + schema: z.ZodType; } ): RunnableToolLike, RunOutput> { const name = fields.name ?? runnable.getName(); @@ -2870,7 +2870,7 @@ export function convertRunnableToTool( return new RunnableToolLike, RunOutput>({ name, description, - schema: fields.schema as z.ZodType, + schema: fields.schema, bound: runnable, }); } From bbb27f86118c99be3c4082595e2842d66312456e Mon Sep 17 00:00:00 2001 From: bracesproul Date: Tue, 16 Jul 2024 16:24:42 -0700 Subject: [PATCH 05/12] cr --- langchain-core/src/tools/index.ts | 32 +++++++++++++++----- langchain-core/src/tools/tests/tools.test.ts | 1 + 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/langchain-core/src/tools/index.ts b/langchain-core/src/tools/index.ts index 09a36c41aff9..ee4e0cc8741a 100644 --- a/langchain-core/src/tools/index.ts +++ b/langchain-core/src/tools/index.ts @@ -459,7 +459,7 @@ export abstract class BaseToolkit { * @template {ZodAny} RunInput The input schema for the tool. * @template {any} RunOutput The output type for the tool. */ -interface ToolWrapperParams +interface ToolWrapperParams extends ToolParams { /** * The name of the tool. If using with an LLM, this @@ -504,20 +504,38 @@ interface ToolWrapperParams * * @returns {DynamicStructuredTool} A new StructuredTool instance. */ +export function tool( + func: RunnableFunc, ToolReturnType>, + fields: ToolWrapperParams +): DynamicTool + export function tool( func: RunnableFunc, ToolReturnType>, fields: ToolWrapperParams -): DynamicStructuredTool { - const schema = - fields.schema ?? - z.object({ input: z.string().optional() }).transform((obj) => obj.input); +): DynamicStructuredTool + +export function tool( + func: RunnableFunc, ToolReturnType>, + fields: ToolWrapperParams +): DynamicStructuredTool | DynamicTool { + + // If the schema is not provided, or it's a string schema, create a DynamicTool + if (!fields.schema || !fields.schema.shape) { + return new DynamicTool({ + name: fields.name, + description: fields.description ?? fields.schema?.description ?? `${fields.name} tool`, + responseFormat: fields.responseFormat, + func, + }) + } const description = - fields.description ?? schema.description ?? `${fields.name} tool`; + fields.description ?? fields.schema.description ?? `${fields.name} tool`; + return new DynamicStructuredTool({ name: fields.name, description, - schema: schema as T, + schema: fields.schema, // TODO: Consider moving into DynamicStructuredTool constructor func: async (input, runManager, config) => { return new Promise((resolve, reject) => { diff --git a/langchain-core/src/tools/tests/tools.test.ts b/langchain-core/src/tools/tests/tools.test.ts index b88662227a2f..0dcb5a8b8684 100644 --- a/langchain-core/src/tools/tests/tools.test.ts +++ b/langchain-core/src/tools/tests/tools.test.ts @@ -108,6 +108,7 @@ test("Tool can accept single string input", async () => { { name: "string_tool", description: "A tool that appends 'a' to the input string", + schema: z.string(), } ); From be01959fbc061de8275ffd305853aae6e13b4bb6 Mon Sep 17 00:00:00 2001 From: bracesproul Date: Tue, 16 Jul 2024 16:25:07 -0700 Subject: [PATCH 06/12] cr --- langchain-core/src/tools/index.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/langchain-core/src/tools/index.ts b/langchain-core/src/tools/index.ts index ee4e0cc8741a..bb0c64378433 100644 --- a/langchain-core/src/tools/index.ts +++ b/langchain-core/src/tools/index.ts @@ -507,26 +507,28 @@ interface ToolWrapperParams export function tool( func: RunnableFunc, ToolReturnType>, fields: ToolWrapperParams -): DynamicTool +): DynamicTool; export function tool( func: RunnableFunc, ToolReturnType>, fields: ToolWrapperParams -): DynamicStructuredTool +): DynamicStructuredTool; export function tool( func: RunnableFunc, ToolReturnType>, fields: ToolWrapperParams ): DynamicStructuredTool | DynamicTool { - // If the schema is not provided, or it's a string schema, create a DynamicTool if (!fields.schema || !fields.schema.shape) { return new DynamicTool({ name: fields.name, - description: fields.description ?? fields.schema?.description ?? `${fields.name} tool`, + description: + fields.description ?? + fields.schema?.description ?? + `${fields.name} tool`, responseFormat: fields.responseFormat, func, - }) + }); } const description = From 8f525142d68f26eecc97c58f7f94776a45c4a2eb Mon Sep 17 00:00:00 2001 From: bracesproul Date: Tue, 16 Jul 2024 16:35:19 -0700 Subject: [PATCH 07/12] fix types --- langchain-core/src/runnables/base.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/langchain-core/src/runnables/base.ts b/langchain-core/src/runnables/base.ts index cca8ae35a30a..ac099057ffc3 100644 --- a/langchain-core/src/runnables/base.ts +++ b/langchain-core/src/runnables/base.ts @@ -2805,7 +2805,7 @@ export class RunnablePick< } export interface RunnableToolLikeArgs< - RunInput extends z.ZodType | z.ZodString = z.ZodType, + RunInput extends z.ZodType = z.ZodType, RunOutput = unknown > extends Omit, RunOutput>, "config"> { name: string; @@ -2818,7 +2818,7 @@ export interface RunnableToolLikeArgs< } export class RunnableToolLike< - RunInput extends z.ZodType | z.ZodString = z.ZodType, + RunInput extends z.ZodType = z.ZodType, RunOutput = unknown > extends RunnableBinding, RunOutput> { name: string; From 79e69eea343bd21b112ff8e3989e94e168ed2e5b Mon Sep 17 00:00:00 2001 From: bracesproul Date: Tue, 16 Jul 2024 16:36:48 -0700 Subject: [PATCH 08/12] rename ZodAny to ZodObjectAny --- langchain-core/src/tools/index.ts | 30 ++++++++++++++++-------------- langchain-core/src/types/zod.ts | 2 +- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/langchain-core/src/tools/index.ts b/langchain-core/src/tools/index.ts index bb0c64378433..b58f673cd551 100644 --- a/langchain-core/src/tools/index.ts +++ b/langchain-core/src/tools/index.ts @@ -16,7 +16,7 @@ import { } from "../runnables/config.js"; import type { RunnableFunc, RunnableInterface } from "../runnables/base.js"; import { ToolCall, ToolMessage } from "../messages/tool.js"; -import { ZodAny } from "../types/zod.js"; +import { ZodObjectAny } from "../types/zod.js"; import { MessageContent } from "../messages/base.js"; import { AsyncLocalStorageProviderSingleton } from "../singletons/index.js"; @@ -58,7 +58,7 @@ export class ToolInputParsingException extends Error { } } -export interface StructuredToolInterface +export interface StructuredToolInterface extends RunnableInterface< (z.output extends string ? string : never) | z.input | ToolCall, ToolReturnType @@ -96,7 +96,7 @@ export interface StructuredToolInterface * Base class for Tools that accept input of any shape defined by a Zod schema. */ export abstract class StructuredTool< - T extends ZodAny = ZodAny + T extends ZodObjectAny = ZodObjectAny > extends BaseLangChain< (z.output extends string ? string : never) | z.input | ToolCall, ToolReturnType @@ -259,7 +259,7 @@ export abstract class StructuredTool< } } -export interface ToolInterface +export interface ToolInterface extends StructuredToolInterface { /** * @deprecated Use .invoke() instead. Will be removed in 0.3.0. @@ -279,7 +279,7 @@ export interface ToolInterface /** * Base class for Tools that accept input as a string. */ -export abstract class Tool extends StructuredTool { +export abstract class Tool extends StructuredTool { schema = z .object({ input: z.string().optional() }) .transform((obj) => obj.input); @@ -328,8 +328,9 @@ export interface DynamicToolInput extends BaseDynamicToolInput { /** * Interface for the input parameters of the DynamicStructuredTool class. */ -export interface DynamicStructuredToolInput - extends BaseDynamicToolInput { +export interface DynamicStructuredToolInput< + T extends ZodObjectAny = ZodObjectAny +> extends BaseDynamicToolInput { func: ( input: BaseDynamicToolInput["responseFormat"] extends "content_and_artifact" ? ToolCall @@ -393,7 +394,7 @@ export class DynamicTool extends Tool { * provided function when the tool is called. */ export class DynamicStructuredTool< - T extends ZodAny = ZodAny + T extends ZodObjectAny = ZodObjectAny > extends StructuredTool { static lc_name() { return "DynamicStructuredTool"; @@ -456,11 +457,12 @@ export abstract class BaseToolkit { /** * Parameters for the tool function. - * @template {ZodAny} RunInput The input schema for the tool. + * @template {ZodObjectAny} RunInput The input schema for the tool. * @template {any} RunOutput The output type for the tool. */ -interface ToolWrapperParams - extends ToolParams { +interface ToolWrapperParams< + RunInput extends ZodObjectAny | z.ZodString = ZodObjectAny +> extends ToolParams { /** * The name of the tool. If using with an LLM, this * will be passed as the tool name. @@ -492,7 +494,7 @@ interface ToolWrapperParams /** * Creates a new StructuredTool instance with the provided function, name, description, and schema. * @function - * @template {RunInput extends ZodAny = ZodAny} RunInput The input schema for the tool. This corresponds to the input type when the tool is invoked. + * @template {RunInput extends ZodObjectAny = ZodObjectAny} RunInput The input schema for the tool. This corresponds to the input type when the tool is invoked. * @template {RunOutput = any} RunOutput The output type for the tool. This corresponds to the output type when the tool is invoked. * @template {FuncInput extends z.infer | ToolCall = z.infer} FuncInput The input type for the function. * @@ -509,12 +511,12 @@ export function tool( fields: ToolWrapperParams ): DynamicTool; -export function tool( +export function tool( func: RunnableFunc, ToolReturnType>, fields: ToolWrapperParams ): DynamicStructuredTool; -export function tool( +export function tool( func: RunnableFunc, ToolReturnType>, fields: ToolWrapperParams ): DynamicStructuredTool | DynamicTool { diff --git a/langchain-core/src/types/zod.ts b/langchain-core/src/types/zod.ts index d864170ddafa..faaa92b3bff2 100644 --- a/langchain-core/src/types/zod.ts +++ b/langchain-core/src/types/zod.ts @@ -1,4 +1,4 @@ import type { z } from "zod"; // eslint-disable-next-line @typescript-eslint/no-explicit-any -export type ZodAny = z.ZodObject; +export type ZodObjectAny = z.ZodObject; From fb7d240568b8f400d760a4a77b41af7ba7c0171b Mon Sep 17 00:00:00 2001 From: bracesproul Date: Tue, 16 Jul 2024 16:43:49 -0700 Subject: [PATCH 09/12] docstring nits --- langchain-core/src/tools/index.ts | 16 +++++++--------- langchain-core/src/tools/tests/tools.test.ts | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/langchain-core/src/tools/index.ts b/langchain-core/src/tools/index.ts index b58f673cd551..32ecf3ff41d4 100644 --- a/langchain-core/src/tools/index.ts +++ b/langchain-core/src/tools/index.ts @@ -457,8 +457,7 @@ export abstract class BaseToolkit { /** * Parameters for the tool function. - * @template {ZodObjectAny} RunInput The input schema for the tool. - * @template {any} RunOutput The output type for the tool. + * @template {ZodObjectAny | z.ZodString = ZodObjectAny} RunInput The input schema for the tool. Either any Zod object, or a Zod string. */ interface ToolWrapperParams< RunInput extends ZodObjectAny | z.ZodString = ZodObjectAny @@ -493,18 +492,17 @@ interface ToolWrapperParams< /** * Creates a new StructuredTool instance with the provided function, name, description, and schema. + * * @function - * @template {RunInput extends ZodObjectAny = ZodObjectAny} RunInput The input schema for the tool. This corresponds to the input type when the tool is invoked. - * @template {RunOutput = any} RunOutput The output type for the tool. This corresponds to the output type when the tool is invoked. - * @template {FuncInput extends z.infer | ToolCall = z.infer} FuncInput The input type for the function. + * @template {ZodObjectAny | z.ZodString = ZodObjectAny} T The input schema for the tool. Either any Zod object, or a Zod string. * - * @param {RunnableFunc | ToolCall, RunOutput>} func - The function to invoke when the tool is called. - * @param fields - An object containing the following properties: + * @param {RunnableFunc, ToolReturnType>} func - The function to invoke when the tool is called. + * @param {ToolWrapperParams} fields - An object containing the following properties: * @param {string} fields.name The name of the tool. * @param {string | undefined} fields.description The description of the tool. Defaults to either the description on the Zod schema, or `${fields.name} tool`. - * @param {z.ZodObject} fields.schema The Zod schema defining the input for the tool. + * @param {ZodObjectAny | z.ZodString | undefined} fields.schema The Zod schema defining the input for the tool. If undefined, it will default to a Zod string schema. * - * @returns {DynamicStructuredTool} A new StructuredTool instance. + * @returns {DynamicStructuredTool} A new StructuredTool instance. */ export function tool( func: RunnableFunc, ToolReturnType>, diff --git a/langchain-core/src/tools/tests/tools.test.ts b/langchain-core/src/tools/tests/tools.test.ts index 0dcb5a8b8684..bf577a4a1dc9 100644 --- a/langchain-core/src/tools/tests/tools.test.ts +++ b/langchain-core/src/tools/tests/tools.test.ts @@ -101,7 +101,7 @@ test("Returns tool message if responseFormat is content_and_artifact and returns }); test("Tool can accept single string input", async () => { - const stringTool = tool( + const stringTool = tool( (input: string): string => { return `${input}a`; }, From 3ef9ac17169dcba13b3376ea7324e4361809db19 Mon Sep 17 00:00:00 2001 From: bracesproul Date: Tue, 16 Jul 2024 17:57:33 -0700 Subject: [PATCH 10/12] fiox --- examples/package.json | 1 + langchain-core/src/runnables/base.ts | 47 +++++++++++++++++-- .../runnables/tests/runnable_tools.test.ts | 29 ++++++++++++ langchain-core/src/tools/index.ts | 27 ++--------- langchain-core/src/tools/tool_exception.ts | 13 +++++ langchain-core/src/tools/utils.ts | 10 ++++ yarn.lock | 17 +++++++ 7 files changed, 116 insertions(+), 28 deletions(-) create mode 100644 langchain-core/src/tools/tool_exception.ts create mode 100644 langchain-core/src/tools/utils.ts diff --git a/examples/package.json b/examples/package.json index 56fb064ed456..8f81162219bd 100644 --- a/examples/package.json +++ b/examples/package.json @@ -48,6 +48,7 @@ "@langchain/google-vertexai": "workspace:*", "@langchain/google-vertexai-web": "workspace:*", "@langchain/groq": "workspace:*", + "@langchain/langgraph": "^0.0.28", "@langchain/mistralai": "workspace:*", "@langchain/mongodb": "workspace:*", "@langchain/nomic": "workspace:*", diff --git a/langchain-core/src/runnables/base.ts b/langchain-core/src/runnables/base.ts index ac099057ffc3..95b6210a5fec 100644 --- a/langchain-core/src/runnables/base.ts +++ b/langchain-core/src/runnables/base.ts @@ -52,6 +52,9 @@ import { isIterableIterator, isIterator, } from "./iter.js"; +import { ToolInputParsingException } from "../tools/tool_exception.js"; +import { _isToolCall } from "../tools/utils.js"; +import { ToolCall } from "../messages/tool.js"; export { type RunnableInterface, RunnableBatchOptions }; @@ -1095,7 +1098,7 @@ export abstract class Runnable< name?: string; description?: string; schema: z.ZodType; - }): RunnableToolLike, RunOutput> { + }): RunnableToolLike, RunOutput> { return convertRunnableToTool(this, fields); } } @@ -2828,8 +2831,29 @@ export class RunnableToolLike< schema: RunInput; constructor(fields: RunnableToolLikeArgs) { + const sequence = RunnableSequence.from([ + RunnableLambda.from(async (input) => { + let toolInput: z.TypeOf; + + if (_isToolCall(input)) { + try { + toolInput = await this.schema.parseAsync(input.args); + } catch (e) { + throw new ToolInputParsingException( + `Received tool input did not match expected schema`, + JSON.stringify(input.args) + ); + } + } else { + toolInput = input; + } + return toolInput; + }).withConfig({ runName: `${fields.name}:parse_input` }), + fields.bound, + ]).withConfig({ runName: fields.name }); + super({ - bound: fields.bound, + bound: sequence, config: fields.config ?? {}, }); @@ -2863,11 +2887,24 @@ export function convertRunnableToTool( description?: string; schema: z.ZodType; } -): RunnableToolLike, RunOutput> { +): RunnableToolLike, RunOutput> { const name = fields.name ?? runnable.getName(); - const description = fields.description ?? fields.schema.description; + const description = fields.description ?? fields.schema?.description; + + if (fields.schema.constructor === z.ZodString) { + return new RunnableToolLike, RunOutput>({ + name, + description, + schema: z + .object({ + input: z.string(), + }) + .transform((input) => input.input) as z.ZodType, + bound: runnable, + }); + } - return new RunnableToolLike, RunOutput>({ + return new RunnableToolLike, RunOutput>({ name, description, schema: fields.schema, diff --git a/langchain-core/src/runnables/tests/runnable_tools.test.ts b/langchain-core/src/runnables/tests/runnable_tools.test.ts index f4bc4a9bb492..b8ac95cf4940 100644 --- a/langchain-core/src/runnables/tests/runnable_tools.test.ts +++ b/langchain-core/src/runnables/tests/runnable_tools.test.ts @@ -1,5 +1,7 @@ import { z } from "zod"; import { RunnableLambda, RunnableToolLike } from "../base.js"; +import { FakeRetriever } from "../../utils/testing/index.js"; +import { Document } from "../../documents/document.js"; test("Runnable asTool works", async () => { const schema = z.object({ @@ -150,3 +152,30 @@ test("Runnable asTool can accept a string zod schema", async () => { const result = await lambda.invoke("b"); expect(result).toBe("ba"); }); + +test("Runnables which dont accept ToolCalls as inputs can accept ToolCalls", async () => { + const pageContent = "Dogs are pretty cool, man!"; + const retriever = new FakeRetriever({ + output: [ + new Document({ + pageContent, + }), + ], + }); + const tool = retriever.asTool({ + name: "pet_info_retriever", + description: "Get information about pets.", + schema: z.string(), + }); + + const result = await tool.invoke({ + type: "tool_call", + name: "pet_info_retriever", + args: { + input: "dogs", + }, + id: "string", + }); + expect(result).toHaveLength(1); + expect(result[0].pageContent).toBe(pageContent); +}); diff --git a/langchain-core/src/tools/index.ts b/langchain-core/src/tools/index.ts index 32ecf3ff41d4..f154cd0c6f78 100644 --- a/langchain-core/src/tools/index.ts +++ b/langchain-core/src/tools/index.ts @@ -19,6 +19,10 @@ import { ToolCall, ToolMessage } from "../messages/tool.js"; import { ZodObjectAny } from "../types/zod.js"; import { MessageContent } from "../messages/base.js"; import { AsyncLocalStorageProviderSingleton } from "../singletons/index.js"; +import { ToolInputParsingException } from "./tool_exception.js"; +import { _isToolCall } from "./utils.js"; + +export { ToolInputParsingException }; export type ResponseFormat = "content" | "content_and_artifact" | string; @@ -44,20 +48,6 @@ export interface ToolParams extends BaseLangChainParams { responseFormat?: ResponseFormat; } -/** - * Custom error class used to handle exceptions related to tool input parsing. - * It extends the built-in `Error` class and adds an optional `output` - * property that can hold the output that caused the exception. - */ -export class ToolInputParsingException extends Error { - output?: string; - - constructor(message: string, output?: string) { - super(message); - this.output = output; - } -} - export interface StructuredToolInterface extends RunnableInterface< (z.output extends string ? string : never) | z.input | ToolCall, @@ -560,15 +550,6 @@ export function tool( }); } -function _isToolCall(toolCall?: unknown): toolCall is ToolCall { - return !!( - toolCall && - typeof toolCall === "object" && - "type" in toolCall && - toolCall.type === "tool_call" - ); -} - function _formatToolOutput(params: { content: unknown; name: string; diff --git a/langchain-core/src/tools/tool_exception.ts b/langchain-core/src/tools/tool_exception.ts new file mode 100644 index 000000000000..4b8898540723 --- /dev/null +++ b/langchain-core/src/tools/tool_exception.ts @@ -0,0 +1,13 @@ +/** + * Custom error class used to handle exceptions related to tool input parsing. + * It extends the built-in `Error` class and adds an optional `output` + * property that can hold the output that caused the exception. + */ +export class ToolInputParsingException extends Error { + output?: string; + + constructor(message: string, output?: string) { + super(message); + this.output = output; + } +} diff --git a/langchain-core/src/tools/utils.ts b/langchain-core/src/tools/utils.ts new file mode 100644 index 000000000000..341ab9051dca --- /dev/null +++ b/langchain-core/src/tools/utils.ts @@ -0,0 +1,10 @@ +import { ToolCall } from "../messages/tool.js"; + +export function _isToolCall(toolCall?: unknown): toolCall is ToolCall { + return !!( + toolCall && + typeof toolCall === "object" && + "type" in toolCall && + toolCall.type === "tool_call" + ); +} diff --git a/yarn.lock b/yarn.lock index 2497a15f11f9..8fcc427d9b2e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11341,6 +11341,22 @@ __metadata: languageName: node linkType: hard +"@langchain/langgraph@npm:^0.0.28": + version: 0.0.28 + resolution: "@langchain/langgraph@npm:0.0.28" + dependencies: + "@langchain/core": ">=0.2.16 <0.3.0" + uuid: ^10.0.0 + zod: ^3.23.8 + peerDependencies: + better-sqlite3: ^9.5.0 + peerDependenciesMeta: + better-sqlite3: + optional: true + checksum: 1465791026ccd6eaa13a2f2d03b8fb9f0972a8c23b9da1cfd581074f413ea60ef860de6d704c6a3b49f7425f23d6ba49c23255167ae83ab7d70dc00cc0560ce2 + languageName: node + linkType: hard + "@langchain/mistralai@workspace:*, @langchain/mistralai@workspace:libs/langchain-mistralai": version: 0.0.0-use.local resolution: "@langchain/mistralai@workspace:libs/langchain-mistralai" @@ -24929,6 +24945,7 @@ __metadata: "@langchain/google-vertexai": "workspace:*" "@langchain/google-vertexai-web": "workspace:*" "@langchain/groq": "workspace:*" + "@langchain/langgraph": ^0.0.28 "@langchain/mistralai": "workspace:*" "@langchain/mongodb": "workspace:*" "@langchain/nomic": "workspace:*" From f233db6b2a06b843e0f10b45064983a532c73010 Mon Sep 17 00:00:00 2001 From: bracesproul Date: Wed, 17 Jul 2024 09:20:03 -0700 Subject: [PATCH 11/12] cr --- langchain-core/src/runnables/base.ts | 3 +-- langchain-core/src/tools/index.ts | 3 +-- langchain-core/src/tools/tool_exception.ts | 13 ------------- langchain-core/src/tools/utils.ts | 14 ++++++++++++++ 4 files changed, 16 insertions(+), 17 deletions(-) delete mode 100644 langchain-core/src/tools/tool_exception.ts diff --git a/langchain-core/src/runnables/base.ts b/langchain-core/src/runnables/base.ts index 95b6210a5fec..ea0f00bd402b 100644 --- a/langchain-core/src/runnables/base.ts +++ b/langchain-core/src/runnables/base.ts @@ -52,8 +52,7 @@ import { isIterableIterator, isIterator, } from "./iter.js"; -import { ToolInputParsingException } from "../tools/tool_exception.js"; -import { _isToolCall } from "../tools/utils.js"; +import { _isToolCall, ToolInputParsingException } from "../tools/utils.js"; import { ToolCall } from "../messages/tool.js"; export { type RunnableInterface, RunnableBatchOptions }; diff --git a/langchain-core/src/tools/index.ts b/langchain-core/src/tools/index.ts index f154cd0c6f78..ed2c3848bb5b 100644 --- a/langchain-core/src/tools/index.ts +++ b/langchain-core/src/tools/index.ts @@ -19,8 +19,7 @@ import { ToolCall, ToolMessage } from "../messages/tool.js"; import { ZodObjectAny } from "../types/zod.js"; import { MessageContent } from "../messages/base.js"; import { AsyncLocalStorageProviderSingleton } from "../singletons/index.js"; -import { ToolInputParsingException } from "./tool_exception.js"; -import { _isToolCall } from "./utils.js"; +import { _isToolCall, ToolInputParsingException } from "./utils.js"; export { ToolInputParsingException }; diff --git a/langchain-core/src/tools/tool_exception.ts b/langchain-core/src/tools/tool_exception.ts deleted file mode 100644 index 4b8898540723..000000000000 --- a/langchain-core/src/tools/tool_exception.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Custom error class used to handle exceptions related to tool input parsing. - * It extends the built-in `Error` class and adds an optional `output` - * property that can hold the output that caused the exception. - */ -export class ToolInputParsingException extends Error { - output?: string; - - constructor(message: string, output?: string) { - super(message); - this.output = output; - } -} diff --git a/langchain-core/src/tools/utils.ts b/langchain-core/src/tools/utils.ts index 341ab9051dca..b9c5bd8b384d 100644 --- a/langchain-core/src/tools/utils.ts +++ b/langchain-core/src/tools/utils.ts @@ -8,3 +8,17 @@ export function _isToolCall(toolCall?: unknown): toolCall is ToolCall { toolCall.type === "tool_call" ); } + +/** + * Custom error class used to handle exceptions related to tool input parsing. + * It extends the built-in `Error` class and adds an optional `output` + * property that can hold the output that caused the exception. + */ +export class ToolInputParsingException extends Error { + output?: string; + + constructor(message: string, output?: string) { + super(message); + this.output = output; + } +} From a545f9aa5e0cd3c3027b2c111c652b4055afe933 Mon Sep 17 00:00:00 2001 From: bracesproul Date: Wed, 17 Jul 2024 10:27:20 -0700 Subject: [PATCH 12/12] cr --- langchain-core/src/tools/index.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/langchain-core/src/tools/index.ts b/langchain-core/src/tools/index.ts index ed2c3848bb5b..8286bb7cc33d 100644 --- a/langchain-core/src/tools/index.ts +++ b/langchain-core/src/tools/index.ts @@ -503,12 +503,14 @@ export function tool( fields: ToolWrapperParams ): DynamicStructuredTool; -export function tool( +export function tool( func: RunnableFunc, ToolReturnType>, fields: ToolWrapperParams -): DynamicStructuredTool | DynamicTool { +): + | DynamicStructuredTool + | DynamicTool { // If the schema is not provided, or it's a string schema, create a DynamicTool - if (!fields.schema || !fields.schema.shape) { + if (!fields.schema || !("shape" in fields.schema) || !fields.schema.shape) { return new DynamicTool({ name: fields.name, description: @@ -523,10 +525,10 @@ export function tool( const description = fields.description ?? fields.schema.description ?? `${fields.name} tool`; - return new DynamicStructuredTool({ + return new DynamicStructuredTool({ name: fields.name, description, - schema: fields.schema, + schema: fields.schema as T extends ZodObjectAny ? T : ZodObjectAny, // TODO: Consider moving into DynamicStructuredTool constructor func: async (input, runManager, config) => { return new Promise((resolve, reject) => {