diff --git a/langchain-core/src/language_models/chat_models.ts b/langchain-core/src/language_models/chat_models.ts index bb952e43d918..9239ff52c03b 100644 --- a/langchain-core/src/language_models/chat_models.ts +++ b/langchain-core/src/language_models/chat_models.ts @@ -33,7 +33,10 @@ import { } from "../callbacks/manager.js"; import type { RunnableConfig } from "../runnables/config.js"; import type { BaseCache } from "../caches/base.js"; -import { StructuredToolInterface } from "../tools/index.js"; +import { + StructuredToolInterface, + StructuredToolParams, +} from "../tools/index.js"; import { Runnable, RunnableLambda, @@ -123,6 +126,14 @@ export type LangSmithParams = { ls_stop?: Array; }; +export type BindToolsInput = + | StructuredToolInterface + // eslint-disable-next-line @typescript-eslint/no-explicit-any + | Record + | ToolDefinition + | RunnableToolLike + | StructuredToolParams; + /** * Base class for chat models. It extends the BaseLanguageModel class and * provides methods for generating chat based on input messages. @@ -168,12 +179,7 @@ export abstract class BaseChatModel< * @param kwargs Any additional parameters to bind. */ bindTools?( - tools: ( - | StructuredToolInterface - | Record - | ToolDefinition - | RunnableToolLike - )[], + tools: BindToolsInput[], kwargs?: Partial ): Runnable; diff --git a/langchain-core/src/tools/index.ts b/langchain-core/src/tools/index.ts index a2c488e58bca..3a73a0b53dde 100644 --- a/langchain-core/src/tools/index.ts +++ b/langchain-core/src/tools/index.ts @@ -48,6 +48,14 @@ export interface ToolParams extends BaseLangChainParams { responseFormat?: ResponseFormat; } +export interface StructuredToolParams + extends Pick { + /** + * An optional description of the tool to pass to the model. + */ + description?: string; +} + export interface StructuredToolInterface extends RunnableInterface< (z.output extends string ? string : never) | z.input | ToolCall, @@ -55,6 +63,9 @@ export interface StructuredToolInterface > { lc_namespace: string[]; + /** + * A Zod schema representing the parameters of the tool. + */ schema: T | z.ZodEffects; /** @@ -75,8 +86,14 @@ export interface StructuredToolInterface tags?: string[] ): Promise; + /** + * The name of the tool. + */ name: string; + /** + * A description of the tool. + */ description: string; returnDirect: boolean; diff --git a/langchain-core/src/utils/function_calling.ts b/langchain-core/src/utils/function_calling.ts index 38a976f75d7b..243d593c55e0 100644 --- a/langchain-core/src/utils/function_calling.ts +++ b/langchain-core/src/utils/function_calling.ts @@ -1,7 +1,11 @@ import { zodToJsonSchema } from "zod-to-json-schema"; -import { StructuredToolInterface } from "../tools/index.js"; +import { + StructuredToolInterface, + StructuredToolParams, +} from "../tools/index.js"; import { FunctionDefinition, ToolDefinition } from "../language_models/base.js"; import { Runnable, RunnableToolLike } from "../runnables/base.js"; +import { isZodSchema } from "./types/is_zod_schema.js"; /** * Formats a `StructuredTool` or `RunnableToolLike` instance into a format @@ -13,7 +17,7 @@ import { Runnable, RunnableToolLike } from "../runnables/base.js"; * @returns {FunctionDefinition} The inputted tool in OpenAI function format. */ export function convertToOpenAIFunction( - tool: StructuredToolInterface | RunnableToolLike, + tool: StructuredToolInterface | RunnableToolLike | StructuredToolParams, fields?: | { /** @@ -111,6 +115,25 @@ export function isRunnableToolLike(tool?: unknown): tool is RunnableToolLike { ); } +/** + * Confirm whether or not the tool contains the necessary properties to be considered a `StructuredToolParams`. + * + * @param {unknown | undefined} tool The object to check if it is a `StructuredToolParams`. + * @returns {tool is StructuredToolParams} Whether the inputted object is a `StructuredToolParams`. + */ +export function isStructuredToolParams( + tool?: unknown +): tool is StructuredToolParams { + return ( + !!tool && + typeof tool === "object" && + "name" in tool && + "schema" in tool && + // eslint-disable-next-line @typescript-eslint/no-explicit-any + isZodSchema(tool.schema as Record) + ); +} + /** * Whether or not the tool is one of StructuredTool, RunnableTool or StructuredToolParams. * It returns `is StructuredToolParams` since that is the most minimal interface of the three, @@ -119,10 +142,9 @@ export function isRunnableToolLike(tool?: unknown): tool is RunnableToolLike { * @param {unknown | undefined} tool The tool to check if it is a LangChain tool. * @returns {tool is StructuredToolParams} Whether the inputted tool is a LangChain tool. */ -export function isLangChainTool( - tool?: unknown -): tool is StructuredToolInterface { +export function isLangChainTool(tool?: unknown): tool is StructuredToolParams { return ( + isStructuredToolParams(tool) || isRunnableToolLike(tool) || // eslint-disable-next-line @typescript-eslint/no-explicit-any isStructuredTool(tool as any) diff --git a/langchain/src/chat_models/universal.ts b/langchain/src/chat_models/universal.ts index 3afbdc0d46f3..e17b30ad69cd 100644 --- a/langchain/src/chat_models/universal.ts +++ b/langchain/src/chat_models/universal.ts @@ -5,6 +5,7 @@ import { import { BaseChatModel, BaseChatModelParams, + BindToolsInput, type BaseChatModelCallOptions, } from "@langchain/core/language_models/chat_models"; import { BaseMessage, type AIMessageChunk } from "@langchain/core/messages"; @@ -298,13 +299,7 @@ class _ConfigurableModel< } override bindTools( - tools: ( - | StructuredToolInterface - // eslint-disable-next-line @typescript-eslint/no-explicit-any - | Record - | ToolDefinition - | RunnableToolLike - )[], + tools: BindToolsInput[], // eslint-disable-next-line @typescript-eslint/no-explicit-any params?: Record ): _ConfigurableModel { diff --git a/libs/langchain-anthropic/src/chat_models.ts b/libs/langchain-anthropic/src/chat_models.ts index e3521e7dc5e2..32631c57f370 100644 --- a/libs/langchain-anthropic/src/chat_models.ts +++ b/libs/langchain-anthropic/src/chat_models.ts @@ -17,14 +17,12 @@ import { type ToolDefinition, isOpenAITool, } from "@langchain/core/language_models/base"; -import { StructuredToolInterface } from "@langchain/core/tools"; import { zodToJsonSchema } from "zod-to-json-schema"; import { BaseLLMOutputParser } from "@langchain/core/output_parsers"; import { Runnable, RunnablePassthrough, RunnableSequence, - RunnableToolLike, } from "@langchain/core/runnables"; import { isZodSchema } from "@langchain/core/utils/types"; import { z } from "zod"; @@ -33,13 +31,9 @@ import type { Tool as AnthropicTool, } from "@anthropic-ai/sdk/resources/index.mjs"; +import { isLangChainTool } from "@langchain/core/utils/function_calling"; import { AnthropicToolsOutputParser } from "./output_parsers.js"; -import { - AnthropicToolChoice, - AnthropicToolTypes, - extractToolCallChunk, - handleToolChoice, -} from "./utils/tools.js"; +import { extractToolCallChunk, handleToolChoice } from "./utils/tools.js"; import { _formatMessagesForAnthropic } from "./utils/message_inputs.js"; import { _makeMessageChunkFromAnthropicEvent, @@ -50,12 +44,14 @@ import { AnthropicMessageStreamEvent, AnthropicRequestOptions, AnthropicStreamingMessageCreateParams, + AnthropicToolChoice, + ChatAnthropicToolType, } from "./types.js"; export interface ChatAnthropicCallOptions extends BaseChatModelCallOptions, Pick { - tools?: AnthropicToolTypes[]; + tools?: ChatAnthropicToolType[]; /** * Whether or not to specify what tool the model should use * @default "auto" @@ -344,21 +340,19 @@ export class ChatAnthropicMessages< throw new Error(`Can not pass in a mix of tool schemas to ChatAnthropic`); } - return (tools as StructuredToolInterface[]).map((tool) => ({ - name: tool.name, - description: tool.description, - input_schema: zodToJsonSchema(tool.schema) as AnthropicTool.InputSchema, - })); + if (tools.every(isLangChainTool)) { + return tools.map((t) => ({ + name: t.name, + description: t.description, + input_schema: zodToJsonSchema(t.schema) as AnthropicTool.InputSchema, + })); + } + + throw new Error("Unsupported tool type passed to ChatAnthropic"); } override bindTools( - tools: ( - | AnthropicTool - | Record - | StructuredToolInterface - | ToolDefinition - | RunnableToolLike - )[], + tools: ChatAnthropicToolType[], kwargs?: Partial ): Runnable { return this.bind({ diff --git a/libs/langchain-anthropic/src/types.ts b/libs/langchain-anthropic/src/types.ts index 5da8c43a8038..ff84cec243eb 100644 --- a/libs/langchain-anthropic/src/types.ts +++ b/libs/langchain-anthropic/src/types.ts @@ -1,4 +1,6 @@ import Anthropic from "@anthropic-ai/sdk"; +import type { Tool as AnthropicTool } from "@anthropic-ai/sdk/resources/index.mjs"; +import { BindToolsInput } from "@langchain/core/language_models/chat_models"; export type AnthropicToolResponse = { type: "tool_use"; @@ -17,3 +19,13 @@ export type AnthropicStreamingMessageCreateParams = Anthropic.MessageCreateParamsStreaming; export type AnthropicMessageStreamEvent = Anthropic.MessageStreamEvent; export type AnthropicRequestOptions = Anthropic.RequestOptions; +export type AnthropicToolChoice = + | { + type: "tool"; + name: string; + } + | "any" + | "auto" + | "none" + | string; +export type ChatAnthropicToolType = AnthropicTool | BindToolsInput; diff --git a/libs/langchain-anthropic/src/utils/tools.ts b/libs/langchain-anthropic/src/utils/tools.ts index 3c5c1688e41c..a56bd22e2a49 100644 --- a/libs/langchain-anthropic/src/utils/tools.ts +++ b/libs/langchain-anthropic/src/utils/tools.ts @@ -1,29 +1,7 @@ -import type { - MessageCreateParams, - Tool as AnthropicTool, -} from "@anthropic-ai/sdk/resources/index.mjs"; -import { ToolDefinition } from "@langchain/core/language_models/base"; +import type { MessageCreateParams } from "@anthropic-ai/sdk/resources/index.mjs"; import { AIMessageChunk } from "@langchain/core/messages"; import { ToolCallChunk } from "@langchain/core/messages/tool"; -import { RunnableToolLike } from "@langchain/core/runnables"; -import { StructuredToolInterface } from "@langchain/core/tools"; - -export type AnthropicToolChoice = - | { - type: "tool"; - name: string; - } - | "any" - | "auto" - | "none" - | string; - -export type AnthropicToolTypes = - | StructuredToolInterface - | AnthropicTool - | Record - | ToolDefinition - | RunnableToolLike; +import { AnthropicToolChoice } from "../types.js"; export function handleToolChoice( toolChoice?: AnthropicToolChoice diff --git a/libs/langchain-aws/src/chat_models.ts b/libs/langchain-aws/src/chat_models.ts index 96b47993c3a6..5abeaa518248 100644 --- a/libs/langchain-aws/src/chat_models.ts +++ b/libs/langchain-aws/src/chat_models.ts @@ -1,9 +1,6 @@ import type { BaseMessage } from "@langchain/core/messages"; import { AIMessageChunk } from "@langchain/core/messages"; -import type { - ToolDefinition, - BaseLanguageModelInput, -} from "@langchain/core/language_models/base"; +import type { BaseLanguageModelInput } from "@langchain/core/language_models/base"; import { CallbackManagerForLLMRun } from "@langchain/core/callbacks/manager"; import { type BaseChatModelParams, @@ -13,7 +10,6 @@ import { } from "@langchain/core/language_models/chat_models"; import type { ToolConfiguration, - Tool as BedrockTool, GuardrailConfiguration, } from "@aws-sdk/client-bedrock-runtime"; import { @@ -28,9 +24,12 @@ import { DefaultProviderInit, } from "@aws-sdk/credential-provider-node"; import type { DocumentType as __DocumentType } from "@smithy/types"; -import { StructuredToolInterface } from "@langchain/core/tools"; -import { Runnable, RunnableToolLike } from "@langchain/core/runnables"; -import { ConverseCommandParams, CredentialType } from "./types.js"; +import { Runnable } from "@langchain/core/runnables"; +import { + ChatBedrockConverseToolType, + ConverseCommandParams, + CredentialType, +} from "./types.js"; import { convertToConverseTools, convertToBedrockToolChoice, @@ -135,7 +134,7 @@ export interface ChatBedrockConverseCallOptions */ stop?: string[]; - tools?: (StructuredToolInterface | ToolDefinition | BedrockTool)[]; + tools?: ChatBedrockConverseToolType[]; /** * Tool choice for the model. If passing a string, it must be "any", "auto" or the @@ -280,14 +279,7 @@ export class ChatBedrockConverse } override bindTools( - tools: ( - | StructuredToolInterface - | BedrockTool - | ToolDefinition - // eslint-disable-next-line @typescript-eslint/no-explicit-any - | Record - | RunnableToolLike - )[], + tools: ChatBedrockConverseToolType[], kwargs?: Partial ): Runnable< BaseLanguageModelInput, diff --git a/libs/langchain-aws/src/common.ts b/libs/langchain-aws/src/common.ts index eb00b5b78dbf..1e201dc8a97c 100644 --- a/libs/langchain-aws/src/common.ts +++ b/libs/langchain-aws/src/common.ts @@ -9,7 +9,6 @@ import { ToolMessage, } from "@langchain/core/messages"; import type { ToolCall } from "@langchain/core/messages/tool"; -import type { ToolDefinition } from "@langchain/core/language_models/base"; import { isOpenAITool } from "@langchain/core/language_models/base"; import type { Message as BedrockMessage, @@ -23,12 +22,10 @@ import type { ContentBlockStartEvent, } from "@aws-sdk/client-bedrock-runtime"; import type { DocumentType as __DocumentType } from "@smithy/types"; -import { StructuredToolInterface } from "@langchain/core/tools"; -import { isStructuredTool } from "@langchain/core/utils/function_calling"; +import { isLangChainTool } from "@langchain/core/utils/function_calling"; import { zodToJsonSchema } from "zod-to-json-schema"; import { ChatGenerationChunk } from "@langchain/core/outputs"; -import { RunnableToolLike } from "@langchain/core/runnables"; -import { BedrockToolChoice } from "./types.js"; +import { ChatBedrockConverseToolType, BedrockToolChoice } from "./types.js"; export function extractImageInfo(base64: string): ContentBlock.ImageMember { // Extract the format from the base64 string @@ -235,14 +232,7 @@ export function isBedrockTool(tool: unknown): tool is BedrockTool { } export function convertToConverseTools( - tools: ( - | StructuredToolInterface - | ToolDefinition - | BedrockTool - // eslint-disable-next-line @typescript-eslint/no-explicit-any - | Record - | RunnableToolLike - )[] + tools: ChatBedrockConverseToolType[] ): BedrockTool[] { if (tools.every(isOpenAITool)) { return tools.map((tool) => ({ @@ -254,7 +244,7 @@ export function convertToConverseTools( }, }, })); - } else if (tools.every(isStructuredTool)) { + } else if (tools.every(isLangChainTool)) { return tools.map((tool) => ({ toolSpec: { name: tool.name, diff --git a/libs/langchain-aws/src/types.ts b/libs/langchain-aws/src/types.ts index 0296c5e90e99..876a6979c2bf 100644 --- a/libs/langchain-aws/src/types.ts +++ b/libs/langchain-aws/src/types.ts @@ -1,6 +1,10 @@ -import type { ToolChoice } from "@aws-sdk/client-bedrock-runtime"; +import type { + ToolChoice, + Tool as BedrockTool, +} from "@aws-sdk/client-bedrock-runtime"; import type { AwsCredentialIdentity, Provider } from "@aws-sdk/types"; import { ConverseCommand } from "@aws-sdk/client-bedrock-runtime"; +import { BindToolsInput } from "@langchain/core/language_models/chat_models"; export type CredentialType = | AwsCredentialIdentity @@ -14,3 +18,4 @@ export type BedrockToolChoice = | ToolChoice.AnyMember | ToolChoice.AutoMember | ToolChoice.ToolMember; +export type ChatBedrockConverseToolType = BindToolsInput | BedrockTool; diff --git a/libs/langchain-cohere/src/chat_models.ts b/libs/langchain-cohere/src/chat_models.ts index 749300e99c86..cfae9e4a1f85 100644 --- a/libs/langchain-cohere/src/chat_models.ts +++ b/libs/langchain-cohere/src/chat_models.ts @@ -12,16 +12,16 @@ import { } from "@langchain/core/messages"; import { BaseLanguageModelInput, - ToolDefinition, isOpenAITool, } from "@langchain/core/language_models/base"; -import { isStructuredTool } from "@langchain/core/utils/function_calling"; +import { isLangChainTool } from "@langchain/core/utils/function_calling"; import { CallbackManagerForLLMRun } from "@langchain/core/callbacks/manager"; import { type BaseChatModelParams, BaseChatModel, LangSmithParams, BaseChatModelCallOptions, + BindToolsInput, } from "@langchain/core/language_models/chat_models"; import { ChatGeneration, @@ -37,8 +37,9 @@ import { ToolCallChunk, } from "@langchain/core/messages/tool"; import * as uuid from "uuid"; -import { StructuredToolInterface } from "@langchain/core/tools"; -import { Runnable, RunnableToolLike } from "@langchain/core/runnables"; +import { Runnable } from "@langchain/core/runnables"; + +type ChatCohereToolType = BindToolsInput | Cohere.Tool; /** * Input interface for ChatCohere @@ -88,13 +89,7 @@ export interface ChatCohereCallOptions Partial>, Partial>, Pick { - tools?: ( - | StructuredToolInterface - | Cohere.Tool - | Record - | ToolDefinition - | RunnableToolLike - )[]; + tools?: ChatCohereToolType[]; } /** @deprecated Import as ChatCohereCallOptions instead. */ @@ -256,12 +251,12 @@ function _formatToolsToCohere( ), }; }); - } else if (tools.every(isStructuredTool)) { + } else if (tools.every(isLangChainTool)) { return tools.map((tool) => { const parameterDefinitionsFromZod = zodToJsonSchema(tool.schema); return { name: tool.name, - description: tool.description, + description: tool.description ?? "", parameterDefinitions: _convertJsonSchemaToCohereTool( parameterDefinitionsFromZod ), @@ -371,13 +366,7 @@ export class ChatCohere< } override bindTools( - tools: ( - | Cohere.Tool - | Record - | StructuredToolInterface - | ToolDefinition - | RunnableToolLike - )[], + tools: ChatCohereToolType[], kwargs?: Partial ): Runnable { return this.bind({ diff --git a/libs/langchain-community/src/chat_models/bedrock/web.ts b/libs/langchain-community/src/chat_models/bedrock/web.ts index e066c69b2922..348af5c2cd68 100644 --- a/libs/langchain-community/src/chat_models/bedrock/web.ts +++ b/libs/langchain-community/src/chat_models/bedrock/web.ts @@ -10,13 +10,13 @@ import { BaseChatModel, LangSmithParams, BaseChatModelCallOptions, + BindToolsInput, } from "@langchain/core/language_models/chat_models"; import { BaseLanguageModelInput, - ToolDefinition, isOpenAITool, } from "@langchain/core/language_models/base"; -import { Runnable, RunnableToolLike } from "@langchain/core/runnables"; +import { Runnable } from "@langchain/core/runnables"; import { getEnvironmentVariable } from "@langchain/core/utils/env"; import { AIMessageChunk, @@ -31,8 +31,10 @@ import { ChatGenerationChunk, ChatResult, } from "@langchain/core/outputs"; -import { StructuredToolInterface } from "@langchain/core/tools"; -import { isStructuredTool } from "@langchain/core/utils/function_calling"; +import { + isLangChainTool, + isStructuredTool, +} from "@langchain/core/utils/function_calling"; import { zodToJsonSchema } from "zod-to-json-schema"; import type { SerializedFields } from "../../load/map_keys.js"; @@ -48,6 +50,8 @@ import { type AnthropicTool = Record; +type BedrockChatToolType = BindToolsInput | AnthropicTool; + const PRELUDE_TOTAL_LENGTH_BYTES = 4; function convertOneMessageToText( @@ -112,29 +116,27 @@ function formatTools(tools: BedrockChatCallOptions["tools"]): AnthropicTool[] { if (!tools || !tools.length) { return []; } - if (tools.every((tc) => isStructuredTool(tc))) { - return (tools as StructuredToolInterface[]).map((tc) => ({ + if (tools.every(isLangChainTool)) { + return tools.map((tc) => ({ name: tc.name, description: tc.description, input_schema: zodToJsonSchema(tc.schema), })); } - if (tools.every((tc) => isOpenAITool(tc))) { - return (tools as ToolDefinition[]).map((tc) => ({ + if (tools.every(isOpenAITool)) { + return tools.map((tc) => ({ name: tc.function.name, description: tc.function.description, input_schema: tc.function.parameters, })); } - - if (tools.every((tc) => isAnthropicTool(tc))) { - return tools as AnthropicTool[]; + if (tools.every(isAnthropicTool)) { + return tools; } - if ( - tools.some((tc) => isStructuredTool(tc)) || - tools.some((tc) => isOpenAITool(tc)) || - tools.some((tc) => isAnthropicTool(tc)) + tools.some(isStructuredTool) || + tools.some(isOpenAITool) || + tools.some(isAnthropicTool) ) { throw new Error( "All tools passed to BedrockChat must be of the same type." @@ -144,12 +146,7 @@ function formatTools(tools: BedrockChatCallOptions["tools"]): AnthropicTool[] { } export interface BedrockChatCallOptions extends BaseChatModelCallOptions { - tools?: ( - | StructuredToolInterface - | AnthropicTool - | ToolDefinition - | RunnableToolLike - )[]; + tools?: BedrockChatToolType[]; } export interface BedrockChatFields @@ -734,12 +731,7 @@ export class BedrockChat } override bindTools( - tools: ( - | StructuredToolInterface - | AnthropicTool - | ToolDefinition - | RunnableToolLike - )[], + tools: BedrockChatToolType[], _kwargs?: Partial ): Runnable< BaseLanguageModelInput, diff --git a/libs/langchain-google-common/src/chat_models.ts b/libs/langchain-google-common/src/chat_models.ts index 8d3884e94295..d6f7b40c5956 100644 --- a/libs/langchain-google-common/src/chat_models.ts +++ b/libs/langchain-google-common/src/chat_models.ts @@ -12,19 +12,16 @@ import { AIMessageChunk } from "@langchain/core/messages"; import { BaseLanguageModelInput, StructuredOutputMethodOptions, - ToolDefinition, } from "@langchain/core/language_models/base"; import type { z } from "zod"; import { Runnable, RunnablePassthrough, RunnableSequence, - RunnableToolLike, } from "@langchain/core/runnables"; import { JsonOutputKeyToolsParser } from "@langchain/core/output_parsers/openai_tools"; import { BaseLLMOutputParser } from "@langchain/core/output_parsers"; import { AsyncCaller } from "@langchain/core/utils/async_caller"; -import { StructuredToolInterface } from "@langchain/core/tools"; import { concat } from "@langchain/core/utils/stream"; import { GoogleAIBaseLLMInput, @@ -57,6 +54,7 @@ import type { GoogleAISafetyParams, GeminiFunctionDeclaration, GeminiFunctionSchema, + GoogleAIToolType, } from "./types.js"; import { zodToGeminiParameters } from "./utils/zod_to_gemini_parameters.js"; @@ -292,12 +290,7 @@ export abstract class ChatGoogleBase } override bindTools( - tools: ( - | StructuredToolInterface - | Record - | ToolDefinition - | RunnableToolLike - )[], + tools: GoogleAIToolType[], kwargs?: Partial ): Runnable< BaseLanguageModelInput, diff --git a/libs/langchain-google-common/src/connection.ts b/libs/langchain-google-common/src/connection.ts index cdc923922b07..bf73c4078759 100644 --- a/libs/langchain-google-common/src/connection.ts +++ b/libs/langchain-google-common/src/connection.ts @@ -4,7 +4,8 @@ import { AsyncCallerCallOptions, } from "@langchain/core/utils/async_caller"; import { getRuntimeEnvironment } from "@langchain/core/utils/env"; -import { StructuredToolInterface } from "@langchain/core/tools"; +import { StructuredToolParams } from "@langchain/core/tools"; +import { isLangChainTool } from "@langchain/core/utils/function_calling"; import type { GoogleAIBaseLLMInput, GoogleConnectionParams, @@ -19,6 +20,7 @@ import type { GeminiTool, GeminiFunctionDeclaration, GoogleAIModelRequestParams, + GoogleAIToolType, } from "./types.js"; import { GoogleAbstractedClient, @@ -299,28 +301,18 @@ export abstract class AbstractGoogleLLMConnection< return {} as GeminiContent; } - // Borrowed from the OpenAI invocation params test - isStructuredToolArray(tools?: unknown[]): tools is StructuredToolInterface[] { - return ( - tools !== undefined && - tools.every((tool) => - Array.isArray((tool as StructuredToolInterface).lc_namespace) - ) - ); - } - structuredToolToFunctionDeclaration( - tool: StructuredToolInterface + tool: StructuredToolParams ): GeminiFunctionDeclaration { const jsonSchema = zodToGeminiParameters(tool.schema); return { name: tool.name, - description: tool.description, + description: tool.description ?? `A function available to call.`, parameters: jsonSchema, }; } - structuredToolsToGeminiTools(tools: StructuredToolInterface[]): GeminiTool[] { + structuredToolsToGeminiTools(tools: StructuredToolParams[]): GeminiTool[] { return [ { functionDeclarations: tools.map( @@ -334,16 +326,19 @@ export abstract class AbstractGoogleLLMConnection< _input: MessageType, parameters: GoogleAIModelRequestParams ): GeminiTool[] { - const tools: StructuredToolInterface[] | GeminiTool[] | undefined = - parameters?.tools; + const tools: GoogleAIToolType[] | undefined = parameters?.tools; if (!tools || tools.length === 0) { return []; } - if (this.isStructuredToolArray(tools)) { + if (tools.every(isLangChainTool)) { return this.structuredToolsToGeminiTools(tools); } else { - if (tools.length === 1 && !tools[0].functionDeclarations?.length) { + if ( + tools.length === 1 && + (!("functionDeclarations" in tools[0]) || + !tools[0].functionDeclarations?.length) + ) { return []; } return tools as GeminiTool[]; diff --git a/libs/langchain-google-common/src/types.ts b/libs/langchain-google-common/src/types.ts index 305b7a7eda78..7721b5136704 100644 --- a/libs/langchain-google-common/src/types.ts +++ b/libs/langchain-google-common/src/types.ts @@ -1,6 +1,8 @@ import type { BaseLLMParams } from "@langchain/core/language_models/llms"; -import { StructuredToolInterface } from "@langchain/core/tools"; -import type { BaseChatModelCallOptions } from "@langchain/core/language_models/chat_models"; +import type { + BaseChatModelCallOptions, + BindToolsInput, +} from "@langchain/core/language_models/chat_models"; import type { JsonStream } from "./utils/stream.js"; /** @@ -113,11 +115,13 @@ export interface GoogleAIModelParams { streaming?: boolean; } +export type GoogleAIToolType = BindToolsInput | GeminiTool; + /** * The params which can be passed to the API at request time. */ export interface GoogleAIModelRequestParams extends GoogleAIModelParams { - tools?: StructuredToolInterface[] | GeminiTool[]; + tools?: GoogleAIToolType[]; /** * Force the model to use tools in a specific way. * diff --git a/libs/langchain-google-common/src/utils/common.ts b/libs/langchain-google-common/src/utils/common.ts index 17f29185811c..b3aa2cba7b4b 100644 --- a/libs/langchain-google-common/src/utils/common.ts +++ b/libs/langchain-google-common/src/utils/common.ts @@ -1,10 +1,5 @@ -import { StructuredToolInterface } from "@langchain/core/tools"; -import { - isOpenAITool, - ToolDefinition, -} from "@langchain/core/language_models/base"; -import { RunnableToolLike } from "@langchain/core/runnables"; -import { isStructuredTool } from "@langchain/core/utils/function_calling"; +import { isOpenAITool } from "@langchain/core/language_models/base"; +import { isLangChainTool } from "@langchain/core/utils/function_calling"; import { isModelGemini, validateGeminiParams } from "./gemini.js"; import type { GeminiFunctionDeclaration, @@ -13,6 +8,7 @@ import type { GoogleAIBaseLanguageModelCallOptions, GoogleAIModelParams, GoogleAIModelRequestParams, + GoogleAIToolType, GoogleLLMModelFamily, } from "../types.js"; import { @@ -64,35 +60,28 @@ function processToolChoice( throw new Error("Object inputs for tool_choice not supported."); } -export function convertToGeminiTools( - structuredTools: ( - | StructuredToolInterface - | Record - | ToolDefinition - | RunnableToolLike - )[] -): GeminiTool[] { - const tools: GeminiTool[] = [ +export function convertToGeminiTools(tools: GoogleAIToolType[]): GeminiTool[] { + const geminiTools: GeminiTool[] = [ { functionDeclarations: [], }, ]; - structuredTools.forEach((tool) => { + tools.forEach((tool) => { if ( "functionDeclarations" in tool && Array.isArray(tool.functionDeclarations) ) { const funcs: GeminiFunctionDeclaration[] = tool.functionDeclarations; - tools[0].functionDeclarations?.push(...funcs); - } else if (isStructuredTool(tool)) { + geminiTools[0].functionDeclarations?.push(...funcs); + } else if (isLangChainTool(tool)) { const jsonSchema = zodToGeminiParameters(tool.schema); - tools[0].functionDeclarations?.push({ + geminiTools[0].functionDeclarations?.push({ name: tool.name, - description: tool.description, + description: tool.description ?? `A function available to call.`, parameters: jsonSchema as GeminiFunctionSchema, }); } else if (isOpenAITool(tool)) { - tools[0].functionDeclarations?.push({ + geminiTools[0].functionDeclarations?.push({ name: tool.function.name, description: tool.function.description ?? `A function available to call.`, @@ -100,7 +89,7 @@ export function convertToGeminiTools( }); } }); - return tools; + return geminiTools; } export function copyAIModelParamsInto( diff --git a/libs/langchain-google-genai/src/chat_models.ts b/libs/langchain-google-genai/src/chat_models.ts index 786fd6b5c272..db5814647ba2 100644 --- a/libs/langchain-google-genai/src/chat_models.ts +++ b/libs/langchain-google-genai/src/chat_models.ts @@ -26,14 +26,12 @@ import { NewTokenIndices } from "@langchain/core/callbacks/base"; import { BaseLanguageModelInput, StructuredOutputMethodOptions, - ToolDefinition, } from "@langchain/core/language_models/base"; import { StructuredToolInterface } from "@langchain/core/tools"; import { Runnable, RunnablePassthrough, RunnableSequence, - RunnableToolLike, } from "@langchain/core/runnables"; import type { z } from "zod"; import { isZodSchema } from "@langchain/core/utils/types"; @@ -46,6 +44,7 @@ import { mapGenerateContentResultToChatResult, } from "./utils/common.js"; import { GoogleGenerativeAIToolsOutputParser } from "./output_parsers.js"; +import { GoogleGenerativeAIToolType } from "./types.js"; interface TokenUsage { completionTokens?: number; @@ -60,9 +59,7 @@ export type BaseMessageExamplePair = { export interface GoogleGenerativeAIChatCallOptions extends BaseChatModelCallOptions { - tools?: - | StructuredToolInterface[] - | GoogleGenerativeAIFunctionDeclarationsTool[]; + tools?: GoogleGenerativeAIToolType[]; /** * Whether or not to include usage data, like token counts * in the streamed response chunks. @@ -354,12 +351,7 @@ export class ChatGoogleGenerativeAI } override bindTools( - tools: ( - | StructuredToolInterface - | Record - | ToolDefinition - | RunnableToolLike - )[], + tools: GoogleGenerativeAIToolType[], kwargs?: Partial ): Runnable< BaseLanguageModelInput, diff --git a/libs/langchain-google-genai/src/types.ts b/libs/langchain-google-genai/src/types.ts new file mode 100644 index 000000000000..8cc4e3eb3c50 --- /dev/null +++ b/libs/langchain-google-genai/src/types.ts @@ -0,0 +1,6 @@ +import { FunctionDeclarationsTool as GoogleGenerativeAIFunctionDeclarationsTool } from "@google/generative-ai"; +import { BindToolsInput } from "@langchain/core/language_models/chat_models"; + +export type GoogleGenerativeAIToolType = + | BindToolsInput + | GoogleGenerativeAIFunctionDeclarationsTool; diff --git a/libs/langchain-google-genai/src/utils/common.ts b/libs/langchain-google-genai/src/utils/common.ts index 1d4bdddbaa66..ed0b7a08755c 100644 --- a/libs/langchain-google-genai/src/utils/common.ts +++ b/libs/langchain-google-genai/src/utils/common.ts @@ -22,18 +22,14 @@ import { ChatGenerationChunk, ChatResult, } from "@langchain/core/outputs"; -import { StructuredToolInterface } from "@langchain/core/tools"; -import { isStructuredTool } from "@langchain/core/utils/function_calling"; -import { - ToolDefinition, - isOpenAITool, -} from "@langchain/core/language_models/base"; +import { isLangChainTool } from "@langchain/core/utils/function_calling"; +import { isOpenAITool } from "@langchain/core/language_models/base"; import { ToolCallChunk } from "@langchain/core/messages/tool"; -import { RunnableToolLike } from "@langchain/core/runnables"; import { jsonSchemaToGeminiParameters, zodToGenerativeAIParameters, } from "./zod_to_genai_parameters.js"; +import { GoogleGenerativeAIToolType } from "../types.js"; export function getMessageAuthor(message: BaseMessage) { const type = message._getType(); @@ -323,48 +319,40 @@ export function convertResponseContentToChatGenerationChunk( } export function convertToGenerativeAITools( - structuredTools: ( - | StructuredToolInterface - | Record - | ToolDefinition - | RunnableToolLike - )[] + tools: GoogleGenerativeAIToolType[] ): GoogleGenerativeAIFunctionDeclarationsTool[] { if ( - structuredTools.every( + tools.every( (tool) => "functionDeclarations" in tool && Array.isArray(tool.functionDeclarations) ) ) { - return structuredTools as GoogleGenerativeAIFunctionDeclarationsTool[]; + return tools as GoogleGenerativeAIFunctionDeclarationsTool[]; } return [ { - functionDeclarations: structuredTools.map( - (structuredTool): GenerativeAIFunctionDeclaration => { - if (isStructuredTool(structuredTool)) { - const jsonSchema = zodToGenerativeAIParameters( - structuredTool.schema - ); + functionDeclarations: tools.map( + (tool): GenerativeAIFunctionDeclaration => { + if (isLangChainTool(tool)) { + const jsonSchema = zodToGenerativeAIParameters(tool.schema); return { - name: structuredTool.name, - description: structuredTool.description, + name: tool.name, + description: tool.description, parameters: jsonSchema, }; } - if (isOpenAITool(structuredTool)) { + if (isOpenAITool(tool)) { return { - name: structuredTool.function.name, + name: tool.function.name, description: - structuredTool.function.description ?? - `A function available to call.`, + tool.function.description ?? `A function available to call.`, parameters: jsonSchemaToGeminiParameters( - structuredTool.function.parameters + tool.function.parameters ), }; } - return structuredTool as unknown as GenerativeAIFunctionDeclaration; + return tool as unknown as GenerativeAIFunctionDeclaration; } ), }, diff --git a/libs/langchain-google-vertexai/package.json b/libs/langchain-google-vertexai/package.json index e2da7b38c254..8b5ee077232b 100644 --- a/libs/langchain-google-vertexai/package.json +++ b/libs/langchain-google-vertexai/package.json @@ -48,7 +48,7 @@ }, "devDependencies": { "@jest/globals": "^29.5.0", - "@langchain/google-common": "latest", + "@langchain/google-common": "workspace:*", "@langchain/scripts": "~0.0.20", "@langchain/standard-tests": "0.0.0", "@swc/core": "^1.3.90", diff --git a/libs/langchain-groq/src/chat_models.ts b/libs/langchain-groq/src/chat_models.ts index 413e8803fdff..ba05ae9e3c43 100644 --- a/libs/langchain-groq/src/chat_models.ts +++ b/libs/langchain-groq/src/chat_models.ts @@ -5,6 +5,7 @@ import { CallbackManagerForLLMRun } from "@langchain/core/callbacks/manager"; import { BaseChatModel, BaseChatModelCallOptions, + BindToolsInput, LangSmithParams, type BaseChatModelParams, } from "@langchain/core/language_models/chat_models"; @@ -45,13 +46,11 @@ import { Runnable, RunnablePassthrough, RunnableSequence, - RunnableToolLike, } from "@langchain/core/runnables"; import { BaseLanguageModelInput, FunctionDefinition, StructuredOutputMethodOptions, - ToolDefinition, } from "@langchain/core/language_models/base"; import { BaseLLMOutputParser, @@ -64,13 +63,14 @@ import { makeInvalidToolCall, convertLangChainToolCallToOpenAI, } from "@langchain/core/output_parsers/openai_tools"; -import { StructuredToolInterface } from "@langchain/core/tools"; import { convertToOpenAITool } from "@langchain/core/utils/function_calling"; import { ToolCallChunk } from "@langchain/core/messages/tool"; +type ChatGroqToolType = BindToolsInput | OpenAIClient.ChatCompletionTool; + export interface ChatGroqCallOptions extends BaseChatModelCallOptions { headers?: Record; - tools?: OpenAIClient.ChatCompletionTool[]; + tools?: ChatGroqToolType[]; tool_choice?: OpenAIClient.ChatCompletionToolChoiceOption | "any" | string; response_format?: { type: "json_object" }; } @@ -428,12 +428,7 @@ export class ChatGroq extends BaseChatModel< } override bindTools( - tools: ( - | Record - | StructuredToolInterface - | ToolDefinition - | RunnableToolLike - )[], + tools: ChatGroqToolType[], kwargs?: Partial ): Runnable { return this.bind({ diff --git a/libs/langchain-mistralai/src/chat_models.ts b/libs/langchain-mistralai/src/chat_models.ts index 2f8265174beb..c78776d4701f 100644 --- a/libs/langchain-mistralai/src/chat_models.ts +++ b/libs/langchain-mistralai/src/chat_models.ts @@ -35,6 +35,7 @@ import { CallbackManagerForLLMRun } from "@langchain/core/callbacks/manager"; import { type BaseChatModelParams, BaseChatModel, + BindToolsInput, LangSmithParams, } from "@langchain/core/language_models/chat_models"; @@ -45,7 +46,6 @@ import { } from "@langchain/core/outputs"; import { getEnvironmentVariable } from "@langchain/core/utils/env"; import { NewTokenIndices } from "@langchain/core/callbacks/base"; -import { StructuredTool, StructuredToolInterface } from "@langchain/core/tools"; import { z } from "zod"; import { type BaseLLMOutputParser, @@ -62,7 +62,6 @@ import { Runnable, RunnablePassthrough, RunnableSequence, - RunnableToolLike, } from "@langchain/core/runnables"; import { zodToJsonSchema } from "zod-to-json-schema"; import { ToolCallChunk } from "@langchain/core/messages/tool"; @@ -78,12 +77,17 @@ export type MistralAIToolChoice = "auto" | "any" | "none"; type MistralAIToolInput = { type: string; function: MistralAIFunction }; +type ChatMistralAIToolType = + | MistralAIToolInput + | MistralAITool + | BindToolsInput; + export interface ChatMistralAICallOptions extends Omit { response_format?: { type: "text" | "json_object"; }; - tools: StructuredToolInterface[] | MistralAIToolInput[] | MistralAITool[]; + tools: ChatMistralAIToolType[]; tool_choice?: MistralAIToolChoice; /** * Whether or not to include token usage in the stream. @@ -385,10 +389,14 @@ function _convertDeltaToMessageChunk( } } -function _convertStructuredToolToMistralTool( - tools: StructuredToolInterface[] +function _convertToolToMistralTool( + tools: ChatMistralAIToolType[] ): MistralAITool[] { return tools.map((tool) => { + if ("function" in tool) { + return tool as MistralAITool; + } + const description = tool.description ?? `Tool: ${tool.name}`; return { type: "function", @@ -491,24 +499,9 @@ export class ChatMistralAI< options?: this["ParsedCallOptions"] ): Omit { const { response_format, tools, tool_choice } = options ?? {}; - const mistralAITools: Array | undefined = tools - ?.map((tool) => { - if ("lc_namespace" in tool) { - return _convertStructuredToolToMistralTool([tool]); - } - if (!tool.function.description) { - return { - type: "function", - function: { - name: tool.function.name, - description: `Tool: ${tool.function.name}`, - parameters: tool.function.parameters, - }, - } as MistralAITool; - } - return tool as MistralAITool; - }) - .flat(); + const mistralAITools: Array | undefined = tools?.length + ? _convertToolToMistralTool(tools) + : undefined; const params: Omit = { model: this.model, tools: mistralAITools, @@ -525,23 +518,11 @@ export class ChatMistralAI< } override bindTools( - tools: ( - | Record - | StructuredToolInterface - | RunnableToolLike - )[], + tools: ChatMistralAIToolType[], kwargs?: Partial ): Runnable { - const mistralAITools = tools - ?.map((tool) => { - if ("lc_namespace" in tool) { - return _convertStructuredToolToMistralTool([tool as StructuredTool]); - } - return tool; - }) - .flat(); return this.bind({ - tools: mistralAITools, + tools: _convertToolToMistralTool(tools), ...kwargs, } as CallOptions); } diff --git a/libs/langchain-ollama/src/chat_models.ts b/libs/langchain-ollama/src/chat_models.ts index 15c7ca31897e..52469ac8e305 100644 --- a/libs/langchain-ollama/src/chat_models.ts +++ b/libs/langchain-ollama/src/chat_models.ts @@ -3,16 +3,14 @@ import { UsageMetadata, type BaseMessage, } from "@langchain/core/messages"; -import { - BaseLanguageModelInput, - ToolDefinition, -} from "@langchain/core/language_models/base"; +import { BaseLanguageModelInput } from "@langchain/core/language_models/base"; import { CallbackManagerForLLMRun } from "@langchain/core/callbacks/manager"; import { type BaseChatModelParams, BaseChatModel, LangSmithParams, BaseChatModelCallOptions, + BindToolsInput, } from "@langchain/core/language_models/chat_models"; import { Ollama } from "ollama/browser"; import { ChatGenerationChunk, ChatResult } from "@langchain/core/outputs"; @@ -23,8 +21,7 @@ import type { Message as OllamaMessage, Tool as OllamaTool, } from "ollama"; -import { StructuredToolInterface } from "@langchain/core/tools"; -import { Runnable, RunnableToolLike } from "@langchain/core/runnables"; +import { Runnable } from "@langchain/core/runnables"; import { convertToOpenAITool } from "@langchain/core/utils/function_calling"; import { concat } from "@langchain/core/utils/stream"; import { @@ -37,7 +34,7 @@ export interface ChatOllamaCallOptions extends BaseChatModelCallOptions { * An array of strings to stop on. */ stop?: string[]; - tools?: (StructuredToolInterface | RunnableToolLike | ToolDefinition)[]; + tools?: BindToolsInput[]; } export interface PullModelOptions { @@ -294,7 +291,7 @@ export class ChatOllama } override bindTools( - tools: (StructuredToolInterface | ToolDefinition | RunnableToolLike)[], + tools: BindToolsInput[], kwargs?: Partial ): Runnable { return this.bind({ diff --git a/libs/langchain-openai/src/chat_models.ts b/libs/langchain-openai/src/chat_models.ts index 52d2eeb52bf4..51488b0ae7c0 100644 --- a/libs/langchain-openai/src/chat_models.ts +++ b/libs/langchain-openai/src/chat_models.ts @@ -20,10 +20,10 @@ import { ChatGenerationChunk, type ChatResult, } from "@langchain/core/outputs"; -import { type StructuredToolInterface } from "@langchain/core/tools"; import { getEnvironmentVariable } from "@langchain/core/utils/env"; import { BaseChatModel, + BindToolsInput, LangSmithParams, type BaseChatModelParams, } from "@langchain/core/language_models/chat_models"; @@ -42,7 +42,6 @@ import { Runnable, RunnablePassthrough, RunnableSequence, - RunnableToolLike, } from "@langchain/core/runnables"; import { JsonOutputParser, @@ -275,12 +274,7 @@ function convertMessagesToOpenAIParams(messages: BaseMessage[]) { }); } -type ChatOpenAIToolType = - | StructuredToolInterface - | OpenAIClient.ChatCompletionTool - | RunnableToolLike - // eslint-disable-next-line @typescript-eslint/no-explicit-any - | Record; +type ChatOpenAIToolType = BindToolsInput | OpenAIClient.ChatCompletionTool; function _convertChatOpenAIToolTypeToOpenAITool( tool: ChatOpenAIToolType, diff --git a/yarn.lock b/yarn.lock index 075329f22ffb..7d5dec095837 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11689,17 +11689,6 @@ __metadata: languageName: unknown linkType: soft -"@langchain/google-common@npm:latest": - version: 0.0.18 - resolution: "@langchain/google-common@npm:0.0.18" - dependencies: - "@langchain/core": ">0.1.56 <0.3.0" - uuid: ^9.0.0 - zod-to-json-schema: ^3.22.4 - checksum: afd120d664f7c3da35cef4323a9c5c50063021e1b64b8e259b6e9ff7fdfb4b270d7393b3b4e78a280f35578005ff8198c6f1283d8e1ccd651db10a15ef035534 - languageName: node - linkType: hard - "@langchain/google-common@workspace:*, @langchain/google-common@workspace:libs/langchain-google-common, @langchain/google-common@~0.0.22": version: 0.0.0-use.local resolution: "@langchain/google-common@workspace:libs/langchain-google-common" @@ -11850,7 +11839,7 @@ __metadata: dependencies: "@jest/globals": ^29.5.0 "@langchain/core": ">=0.2.16 <0.3.0" - "@langchain/google-common": latest + "@langchain/google-common": "workspace:*" "@langchain/google-gauth": ~0.0.21 "@langchain/scripts": ~0.0.20 "@langchain/standard-tests": 0.0.0