import { XMLParser } from "fast-xml-parser"; import { BaseChatModelParams } from "../../chat_models/base.js"; import { CallbackManagerForLLMRun } from "../../callbacks/manager.js"; import { AIMessage, BaseMessage, ChatResult, SystemMessage, } from "../../schema/index.js"; import { ChatAnthropic, DEFAULT_STOP_SEQUENCES, type AnthropicInput, } from "../../chat_models/anthropic.js"; import { BaseFunctionCallOptions } from "../../base_language/index.js"; import { StructuredTool } from "../../tools/base.js"; import { PromptTemplate } from "../../prompts/prompt.js"; import { formatToOpenAIFunction } from "../../tools/convert_to_openai.js"; const TOOL_SYSTEM_PROMPT = /* #__PURE__ */ PromptTemplate.fromTemplate(`In addition to responding, you can use tools. You have access to the following tools. {tools} In order to use a tool, you can use to specify the name, and the tags to specify the parameters. Each parameter should be passed in as <$param_name>$value, Where $param_name is the name of the specific parameter, and $value is the value for that parameter. You will then get back a response in the form For example, if you have a tool called 'search' that accepts a single parameter 'query' that could run a google search, in order to search for the weather in SF you would respond: searchweather in SF 64 degrees`); export interface ChatAnthropicFunctionsCallOptions extends BaseFunctionCallOptions { tools?: StructuredTool[]; } export class AnthropicFunctions extends ChatAnthropic { static lc_name(): string { return "AnthropicFunctions"; } constructor(fields?: Partial & BaseChatModelParams) { super(fields ?? {}); } async _generate( messages: BaseMessage[], options: this["ParsedCallOptions"], runManager?: CallbackManagerForLLMRun | undefined ): Promise { let promptMessages = messages; let forced = false; let functionCall: string | undefined; if (options.tools) { // eslint-disable-next-line no-param-reassign options.functions = (options.functions ?? []).concat( options.tools.map(formatToOpenAIFunction) ); } if (options.functions !== undefined && options.functions.length > 0) { const content = await TOOL_SYSTEM_PROMPT.format({ tools: JSON.stringify(options.functions, null, 2), }); const systemMessage = new SystemMessage({ content }); promptMessages = [systemMessage].concat(promptMessages); const stopSequences = options?.stop?.concat(DEFAULT_STOP_SEQUENCES) ?? this.stopSequences ?? DEFAULT_STOP_SEQUENCES; // eslint-disable-next-line no-param-reassign options.stop = stopSequences.concat([""]); if (options.function_call) { if (typeof options.function_call === "string") { functionCall = JSON.parse(options.function_call).name; } else { functionCall = options.function_call.name; } forced = true; const matchingFunction = options.functions.find( (tool) => tool.name === functionCall ); if (!matchingFunction) { throw new Error( `No matching function found for passed "function_call"` ); } promptMessages = promptMessages.concat([ new AIMessage({ content: `${functionCall}`, }), ]); // eslint-disable-next-line no-param-reassign delete options.function_call; } // eslint-disable-next-line no-param-reassign delete options.functions; } else if (options.function_call !== undefined) { throw new Error( `If "function_call" is provided, "functions" must also be.` ); } const chatResult = await super._generate( promptMessages, options, runManager ); const chatGenerationContent = chatResult.generations[0].message.content; if (forced) { const parser = new XMLParser(); const result = parser.parse(`${chatGenerationContent}`); if (functionCall === undefined) { throw new Error(`Could not parse called function from model output.`); } const responseMessageWithFunctions = new AIMessage({ content: "", additional_kwargs: { function_call: { name: functionCall, arguments: result.tool_input ? JSON.stringify(result.tool_input) : "", }, }, }); return { generations: [{ message: responseMessageWithFunctions, text: "" }], }; } else if (chatGenerationContent.includes("")) { const parser = new XMLParser(); const result = parser.parse(`${chatGenerationContent}`); const responseMessageWithFunctions = new AIMessage({ content: chatGenerationContent.split("")[0], additional_kwargs: { function_call: { name: result.tool, arguments: result.tool_input ? JSON.stringify(result.tool_input) : "", }, }, }); return { generations: [{ message: responseMessageWithFunctions, text: "" }], }; } return chatResult; } _llmType(): string { return "anthropic_functions"; } /** @ignore */ _combineLLMOutput() { return []; } }