Skip to content

Commit

Permalink
anthropic[minor]: Allow different tool types to be bound (#6712)
Browse files Browse the repository at this point in the history
  • Loading branch information
bracesproul authored Sep 6, 2024
1 parent 1e76bf0 commit 6e11e05
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 33 deletions.
61 changes: 28 additions & 33 deletions libs/langchain-anthropic/src/chat_models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {
import {
type StructuredOutputMethodOptions,
type BaseLanguageModelInput,
type ToolDefinition,
isOpenAITool,
} from "@langchain/core/language_models/base";
import { zodToJsonSchema } from "zod-to-json-schema";
Expand Down Expand Up @@ -682,45 +681,41 @@ export class ChatAnthropicMessages<
*
* @param {ChatAnthropicCallOptions["tools"]} tools The tools to format
* @returns {AnthropicTool[] | undefined} The formatted tools, or undefined if none are passed.
* @throws {Error} If a mix of AnthropicTools and StructuredTools are passed.
*/
formatStructuredToolToAnthropic(
tools: ChatAnthropicCallOptions["tools"]
): AnthropicTool[] | undefined {
if (!tools || !tools.length) {
return undefined;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
if ((tools as any[]).every((tool) => isAnthropicTool(tool))) {
// If the tool is already an anthropic tool, return it
return tools as AnthropicTool[];
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
if ((tools as any[]).every((tool) => isOpenAITool(tool))) {
// Formatted as OpenAI tool, convert to Anthropic tool
return (tools as ToolDefinition[]).map((tc) => ({
name: tc.function.name,
description: tc.function.description,
input_schema: tc.function.parameters as AnthropicTool.InputSchema,
}));
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
if ((tools as any[]).some((tool) => isAnthropicTool(tool))) {
throw new Error(`Can not pass in a mix of tool schemas to ChatAnthropic`);
}

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");
return tools.map((tool) => {
if (isAnthropicTool(tool)) {
return tool;
}
if (isOpenAITool(tool)) {
return {
name: tool.function.name,
description: tool.function.description,
input_schema: tool.function.parameters as AnthropicTool.InputSchema,
};
}
if (isLangChainTool(tool)) {
return {
name: tool.name,
description: tool.description,
input_schema: zodToJsonSchema(
tool.schema
) as AnthropicTool.InputSchema,
};
}
throw new Error(
`Unknown tool type passed to ChatAnthropic: ${JSON.stringify(
tool,
null,
2
)}`
);
});
}

override bindTools(
Expand Down
27 changes: 27 additions & 0 deletions libs/langchain-anthropic/src/tests/chat_models-tools.int.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -461,3 +461,30 @@ test("streaming with structured output", async () => {
}
expect(typeof finalChunk2).toEqual("object");
});

test("Can bound and invoke different tool types", async () => {
const langchainTool = {
name: "get_weather_lc",
description: "Get the weather of a specific location.",
schema: zodSchema,
};
const openaiTool = {
type: "function",
function: {
name: "get_weather_oai",
description: "Get the weather of a specific location.",
parameters: zodToJsonSchema(zodSchema),
},
};
const anthropicTool = {
name: "get_weather_ant",
description: "Get the weather of a specific location.",
input_schema: zodToJsonSchema(zodSchema),
};
const tools = [langchainTool, openaiTool, anthropicTool];
const modelWithTools = model.bindTools(tools);
const result = await modelWithTools.invoke(
"Whats the current weather in san francisco?"
);
expect(result.tool_calls?.length).toBeGreaterThanOrEqual(1);
});

0 comments on commit 6e11e05

Please sign in to comment.