@n8n/n8n-nodes-langchain 1.110.0 → 1.111.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/nodes/agents/Agent/agents/ToolsAgent/common.js +6 -1
- package/dist/nodes/agents/Agent/agents/ToolsAgent/common.js.map +1 -1
- package/dist/nodes/llms/LMChatOpenAi/LmChatOpenAi.node.js +5 -0
- package/dist/nodes/llms/LMChatOpenAi/LmChatOpenAi.node.js.map +1 -1
- package/dist/nodes/tools/ToolCode/ToolCode.node.js +22 -2
- package/dist/nodes/tools/ToolCode/ToolCode.node.js.map +1 -1
- package/dist/nodes/vendors/GoogleGemini/actions/image/edit.operation.js +206 -0
- package/dist/nodes/vendors/GoogleGemini/actions/image/edit.operation.js.map +1 -0
- package/dist/nodes/vendors/GoogleGemini/actions/image/index.js +11 -1
- package/dist/nodes/vendors/GoogleGemini/actions/image/index.js.map +1 -1
- package/dist/nodes/vendors/OpenAi/transport/index.js +8 -1
- package/dist/nodes/vendors/OpenAi/transport/index.js.map +1 -1
- package/dist/types/nodes.json +1 -1
- package/dist/utils/N8nBinaryLoader.js +1 -1
- package/dist/utils/N8nBinaryLoader.js.map +1 -1
- package/package.json +9 -7
|
@@ -127,7 +127,12 @@ const getAgentStepsParser = (outputParser, memory) => async (steps) => {
|
|
|
127
127
|
if (finalResponse instanceof Object) {
|
|
128
128
|
if ("output" in finalResponse) {
|
|
129
129
|
try {
|
|
130
|
-
|
|
130
|
+
const parsedOutput = (0, import_n8n_workflow.jsonParse)(finalResponse.output);
|
|
131
|
+
if (parsedOutput !== null && typeof parsedOutput === "object" && "output" in parsedOutput && Object.keys(parsedOutput).length === 1) {
|
|
132
|
+
parserInput = JSON.stringify(parsedOutput);
|
|
133
|
+
} else {
|
|
134
|
+
parserInput = JSON.stringify({ output: parsedOutput });
|
|
135
|
+
}
|
|
131
136
|
} catch (error) {
|
|
132
137
|
parserInput = finalResponse.output;
|
|
133
138
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../nodes/agents/Agent/agents/ToolsAgent/common.ts"],"sourcesContent":["import type { BaseChatModel } from '@langchain/core/language_models/chat_models';\nimport { HumanMessage } from '@langchain/core/messages';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport { ChatPromptTemplate, type BaseMessagePromptTemplateLike } from '@langchain/core/prompts';\nimport type { AgentAction, AgentFinish } from 'langchain/agents';\nimport type { ToolsAgentAction } from 'langchain/dist/agents/tool_calling/output_parser';\nimport type { BaseChatMemory } from 'langchain/memory';\nimport { DynamicStructuredTool, type Tool } from 'langchain/tools';\nimport { BINARY_ENCODING, jsonParse, NodeConnectionTypes, NodeOperationError } from 'n8n-workflow';\nimport type { IExecuteFunctions, ISupplyDataFunctions } from 'n8n-workflow';\nimport type { ZodObject } from 'zod';\nimport { z } from 'zod';\n\nimport { isChatInstance, getConnectedTools } from '@utils/helpers';\nimport { type N8nOutputParser } from '@utils/output_parsers/N8nOutputParser';\n/* -----------------------------------------------------------\n Output Parser Helper\n----------------------------------------------------------- */\n/**\n * Retrieve the output parser schema.\n * If the parser does not return a valid schema, default to a schema with a single text field.\n */\nexport function getOutputParserSchema(\n\toutputParser: N8nOutputParser,\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n): ZodObject<any, any, any, any> {\n\tconst schema =\n\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\t(outputParser.getSchema() as ZodObject<any, any, any, any>) ?? z.object({ text: z.string() });\n\treturn schema;\n}\n\n/* -----------------------------------------------------------\n Binary Data Helpers\n----------------------------------------------------------- */\n/**\n * Extracts binary image messages from the input data.\n * When operating in filesystem mode, the binary stream is first converted to a buffer.\n *\n * @param ctx - The execution context\n * @param itemIndex - The current item index\n * @returns A HumanMessage containing the binary image messages.\n */\nexport async function extractBinaryMessages(\n\tctx: IExecuteFunctions | ISupplyDataFunctions,\n\titemIndex: number,\n): Promise<HumanMessage> {\n\tconst binaryData = ctx.getInputData()?.[itemIndex]?.binary ?? {};\n\tconst binaryMessages = await Promise.all(\n\t\tObject.values(binaryData)\n\t\t\t.filter((data) => data.mimeType.startsWith('image/'))\n\t\t\t.map(async (data) => {\n\t\t\t\tlet binaryUrlString: string;\n\n\t\t\t\t// In filesystem mode we need to get binary stream by id before converting it to buffer\n\t\t\t\tif (data.id) {\n\t\t\t\t\tconst binaryBuffer = await ctx.helpers.binaryToBuffer(\n\t\t\t\t\t\tawait ctx.helpers.getBinaryStream(data.id),\n\t\t\t\t\t);\n\t\t\t\t\tbinaryUrlString = `data:${data.mimeType};base64,${Buffer.from(binaryBuffer).toString(\n\t\t\t\t\t\tBINARY_ENCODING,\n\t\t\t\t\t)}`;\n\t\t\t\t} else {\n\t\t\t\t\tbinaryUrlString = data.data.includes('base64')\n\t\t\t\t\t\t? data.data\n\t\t\t\t\t\t: `data:${data.mimeType};base64,${data.data}`;\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'image_url',\n\t\t\t\t\timage_url: {\n\t\t\t\t\t\turl: binaryUrlString,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}),\n\t);\n\treturn new HumanMessage({\n\t\tcontent: [...binaryMessages],\n\t});\n}\n\n/* -----------------------------------------------------------\n Agent Output Format Helpers\n----------------------------------------------------------- */\n/**\n * Fixes empty content messages in agent steps.\n *\n * This function is necessary when using RunnableSequence.from in LangChain.\n * If a tool doesn't have any arguments, LangChain returns input: '' (empty string).\n * This can throw an error for some providers (like Anthropic) which expect the input to always be an object.\n * This function replaces empty string inputs with empty objects to prevent such errors.\n *\n * @param steps - The agent steps to fix\n * @returns The fixed agent steps\n */\nexport function fixEmptyContentMessage(\n\tsteps: AgentFinish | ToolsAgentAction[],\n): AgentFinish | ToolsAgentAction[] {\n\tif (!Array.isArray(steps)) return steps;\n\n\tsteps.forEach((step) => {\n\t\tif ('messageLog' in step && step.messageLog !== undefined) {\n\t\t\tif (Array.isArray(step.messageLog)) {\n\t\t\t\tstep.messageLog.forEach((message: BaseMessage) => {\n\t\t\t\t\tif ('content' in message && Array.isArray(message.content)) {\n\t\t\t\t\t\t(message.content as Array<{ input?: string | object }>).forEach((content) => {\n\t\t\t\t\t\t\tif (content.input === '') {\n\t\t\t\t\t\t\t\tcontent.input = {};\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t});\n\n\treturn steps;\n}\n\n/**\n * Ensures consistent handling of outputs regardless of the model used,\n * providing a unified output format for further processing.\n *\n * This method is necessary to handle different output formats from various language models.\n * Specifically, it checks if the agent step is the final step (contains returnValues) and determines\n * if the output is a simple string (e.g., from OpenAI models) or an array of outputs (e.g., from Anthropic models).\n *\n * Examples:\n * 1. Anthropic model output:\n * ```json\n * {\n * \"output\": [\n * {\n * \"index\": 0,\n * \"type\": \"text\",\n * \"text\": \"The result of the calculation is approximately 1001.8166...\"\n * }\n * ]\n * }\n *```\n * 2. OpenAI model output:\n * ```json\n * {\n * \"output\": \"The result of the calculation is approximately 1001.82...\"\n * }\n * ```\n *\n * @param steps - The agent finish or agent action steps.\n * @returns The modified agent finish steps or the original steps.\n */\nexport function handleAgentFinishOutput(\n\tsteps: AgentFinish | AgentAction[],\n): AgentFinish | AgentAction[] {\n\ttype AgentMultiOutputFinish = AgentFinish & {\n\t\treturnValues: { output: Array<{ text: string; type: string; index: number }> };\n\t};\n\tconst agentFinishSteps = steps as AgentMultiOutputFinish | AgentFinish;\n\n\tif (agentFinishSteps.returnValues) {\n\t\tconst isMultiOutput = Array.isArray(agentFinishSteps.returnValues?.output);\n\t\tif (isMultiOutput) {\n\t\t\t// If all items in the multi-output array are of type 'text', merge them into a single string\n\t\t\tconst multiOutputSteps = agentFinishSteps.returnValues.output as Array<{\n\t\t\t\tindex: number;\n\t\t\t\ttype: string;\n\t\t\t\ttext: string;\n\t\t\t}>;\n\t\t\tconst isTextOnly = multiOutputSteps.every((output) => 'text' in output);\n\t\t\tif (isTextOnly) {\n\t\t\t\tagentFinishSteps.returnValues.output = multiOutputSteps\n\t\t\t\t\t.map((output) => output.text)\n\t\t\t\t\t.join('\\n')\n\t\t\t\t\t.trim();\n\t\t\t}\n\t\t\treturn agentFinishSteps;\n\t\t}\n\t}\n\n\treturn agentFinishSteps;\n}\n\n/**\n * Wraps the parsed output so that it can be stored in memory.\n * If memory is connected, the output is stringified.\n *\n * @param output - The parsed output object\n * @param memory - The connected memory (if any)\n * @returns The formatted output object\n */\nexport function handleParsedStepOutput(\n\toutput: Record<string, unknown>,\n\tmemory?: BaseChatMemory,\n): { returnValues: Record<string, unknown>; log: string } {\n\treturn {\n\t\treturnValues: memory ? { output: JSON.stringify(output) } : output,\n\t\tlog: 'Final response formatted',\n\t};\n}\n\n/**\n * Parses agent steps using the provided output parser.\n * If the agent used the 'format_final_json_response' tool, the output is parsed accordingly.\n *\n * @param steps - The agent finish or action steps\n * @param outputParser - The output parser (if defined)\n * @param memory - The connected memory (if any)\n * @returns The parsed steps with the final output\n */\nexport const getAgentStepsParser =\n\t(outputParser?: N8nOutputParser, memory?: BaseChatMemory) =>\n\tasync (steps: AgentFinish | AgentAction[]): Promise<AgentFinish | AgentAction[]> => {\n\t\t// Check if the steps contain the 'format_final_json_response' tool invocation.\n\t\tif (Array.isArray(steps)) {\n\t\t\tconst responseParserTool = steps.find((step) => step.tool === 'format_final_json_response');\n\t\t\tif (responseParserTool && outputParser) {\n\t\t\t\tconst toolInput = responseParserTool.toolInput;\n\t\t\t\t// Ensure the tool input is a string\n\t\t\t\tconst parserInput = toolInput instanceof Object ? JSON.stringify(toolInput) : toolInput;\n\t\t\t\tconst returnValues = (await outputParser.parse(parserInput)) as Record<string, unknown>;\n\t\t\t\treturn handleParsedStepOutput(returnValues, memory);\n\t\t\t}\n\t\t}\n\n\t\t// Otherwise, if the steps contain a returnValues field, try to parse them manually.\n\t\tif (outputParser && typeof steps === 'object' && (steps as AgentFinish).returnValues) {\n\t\t\tconst finalResponse = (steps as AgentFinish).returnValues;\n\t\t\tlet parserInput: string;\n\n\t\t\tif (finalResponse instanceof Object) {\n\t\t\t\tif ('output' in finalResponse) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// If the output is an object, we will try to parse it as JSON\n\t\t\t\t\t\t// this is because parser expects stringified JSON object like { \"output\": { .... } }\n\t\t\t\t\t\t// so we try to parse the output before wrapping it and then stringify it\n\t\t\t\t\t\tparserInput = JSON.stringify({ output: jsonParse(finalResponse.output) });\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t// Fallback to the raw output if parsing fails.\n\t\t\t\t\t\tparserInput = finalResponse.output;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// If the output is not an object, we will stringify it as it is\n\t\t\t\t\tparserInput = JSON.stringify(finalResponse);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tparserInput = finalResponse;\n\t\t\t}\n\n\t\t\tconst returnValues = (await outputParser.parse(parserInput)) as Record<string, unknown>;\n\t\t\treturn handleParsedStepOutput(returnValues, memory);\n\t\t}\n\n\t\treturn handleAgentFinishOutput(steps);\n\t};\n\n/* -----------------------------------------------------------\n Agent Setup Helpers\n----------------------------------------------------------- */\n/**\n * Retrieves the language model from the input connection.\n * Throws an error if the model is not a valid chat instance or does not support tools.\n *\n * @param ctx - The execution context\n * @returns The validated chat model\n */\nexport async function getChatModel(\n\tctx: IExecuteFunctions | ISupplyDataFunctions,\n\tindex: number = 0,\n): Promise<BaseChatModel | undefined> {\n\tconst connectedModels = await ctx.getInputConnectionData(NodeConnectionTypes.AiLanguageModel, 0);\n\n\tlet model;\n\n\tif (Array.isArray(connectedModels) && index !== undefined) {\n\t\tif (connectedModels.length <= index) {\n\t\t\treturn undefined;\n\t\t}\n\t\t// We get the models in reversed order from the workflow so we need to reverse them to match the right index\n\t\tconst reversedModels = [...connectedModels].reverse();\n\t\tmodel = reversedModels[index] as BaseChatModel;\n\t} else {\n\t\tmodel = connectedModels as BaseChatModel;\n\t}\n\n\tif (!isChatInstance(model) || !model.bindTools) {\n\t\tthrow new NodeOperationError(\n\t\t\tctx.getNode(),\n\t\t\t'Tools Agent requires Chat Model which supports Tools calling',\n\t\t);\n\t}\n\treturn model;\n}\n\n/**\n * Retrieves the memory instance from the input connection if it is connected\n *\n * @param ctx - The execution context\n * @returns The connected memory (if any)\n */\nexport async function getOptionalMemory(\n\tctx: IExecuteFunctions | ISupplyDataFunctions,\n): Promise<BaseChatMemory | undefined> {\n\treturn (await ctx.getInputConnectionData(NodeConnectionTypes.AiMemory, 0)) as\n\t\t| BaseChatMemory\n\t\t| undefined;\n}\n\n/**\n * Retrieves the connected tools and (if an output parser is defined)\n * appends a structured output parser tool.\n *\n * @param ctx - The execution context\n * @param outputParser - The optional output parser\n * @returns The array of connected tools\n */\nexport async function getTools(\n\tctx: IExecuteFunctions | ISupplyDataFunctions,\n\toutputParser?: N8nOutputParser,\n): Promise<Array<DynamicStructuredTool | Tool>> {\n\tconst tools = (await getConnectedTools(ctx, true, false)) as Array<DynamicStructuredTool | Tool>;\n\n\t// If an output parser is available, create a dynamic tool to validate the final output.\n\tif (outputParser) {\n\t\tconst schema = getOutputParserSchema(outputParser);\n\t\tconst structuredOutputParserTool = new DynamicStructuredTool({\n\t\t\tschema,\n\t\t\tname: 'format_final_json_response',\n\t\t\tdescription:\n\t\t\t\t'Use this tool to format your final response to the user in a structured JSON format. This tool validates your output against a schema to ensure it meets the required format. ONLY use this tool when you have completed all necessary reasoning and are ready to provide your final answer. Do not use this tool for intermediate steps or for asking questions. The output from this tool will be directly returned to the user.',\n\t\t\t// We do not use a function here because we intercept the output with the parser.\n\t\t\tfunc: async () => '',\n\t\t});\n\t\ttools.push(structuredOutputParserTool);\n\t}\n\treturn tools;\n}\n\n/**\n * Prepares the prompt messages for the agent.\n *\n * @param ctx - The execution context\n * @param itemIndex - The current item index\n * @param options - Options containing systemMessage and other parameters\n * @returns The array of prompt messages\n */\nexport async function prepareMessages(\n\tctx: IExecuteFunctions | ISupplyDataFunctions,\n\titemIndex: number,\n\toptions: {\n\t\tsystemMessage?: string;\n\t\tpassthroughBinaryImages?: boolean;\n\t\toutputParser?: N8nOutputParser;\n\t},\n): Promise<BaseMessagePromptTemplateLike[]> {\n\tconst useSystemMessage = options.systemMessage ?? ctx.getNode().typeVersion < 1.9;\n\n\tconst messages: BaseMessagePromptTemplateLike[] = [];\n\n\tif (useSystemMessage) {\n\t\tmessages.push([\n\t\t\t'system',\n\t\t\t`{system_message}${options.outputParser ? '\\n\\n{formatting_instructions}' : ''}`,\n\t\t]);\n\t} else if (options.outputParser) {\n\t\tmessages.push(['system', '{formatting_instructions}']);\n\t}\n\n\tmessages.push(['placeholder', '{chat_history}'], ['human', '{input}']);\n\n\t// If there is binary data and the node option permits it, add a binary message\n\tconst hasBinaryData = ctx.getInputData()?.[itemIndex]?.binary !== undefined;\n\tif (hasBinaryData && options.passthroughBinaryImages) {\n\t\tconst binaryMessage = await extractBinaryMessages(ctx, itemIndex);\n\t\tif (binaryMessage.content.length !== 0) {\n\t\t\tmessages.push(binaryMessage);\n\t\t} else {\n\t\t\tctx.logger.debug('Not attaching binary message, since its content was empty');\n\t\t}\n\t}\n\n\t// We add the agent scratchpad last, so that the agent will not run in loops\n\t// by adding binary messages between each interaction\n\tmessages.push(['placeholder', '{agent_scratchpad}']);\n\treturn messages;\n}\n\n/**\n * Creates the chat prompt from messages.\n *\n * @param messages - The messages array\n * @returns The ChatPromptTemplate instance\n */\nexport function preparePrompt(messages: BaseMessagePromptTemplateLike[]): ChatPromptTemplate {\n\treturn ChatPromptTemplate.fromMessages(messages);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,sBAA6B;AAE7B,qBAAuE;AAIvE,mBAAiD;AACjD,0BAAoF;AAGpF,iBAAkB;AAElB,qBAAkD;AAS3C,SAAS,sBACf,cAEgC;AAChC,QAAM;AAAA;AAAA,IAEJ,aAAa,UAAU,KAAuC,aAAE,OAAO,EAAE,MAAM,aAAE,OAAO,EAAE,CAAC;AAAA;AAC7F,SAAO;AACR;AAaA,eAAsB,sBACrB,KACA,WACwB;AACxB,QAAM,aAAa,IAAI,aAAa,IAAI,SAAS,GAAG,UAAU,CAAC;AAC/D,QAAM,iBAAiB,MAAM,QAAQ;AAAA,IACpC,OAAO,OAAO,UAAU,EACtB,OAAO,CAAC,SAAS,KAAK,SAAS,WAAW,QAAQ,CAAC,EACnD,IAAI,OAAO,SAAS;AACpB,UAAI;AAGJ,UAAI,KAAK,IAAI;AACZ,cAAM,eAAe,MAAM,IAAI,QAAQ;AAAA,UACtC,MAAM,IAAI,QAAQ,gBAAgB,KAAK,EAAE;AAAA,QAC1C;AACA,0BAAkB,QAAQ,KAAK,QAAQ,WAAW,OAAO,KAAK,YAAY,EAAE;AAAA,UAC3E;AAAA,QACD,CAAC;AAAA,MACF,OAAO;AACN,0BAAkB,KAAK,KAAK,SAAS,QAAQ,IAC1C,KAAK,OACL,QAAQ,KAAK,QAAQ,WAAW,KAAK,IAAI;AAAA,MAC7C;AAEA,aAAO;AAAA,QACN,MAAM;AAAA,QACN,WAAW;AAAA,UACV,KAAK;AAAA,QACN;AAAA,MACD;AAAA,IACD,CAAC;AAAA,EACH;AACA,SAAO,IAAI,6BAAa;AAAA,IACvB,SAAS,CAAC,GAAG,cAAc;AAAA,EAC5B,CAAC;AACF;AAgBO,SAAS,uBACf,OACmC;AACnC,MAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,QAAO;AAElC,QAAM,QAAQ,CAAC,SAAS;AACvB,QAAI,gBAAgB,QAAQ,KAAK,eAAe,QAAW;AAC1D,UAAI,MAAM,QAAQ,KAAK,UAAU,GAAG;AACnC,aAAK,WAAW,QAAQ,CAAC,YAAyB;AACjD,cAAI,aAAa,WAAW,MAAM,QAAQ,QAAQ,OAAO,GAAG;AAC3D,YAAC,QAAQ,QAA+C,QAAQ,CAAC,YAAY;AAC5E,kBAAI,QAAQ,UAAU,IAAI;AACzB,wBAAQ,QAAQ,CAAC;AAAA,cAClB;AAAA,YACD,CAAC;AAAA,UACF;AAAA,QACD,CAAC;AAAA,MACF;AAAA,IACD;AAAA,EACD,CAAC;AAED,SAAO;AACR;AAiCO,SAAS,wBACf,OAC8B;AAI9B,QAAM,mBAAmB;AAEzB,MAAI,iBAAiB,cAAc;AAClC,UAAM,gBAAgB,MAAM,QAAQ,iBAAiB,cAAc,MAAM;AACzE,QAAI,eAAe;AAElB,YAAM,mBAAmB,iBAAiB,aAAa;AAKvD,YAAM,aAAa,iBAAiB,MAAM,CAAC,WAAW,UAAU,MAAM;AACtE,UAAI,YAAY;AACf,yBAAiB,aAAa,SAAS,iBACrC,IAAI,CAAC,WAAW,OAAO,IAAI,EAC3B,KAAK,IAAI,EACT,KAAK;AAAA,MACR;AACA,aAAO;AAAA,IACR;AAAA,EACD;AAEA,SAAO;AACR;AAUO,SAAS,uBACf,QACA,QACyD;AACzD,SAAO;AAAA,IACN,cAAc,SAAS,EAAE,QAAQ,KAAK,UAAU,MAAM,EAAE,IAAI;AAAA,IAC5D,KAAK;AAAA,EACN;AACD;AAWO,MAAM,sBACZ,CAAC,cAAgC,WACjC,OAAO,UAA6E;AAEnF,MAAI,MAAM,QAAQ,KAAK,GAAG;AACzB,UAAM,qBAAqB,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,4BAA4B;AAC1F,QAAI,sBAAsB,cAAc;AACvC,YAAM,YAAY,mBAAmB;AAErC,YAAM,cAAc,qBAAqB,SAAS,KAAK,UAAU,SAAS,IAAI;AAC9E,YAAM,eAAgB,MAAM,aAAa,MAAM,WAAW;AAC1D,aAAO,uBAAuB,cAAc,MAAM;AAAA,IACnD;AAAA,EACD;AAGA,MAAI,gBAAgB,OAAO,UAAU,YAAa,MAAsB,cAAc;AACrF,UAAM,gBAAiB,MAAsB;AAC7C,QAAI;AAEJ,QAAI,yBAAyB,QAAQ;AACpC,UAAI,YAAY,eAAe;AAC9B,YAAI;AAIH,wBAAc,KAAK,UAAU,EAAE,YAAQ,+BAAU,cAAc,MAAM,EAAE,CAAC;AAAA,QACzE,SAAS,OAAO;AAEf,wBAAc,cAAc;AAAA,QAC7B;AAAA,MACD,OAAO;AAEN,sBAAc,KAAK,UAAU,aAAa;AAAA,MAC3C;AAAA,IACD,OAAO;AACN,oBAAc;AAAA,IACf;AAEA,UAAM,eAAgB,MAAM,aAAa,MAAM,WAAW;AAC1D,WAAO,uBAAuB,cAAc,MAAM;AAAA,EACnD;AAEA,SAAO,wBAAwB,KAAK;AACrC;AAYD,eAAsB,aACrB,KACA,QAAgB,GACqB;AACrC,QAAM,kBAAkB,MAAM,IAAI,uBAAuB,wCAAoB,iBAAiB,CAAC;AAE/F,MAAI;AAEJ,MAAI,MAAM,QAAQ,eAAe,KAAK,UAAU,QAAW;AAC1D,QAAI,gBAAgB,UAAU,OAAO;AACpC,aAAO;AAAA,IACR;AAEA,UAAM,iBAAiB,CAAC,GAAG,eAAe,EAAE,QAAQ;AACpD,YAAQ,eAAe,KAAK;AAAA,EAC7B,OAAO;AACN,YAAQ;AAAA,EACT;AAEA,MAAI,KAAC,+BAAe,KAAK,KAAK,CAAC,MAAM,WAAW;AAC/C,UAAM,IAAI;AAAA,MACT,IAAI,QAAQ;AAAA,MACZ;AAAA,IACD;AAAA,EACD;AACA,SAAO;AACR;AAQA,eAAsB,kBACrB,KACsC;AACtC,SAAQ,MAAM,IAAI,uBAAuB,wCAAoB,UAAU,CAAC;AAGzE;AAUA,eAAsB,SACrB,KACA,cAC+C;AAC/C,QAAM,QAAS,UAAM,kCAAkB,KAAK,MAAM,KAAK;AAGvD,MAAI,cAAc;AACjB,UAAM,SAAS,sBAAsB,YAAY;AACjD,UAAM,6BAA6B,IAAI,mCAAsB;AAAA,MAC5D;AAAA,MACA,MAAM;AAAA,MACN,aACC;AAAA;AAAA,MAED,MAAM,YAAY;AAAA,IACnB,CAAC;AACD,UAAM,KAAK,0BAA0B;AAAA,EACtC;AACA,SAAO;AACR;AAUA,eAAsB,gBACrB,KACA,WACA,SAK2C;AAC3C,QAAM,mBAAmB,QAAQ,iBAAiB,IAAI,QAAQ,EAAE,cAAc;AAE9E,QAAM,WAA4C,CAAC;AAEnD,MAAI,kBAAkB;AACrB,aAAS,KAAK;AAAA,MACb;AAAA,MACA,mBAAmB,QAAQ,eAAe,kCAAkC,EAAE;AAAA,IAC/E,CAAC;AAAA,EACF,WAAW,QAAQ,cAAc;AAChC,aAAS,KAAK,CAAC,UAAU,2BAA2B,CAAC;AAAA,EACtD;AAEA,WAAS,KAAK,CAAC,eAAe,gBAAgB,GAAG,CAAC,SAAS,SAAS,CAAC;AAGrE,QAAM,gBAAgB,IAAI,aAAa,IAAI,SAAS,GAAG,WAAW;AAClE,MAAI,iBAAiB,QAAQ,yBAAyB;AACrD,UAAM,gBAAgB,MAAM,sBAAsB,KAAK,SAAS;AAChE,QAAI,cAAc,QAAQ,WAAW,GAAG;AACvC,eAAS,KAAK,aAAa;AAAA,IAC5B,OAAO;AACN,UAAI,OAAO,MAAM,2DAA2D;AAAA,IAC7E;AAAA,EACD;AAIA,WAAS,KAAK,CAAC,eAAe,oBAAoB,CAAC;AACnD,SAAO;AACR;AAQO,SAAS,cAAc,UAA+D;AAC5F,SAAO,kCAAmB,aAAa,QAAQ;AAChD;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../nodes/agents/Agent/agents/ToolsAgent/common.ts"],"sourcesContent":["import type { BaseChatModel } from '@langchain/core/language_models/chat_models';\nimport { HumanMessage } from '@langchain/core/messages';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport { ChatPromptTemplate, type BaseMessagePromptTemplateLike } from '@langchain/core/prompts';\nimport type { AgentAction, AgentFinish } from 'langchain/agents';\nimport type { ToolsAgentAction } from 'langchain/dist/agents/tool_calling/output_parser';\nimport type { BaseChatMemory } from 'langchain/memory';\nimport { DynamicStructuredTool, type Tool } from 'langchain/tools';\nimport { BINARY_ENCODING, jsonParse, NodeConnectionTypes, NodeOperationError } from 'n8n-workflow';\nimport type { IExecuteFunctions, ISupplyDataFunctions } from 'n8n-workflow';\nimport type { ZodObject } from 'zod';\nimport { z } from 'zod';\n\nimport { isChatInstance, getConnectedTools } from '@utils/helpers';\nimport { type N8nOutputParser } from '@utils/output_parsers/N8nOutputParser';\n/* -----------------------------------------------------------\n Output Parser Helper\n----------------------------------------------------------- */\n/**\n * Retrieve the output parser schema.\n * If the parser does not return a valid schema, default to a schema with a single text field.\n */\nexport function getOutputParserSchema(\n\toutputParser: N8nOutputParser,\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n): ZodObject<any, any, any, any> {\n\tconst schema =\n\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\t(outputParser.getSchema() as ZodObject<any, any, any, any>) ?? z.object({ text: z.string() });\n\treturn schema;\n}\n\n/* -----------------------------------------------------------\n Binary Data Helpers\n----------------------------------------------------------- */\n/**\n * Extracts binary image messages from the input data.\n * When operating in filesystem mode, the binary stream is first converted to a buffer.\n *\n * @param ctx - The execution context\n * @param itemIndex - The current item index\n * @returns A HumanMessage containing the binary image messages.\n */\nexport async function extractBinaryMessages(\n\tctx: IExecuteFunctions | ISupplyDataFunctions,\n\titemIndex: number,\n): Promise<HumanMessage> {\n\tconst binaryData = ctx.getInputData()?.[itemIndex]?.binary ?? {};\n\tconst binaryMessages = await Promise.all(\n\t\tObject.values(binaryData)\n\t\t\t.filter((data) => data.mimeType.startsWith('image/'))\n\t\t\t.map(async (data) => {\n\t\t\t\tlet binaryUrlString: string;\n\n\t\t\t\t// In filesystem mode we need to get binary stream by id before converting it to buffer\n\t\t\t\tif (data.id) {\n\t\t\t\t\tconst binaryBuffer = await ctx.helpers.binaryToBuffer(\n\t\t\t\t\t\tawait ctx.helpers.getBinaryStream(data.id),\n\t\t\t\t\t);\n\t\t\t\t\tbinaryUrlString = `data:${data.mimeType};base64,${Buffer.from(binaryBuffer).toString(\n\t\t\t\t\t\tBINARY_ENCODING,\n\t\t\t\t\t)}`;\n\t\t\t\t} else {\n\t\t\t\t\tbinaryUrlString = data.data.includes('base64')\n\t\t\t\t\t\t? data.data\n\t\t\t\t\t\t: `data:${data.mimeType};base64,${data.data}`;\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'image_url',\n\t\t\t\t\timage_url: {\n\t\t\t\t\t\turl: binaryUrlString,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}),\n\t);\n\treturn new HumanMessage({\n\t\tcontent: [...binaryMessages],\n\t});\n}\n\n/* -----------------------------------------------------------\n Agent Output Format Helpers\n----------------------------------------------------------- */\n/**\n * Fixes empty content messages in agent steps.\n *\n * This function is necessary when using RunnableSequence.from in LangChain.\n * If a tool doesn't have any arguments, LangChain returns input: '' (empty string).\n * This can throw an error for some providers (like Anthropic) which expect the input to always be an object.\n * This function replaces empty string inputs with empty objects to prevent such errors.\n *\n * @param steps - The agent steps to fix\n * @returns The fixed agent steps\n */\nexport function fixEmptyContentMessage(\n\tsteps: AgentFinish | ToolsAgentAction[],\n): AgentFinish | ToolsAgentAction[] {\n\tif (!Array.isArray(steps)) return steps;\n\n\tsteps.forEach((step) => {\n\t\tif ('messageLog' in step && step.messageLog !== undefined) {\n\t\t\tif (Array.isArray(step.messageLog)) {\n\t\t\t\tstep.messageLog.forEach((message: BaseMessage) => {\n\t\t\t\t\tif ('content' in message && Array.isArray(message.content)) {\n\t\t\t\t\t\t(message.content as Array<{ input?: string | object }>).forEach((content) => {\n\t\t\t\t\t\t\tif (content.input === '') {\n\t\t\t\t\t\t\t\tcontent.input = {};\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t});\n\n\treturn steps;\n}\n\n/**\n * Ensures consistent handling of outputs regardless of the model used,\n * providing a unified output format for further processing.\n *\n * This method is necessary to handle different output formats from various language models.\n * Specifically, it checks if the agent step is the final step (contains returnValues) and determines\n * if the output is a simple string (e.g., from OpenAI models) or an array of outputs (e.g., from Anthropic models).\n *\n * Examples:\n * 1. Anthropic model output:\n * ```json\n * {\n * \"output\": [\n * {\n * \"index\": 0,\n * \"type\": \"text\",\n * \"text\": \"The result of the calculation is approximately 1001.8166...\"\n * }\n * ]\n * }\n *```\n * 2. OpenAI model output:\n * ```json\n * {\n * \"output\": \"The result of the calculation is approximately 1001.82...\"\n * }\n * ```\n *\n * @param steps - The agent finish or agent action steps.\n * @returns The modified agent finish steps or the original steps.\n */\nexport function handleAgentFinishOutput(\n\tsteps: AgentFinish | AgentAction[],\n): AgentFinish | AgentAction[] {\n\ttype AgentMultiOutputFinish = AgentFinish & {\n\t\treturnValues: { output: Array<{ text: string; type: string; index: number }> };\n\t};\n\tconst agentFinishSteps = steps as AgentMultiOutputFinish | AgentFinish;\n\n\tif (agentFinishSteps.returnValues) {\n\t\tconst isMultiOutput = Array.isArray(agentFinishSteps.returnValues?.output);\n\t\tif (isMultiOutput) {\n\t\t\t// If all items in the multi-output array are of type 'text', merge them into a single string\n\t\t\tconst multiOutputSteps = agentFinishSteps.returnValues.output as Array<{\n\t\t\t\tindex: number;\n\t\t\t\ttype: string;\n\t\t\t\ttext: string;\n\t\t\t}>;\n\t\t\tconst isTextOnly = multiOutputSteps.every((output) => 'text' in output);\n\t\t\tif (isTextOnly) {\n\t\t\t\tagentFinishSteps.returnValues.output = multiOutputSteps\n\t\t\t\t\t.map((output) => output.text)\n\t\t\t\t\t.join('\\n')\n\t\t\t\t\t.trim();\n\t\t\t}\n\t\t\treturn agentFinishSteps;\n\t\t}\n\t}\n\n\treturn agentFinishSteps;\n}\n\n/**\n * Wraps the parsed output so that it can be stored in memory.\n * If memory is connected, the output is stringified.\n *\n * @param output - The parsed output object\n * @param memory - The connected memory (if any)\n * @returns The formatted output object\n */\nexport function handleParsedStepOutput(\n\toutput: Record<string, unknown>,\n\tmemory?: BaseChatMemory,\n): { returnValues: Record<string, unknown>; log: string } {\n\treturn {\n\t\treturnValues: memory ? { output: JSON.stringify(output) } : output,\n\t\tlog: 'Final response formatted',\n\t};\n}\n\n/**\n * Parses agent steps using the provided output parser.\n * If the agent used the 'format_final_json_response' tool, the output is parsed accordingly.\n *\n * @param steps - The agent finish or action steps\n * @param outputParser - The output parser (if defined)\n * @param memory - The connected memory (if any)\n * @returns The parsed steps with the final output\n */\nexport const getAgentStepsParser =\n\t(outputParser?: N8nOutputParser, memory?: BaseChatMemory) =>\n\tasync (steps: AgentFinish | AgentAction[]): Promise<AgentFinish | AgentAction[]> => {\n\t\t// Check if the steps contain the 'format_final_json_response' tool invocation.\n\t\tif (Array.isArray(steps)) {\n\t\t\tconst responseParserTool = steps.find((step) => step.tool === 'format_final_json_response');\n\t\t\tif (responseParserTool && outputParser) {\n\t\t\t\tconst toolInput = responseParserTool.toolInput;\n\t\t\t\t// Ensure the tool input is a string\n\t\t\t\tconst parserInput = toolInput instanceof Object ? JSON.stringify(toolInput) : toolInput;\n\t\t\t\tconst returnValues = (await outputParser.parse(parserInput)) as Record<string, unknown>;\n\t\t\t\treturn handleParsedStepOutput(returnValues, memory);\n\t\t\t}\n\t\t}\n\n\t\t// Otherwise, if the steps contain a returnValues field, try to parse them manually.\n\t\tif (outputParser && typeof steps === 'object' && (steps as AgentFinish).returnValues) {\n\t\t\tconst finalResponse = (steps as AgentFinish).returnValues;\n\t\t\tlet parserInput: string;\n\n\t\t\tif (finalResponse instanceof Object) {\n\t\t\t\tif ('output' in finalResponse) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst parsedOutput = jsonParse<Record<string, unknown>>(finalResponse.output);\n\t\t\t\t\t\t// Check if the parsed output already has the expected structure\n\t\t\t\t\t\t// If it already has { output: ... }, use it as-is to avoid double wrapping\n\t\t\t\t\t\t// Otherwise, wrap it in { output: ... } as expected by the parser\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tparsedOutput !== null &&\n\t\t\t\t\t\t\ttypeof parsedOutput === 'object' &&\n\t\t\t\t\t\t\t'output' in parsedOutput &&\n\t\t\t\t\t\t\tObject.keys(parsedOutput).length === 1\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t// Already has the expected structure, use as-is\n\t\t\t\t\t\t\tparserInput = JSON.stringify(parsedOutput);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// Needs wrapping for the parser\n\t\t\t\t\t\t\tparserInput = JSON.stringify({ output: parsedOutput });\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t// Fallback to the raw output if parsing fails.\n\t\t\t\t\t\tparserInput = finalResponse.output;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// If the output is not an object, we will stringify it as it is\n\t\t\t\t\tparserInput = JSON.stringify(finalResponse);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tparserInput = finalResponse;\n\t\t\t}\n\n\t\t\tconst returnValues = (await outputParser.parse(parserInput)) as Record<string, unknown>;\n\t\t\treturn handleParsedStepOutput(returnValues, memory);\n\t\t}\n\n\t\treturn handleAgentFinishOutput(steps);\n\t};\n\n/* -----------------------------------------------------------\n Agent Setup Helpers\n----------------------------------------------------------- */\n/**\n * Retrieves the language model from the input connection.\n * Throws an error if the model is not a valid chat instance or does not support tools.\n *\n * @param ctx - The execution context\n * @returns The validated chat model\n */\nexport async function getChatModel(\n\tctx: IExecuteFunctions | ISupplyDataFunctions,\n\tindex: number = 0,\n): Promise<BaseChatModel | undefined> {\n\tconst connectedModels = await ctx.getInputConnectionData(NodeConnectionTypes.AiLanguageModel, 0);\n\n\tlet model;\n\n\tif (Array.isArray(connectedModels) && index !== undefined) {\n\t\tif (connectedModels.length <= index) {\n\t\t\treturn undefined;\n\t\t}\n\t\t// We get the models in reversed order from the workflow so we need to reverse them to match the right index\n\t\tconst reversedModels = [...connectedModels].reverse();\n\t\tmodel = reversedModels[index] as BaseChatModel;\n\t} else {\n\t\tmodel = connectedModels as BaseChatModel;\n\t}\n\n\tif (!isChatInstance(model) || !model.bindTools) {\n\t\tthrow new NodeOperationError(\n\t\t\tctx.getNode(),\n\t\t\t'Tools Agent requires Chat Model which supports Tools calling',\n\t\t);\n\t}\n\treturn model;\n}\n\n/**\n * Retrieves the memory instance from the input connection if it is connected\n *\n * @param ctx - The execution context\n * @returns The connected memory (if any)\n */\nexport async function getOptionalMemory(\n\tctx: IExecuteFunctions | ISupplyDataFunctions,\n): Promise<BaseChatMemory | undefined> {\n\treturn (await ctx.getInputConnectionData(NodeConnectionTypes.AiMemory, 0)) as\n\t\t| BaseChatMemory\n\t\t| undefined;\n}\n\n/**\n * Retrieves the connected tools and (if an output parser is defined)\n * appends a structured output parser tool.\n *\n * @param ctx - The execution context\n * @param outputParser - The optional output parser\n * @returns The array of connected tools\n */\nexport async function getTools(\n\tctx: IExecuteFunctions | ISupplyDataFunctions,\n\toutputParser?: N8nOutputParser,\n): Promise<Array<DynamicStructuredTool | Tool>> {\n\tconst tools = (await getConnectedTools(ctx, true, false)) as Array<DynamicStructuredTool | Tool>;\n\n\t// If an output parser is available, create a dynamic tool to validate the final output.\n\tif (outputParser) {\n\t\tconst schema = getOutputParserSchema(outputParser);\n\t\tconst structuredOutputParserTool = new DynamicStructuredTool({\n\t\t\tschema,\n\t\t\tname: 'format_final_json_response',\n\t\t\tdescription:\n\t\t\t\t'Use this tool to format your final response to the user in a structured JSON format. This tool validates your output against a schema to ensure it meets the required format. ONLY use this tool when you have completed all necessary reasoning and are ready to provide your final answer. Do not use this tool for intermediate steps or for asking questions. The output from this tool will be directly returned to the user.',\n\t\t\t// We do not use a function here because we intercept the output with the parser.\n\t\t\tfunc: async () => '',\n\t\t});\n\t\ttools.push(structuredOutputParserTool);\n\t}\n\treturn tools;\n}\n\n/**\n * Prepares the prompt messages for the agent.\n *\n * @param ctx - The execution context\n * @param itemIndex - The current item index\n * @param options - Options containing systemMessage and other parameters\n * @returns The array of prompt messages\n */\nexport async function prepareMessages(\n\tctx: IExecuteFunctions | ISupplyDataFunctions,\n\titemIndex: number,\n\toptions: {\n\t\tsystemMessage?: string;\n\t\tpassthroughBinaryImages?: boolean;\n\t\toutputParser?: N8nOutputParser;\n\t},\n): Promise<BaseMessagePromptTemplateLike[]> {\n\tconst useSystemMessage = options.systemMessage ?? ctx.getNode().typeVersion < 1.9;\n\n\tconst messages: BaseMessagePromptTemplateLike[] = [];\n\n\tif (useSystemMessage) {\n\t\tmessages.push([\n\t\t\t'system',\n\t\t\t`{system_message}${options.outputParser ? '\\n\\n{formatting_instructions}' : ''}`,\n\t\t]);\n\t} else if (options.outputParser) {\n\t\tmessages.push(['system', '{formatting_instructions}']);\n\t}\n\n\tmessages.push(['placeholder', '{chat_history}'], ['human', '{input}']);\n\n\t// If there is binary data and the node option permits it, add a binary message\n\tconst hasBinaryData = ctx.getInputData()?.[itemIndex]?.binary !== undefined;\n\tif (hasBinaryData && options.passthroughBinaryImages) {\n\t\tconst binaryMessage = await extractBinaryMessages(ctx, itemIndex);\n\t\tif (binaryMessage.content.length !== 0) {\n\t\t\tmessages.push(binaryMessage);\n\t\t} else {\n\t\t\tctx.logger.debug('Not attaching binary message, since its content was empty');\n\t\t}\n\t}\n\n\t// We add the agent scratchpad last, so that the agent will not run in loops\n\t// by adding binary messages between each interaction\n\tmessages.push(['placeholder', '{agent_scratchpad}']);\n\treturn messages;\n}\n\n/**\n * Creates the chat prompt from messages.\n *\n * @param messages - The messages array\n * @returns The ChatPromptTemplate instance\n */\nexport function preparePrompt(messages: BaseMessagePromptTemplateLike[]): ChatPromptTemplate {\n\treturn ChatPromptTemplate.fromMessages(messages);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -338,6 +338,11 @@ class LmChatOpenAi {
|
|
|
338
338
|
dispatcher: (0, import_httpProxyAgent.getProxyAgent)(configuration.baseURL ?? "https://api.openai.com/v1")
|
|
339
339
|
};
|
|
340
340
|
}
|
|
341
|
+
if (credentials.header && typeof credentials.headerName === "string" && credentials.headerName && typeof credentials.headerValue === "string") {
|
|
342
|
+
configuration.defaultHeaders = {
|
|
343
|
+
[credentials.headerName]: credentials.headerValue
|
|
344
|
+
};
|
|
345
|
+
}
|
|
341
346
|
const modelKwargs = {};
|
|
342
347
|
if (options.responseFormat) modelKwargs.response_format = { type: options.responseFormat };
|
|
343
348
|
if (options.reasoningEffort && ["low", "medium", "high"].includes(options.reasoningEffort))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../nodes/llms/LMChatOpenAi/LmChatOpenAi.node.ts"],"sourcesContent":["import { ChatOpenAI, type ClientOptions } from '@langchain/openai';\nimport {\n\tNodeConnectionTypes,\n\ttype INodeType,\n\ttype INodeTypeDescription,\n\ttype ISupplyDataFunctions,\n\ttype SupplyData,\n} from 'n8n-workflow';\n\nimport { getProxyAgent } from '@utils/httpProxyAgent';\nimport { getConnectionHintNoticeField } from '@utils/sharedFields';\n\nimport { searchModels } from './methods/loadModels';\nimport { openAiFailedAttemptHandler } from '../../vendors/OpenAi/helpers/error-handling';\nimport { makeN8nLlmFailedAttemptHandler } from '../n8nLlmFailedAttemptHandler';\nimport { N8nLlmTracing } from '../N8nLlmTracing';\n\nexport class LmChatOpenAi implements INodeType {\n\tmethods = {\n\t\tlistSearch: {\n\t\t\tsearchModels,\n\t\t},\n\t};\n\n\tdescription: INodeTypeDescription = {\n\t\tdisplayName: 'OpenAI Chat Model',\n\n\t\tname: 'lmChatOpenAi',\n\t\ticon: { light: 'file:openAiLight.svg', dark: 'file:openAiLight.dark.svg' },\n\t\tgroup: ['transform'],\n\t\tversion: [1, 1.1, 1.2],\n\t\tdescription: 'For advanced usage with an AI chain',\n\t\tdefaults: {\n\t\t\tname: 'OpenAI Chat Model',\n\t\t},\n\t\tcodex: {\n\t\t\tcategories: ['AI'],\n\t\t\tsubcategories: {\n\t\t\t\tAI: ['Language Models', 'Root Nodes'],\n\t\t\t\t'Language Models': ['Chat Models (Recommended)'],\n\t\t\t},\n\t\t\tresources: {\n\t\t\t\tprimaryDocumentation: [\n\t\t\t\t\t{\n\t\t\t\t\t\turl: 'https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.lmchatopenai/',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t},\n\n\t\tinputs: [],\n\n\t\toutputs: [NodeConnectionTypes.AiLanguageModel],\n\t\toutputNames: ['Model'],\n\t\tcredentials: [\n\t\t\t{\n\t\t\t\tname: 'openAiApi',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t\trequestDefaults: {\n\t\t\tignoreHttpStatusErrors: true,\n\t\t\tbaseURL:\n\t\t\t\t'={{ $parameter.options?.baseURL?.split(\"/\").slice(0,-1).join(\"/\") || $credentials?.url?.split(\"/\").slice(0,-1).join(\"/\") || \"https://api.openai.com\" }}',\n\t\t},\n\t\tproperties: [\n\t\t\tgetConnectionHintNoticeField([NodeConnectionTypes.AiChain, NodeConnectionTypes.AiAgent]),\n\t\t\t{\n\t\t\t\tdisplayName:\n\t\t\t\t\t'If using JSON response format, you must include word \"json\" in the prompt in your chain or agent. Also, make sure to select latest models released post November 2023.',\n\t\t\t\tname: 'notice',\n\t\t\t\ttype: 'notice',\n\t\t\t\tdefault: '',\n\t\t\t\tdisplayOptions: {\n\t\t\t\t\tshow: {\n\t\t\t\t\t\t'/options.responseFormat': ['json_object'],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Model',\n\t\t\t\tname: 'model',\n\t\t\t\ttype: 'options',\n\t\t\t\tdescription:\n\t\t\t\t\t'The model which will generate the completion. <a href=\"https://beta.openai.com/docs/models/overview\">Learn more</a>.',\n\t\t\t\ttypeOptions: {\n\t\t\t\t\tloadOptions: {\n\t\t\t\t\t\trouting: {\n\t\t\t\t\t\t\trequest: {\n\t\t\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t\t\t\turl: '={{ $parameter.options?.baseURL?.split(\"/\").slice(-1).pop() || $credentials?.url?.split(\"/\").slice(-1).pop() || \"v1\" }}/models',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\toutput: {\n\t\t\t\t\t\t\t\tpostReceive: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: 'rootProperty',\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tproperty: 'data',\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: 'filter',\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t// If the baseURL is not set or is set to api.openai.com, include only chat models\n\t\t\t\t\t\t\t\t\t\t\tpass: `={{\n\t\t\t\t\t\t\t\t\t\t\t\t($parameter.options?.baseURL && !$parameter.options?.baseURL?.startsWith('https://api.openai.com/')) ||\n\t\t\t\t\t\t\t\t\t\t\t\t($credentials?.url && !$credentials.url.startsWith('https://api.openai.com/')) ||\n\t\t\t\t\t\t\t\t\t\t\t\t$responseItem.id.startsWith('ft:') ||\n\t\t\t\t\t\t\t\t\t\t\t\t$responseItem.id.startsWith('o1') ||\n\t\t\t\t\t\t\t\t\t\t\t\t$responseItem.id.startsWith('o3') ||\n\t\t\t\t\t\t\t\t\t\t\t\t($responseItem.id.startsWith('gpt-') && !$responseItem.id.includes('instruct'))\n\t\t\t\t\t\t\t\t\t\t\t}}`,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: 'setKeyValue',\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tname: '={{$responseItem.id}}',\n\t\t\t\t\t\t\t\t\t\t\tvalue: '={{$responseItem.id}}',\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: 'sort',\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tkey: 'name',\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\trouting: {\n\t\t\t\t\tsend: {\n\t\t\t\t\t\ttype: 'body',\n\t\t\t\t\t\tproperty: 'model',\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tdefault: 'gpt-4o-mini',\n\t\t\t\tdisplayOptions: {\n\t\t\t\t\thide: {\n\t\t\t\t\t\t'@version': [{ _cnd: { gte: 1.2 } }],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Model',\n\t\t\t\tname: 'model',\n\t\t\t\ttype: 'resourceLocator',\n\t\t\t\tdefault: { mode: 'list', value: 'gpt-4.1-mini' },\n\t\t\t\trequired: true,\n\t\t\t\tmodes: [\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'From List',\n\t\t\t\t\t\tname: 'list',\n\t\t\t\t\t\ttype: 'list',\n\t\t\t\t\t\tplaceholder: 'Select a model...',\n\t\t\t\t\t\ttypeOptions: {\n\t\t\t\t\t\t\tsearchListMethod: 'searchModels',\n\t\t\t\t\t\t\tsearchable: true,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'ID',\n\t\t\t\t\t\tname: 'id',\n\t\t\t\t\t\ttype: 'string',\n\t\t\t\t\t\tplaceholder: 'gpt-4.1-mini',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tdescription: 'The model. Choose from the list, or specify an ID.',\n\t\t\t\tdisplayOptions: {\n\t\t\t\t\thide: {\n\t\t\t\t\t\t'@version': [{ _cnd: { lte: 1.1 } }],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName:\n\t\t\t\t\t'When using non-OpenAI models via \"Base URL\" override, not all models might be chat-compatible or support other features, like tools calling or JSON response format',\n\t\t\t\tname: 'notice',\n\t\t\t\ttype: 'notice',\n\t\t\t\tdefault: '',\n\t\t\t\tdisplayOptions: {\n\t\t\t\t\tshow: {\n\t\t\t\t\t\t'/options.baseURL': [{ _cnd: { exists: true } }],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Options',\n\t\t\t\tname: 'options',\n\t\t\t\tplaceholder: 'Add Option',\n\t\t\t\tdescription: 'Additional options to add',\n\t\t\t\ttype: 'collection',\n\t\t\t\tdefault: {},\n\t\t\t\toptions: [\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Base URL',\n\t\t\t\t\t\tname: 'baseURL',\n\t\t\t\t\t\tdefault: 'https://api.openai.com/v1',\n\t\t\t\t\t\tdescription: 'Override the default base URL for the API',\n\t\t\t\t\t\ttype: 'string',\n\t\t\t\t\t\tdisplayOptions: {\n\t\t\t\t\t\t\thide: {\n\t\t\t\t\t\t\t\t'@version': [{ _cnd: { gte: 1.1 } }],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Frequency Penalty',\n\t\t\t\t\t\tname: 'frequencyPenalty',\n\t\t\t\t\t\tdefault: 0,\n\t\t\t\t\t\ttypeOptions: { maxValue: 2, minValue: -2, numberPrecision: 1 },\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim\",\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Maximum Number of Tokens',\n\t\t\t\t\t\tname: 'maxTokens',\n\t\t\t\t\t\tdefault: -1,\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t'The maximum number of tokens to generate in the completion. Most models have a context length of 2048 tokens (except for the newest models, which support 32,768).',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t\ttypeOptions: {\n\t\t\t\t\t\t\tmaxValue: 32768,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Response Format',\n\t\t\t\t\t\tname: 'responseFormat',\n\t\t\t\t\t\tdefault: 'text',\n\t\t\t\t\t\ttype: 'options',\n\t\t\t\t\t\toptions: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tname: 'Text',\n\t\t\t\t\t\t\t\tvalue: 'text',\n\t\t\t\t\t\t\t\tdescription: 'Regular text response',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tname: 'JSON',\n\t\t\t\t\t\t\t\tvalue: 'json_object',\n\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t'Enables JSON mode, which should guarantee the message the model generates is valid JSON',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Presence Penalty',\n\t\t\t\t\t\tname: 'presencePenalty',\n\t\t\t\t\t\tdefault: 0,\n\t\t\t\t\t\ttypeOptions: { maxValue: 2, minValue: -2, numberPrecision: 1 },\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics\",\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Sampling Temperature',\n\t\t\t\t\t\tname: 'temperature',\n\t\t\t\t\t\tdefault: 0.7,\n\t\t\t\t\t\ttypeOptions: { maxValue: 2, minValue: 0, numberPrecision: 1 },\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t'Controls randomness: Lowering results in less random completions. As the temperature approaches zero, the model will become deterministic and repetitive.',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Reasoning Effort',\n\t\t\t\t\t\tname: 'reasoningEffort',\n\t\t\t\t\t\tdefault: 'medium',\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t'Controls the amount of reasoning tokens to use. A value of \"low\" will favor speed and economical token usage, \"high\" will favor more complete reasoning at the cost of more tokens generated and slower responses.',\n\t\t\t\t\t\ttype: 'options',\n\t\t\t\t\t\toptions: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tname: 'Low',\n\t\t\t\t\t\t\t\tvalue: 'low',\n\t\t\t\t\t\t\t\tdescription: 'Favors speed and economical token usage',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tname: 'Medium',\n\t\t\t\t\t\t\t\tvalue: 'medium',\n\t\t\t\t\t\t\t\tdescription: 'Balance between speed and reasoning accuracy',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tname: 'High',\n\t\t\t\t\t\t\t\tvalue: 'high',\n\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t'Favors more complete reasoning at the cost of more tokens generated and slower responses',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t\tdisplayOptions: {\n\t\t\t\t\t\t\tshow: {\n\t\t\t\t\t\t\t\t// reasoning_effort is only available on o1, o1-versioned, or on o3-mini and beyond, and gpt-5 models. Not on o1-mini or other GPT-models.\n\t\t\t\t\t\t\t\t'/model': [{ _cnd: { regex: '(^o1([-\\\\d]+)?$)|(^o[3-9].*)|(^gpt-5.*)' } }],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Timeout',\n\t\t\t\t\t\tname: 'timeout',\n\t\t\t\t\t\tdefault: 60000,\n\t\t\t\t\t\tdescription: 'Maximum amount of time a request is allowed to take in milliseconds',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Max Retries',\n\t\t\t\t\t\tname: 'maxRetries',\n\t\t\t\t\t\tdefault: 2,\n\t\t\t\t\t\tdescription: 'Maximum number of retries to attempt',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Top P',\n\t\t\t\t\t\tname: 'topP',\n\t\t\t\t\t\tdefault: 1,\n\t\t\t\t\t\ttypeOptions: { maxValue: 1, minValue: 0, numberPrecision: 1 },\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t'Controls diversity via nucleus sampling: 0.5 means half of all likelihood-weighted options are considered. We generally recommend altering this or temperature but not both.',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t};\n\n\tasync supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {\n\t\tconst credentials = await this.getCredentials('openAiApi');\n\n\t\tconst version = this.getNode().typeVersion;\n\t\tconst modelName =\n\t\t\tversion >= 1.2\n\t\t\t\t? (this.getNodeParameter('model.value', itemIndex) as string)\n\t\t\t\t: (this.getNodeParameter('model', itemIndex) as string);\n\n\t\tconst options = this.getNodeParameter('options', itemIndex, {}) as {\n\t\t\tbaseURL?: string;\n\t\t\tfrequencyPenalty?: number;\n\t\t\tmaxTokens?: number;\n\t\t\tmaxRetries: number;\n\t\t\ttimeout: number;\n\t\t\tpresencePenalty?: number;\n\t\t\ttemperature?: number;\n\t\t\ttopP?: number;\n\t\t\tresponseFormat?: 'text' | 'json_object';\n\t\t\treasoningEffort?: 'low' | 'medium' | 'high';\n\t\t};\n\n\t\tconst configuration: ClientOptions = {};\n\n\t\tif (options.baseURL) {\n\t\t\tconfiguration.baseURL = options.baseURL;\n\t\t} else if (credentials.url) {\n\t\t\tconfiguration.baseURL = credentials.url as string;\n\t\t}\n\n\t\tif (configuration.baseURL) {\n\t\t\tconfiguration.fetchOptions = {\n\t\t\t\tdispatcher: getProxyAgent(configuration.baseURL ?? 'https://api.openai.com/v1'),\n\t\t\t};\n\t\t}\n\n\t\t// Extra options to send to OpenAI, that are not directly supported by LangChain\n\t\tconst modelKwargs: {\n\t\t\tresponse_format?: object;\n\t\t\treasoning_effort?: 'low' | 'medium' | 'high';\n\t\t} = {};\n\t\tif (options.responseFormat) modelKwargs.response_format = { type: options.responseFormat };\n\t\tif (options.reasoningEffort && ['low', 'medium', 'high'].includes(options.reasoningEffort))\n\t\t\tmodelKwargs.reasoning_effort = options.reasoningEffort;\n\n\t\tconst model = new ChatOpenAI({\n\t\t\tapiKey: credentials.apiKey as string,\n\t\t\tmodel: modelName,\n\t\t\t...options,\n\t\t\ttimeout: options.timeout ?? 60000,\n\t\t\tmaxRetries: options.maxRetries ?? 2,\n\t\t\tconfiguration,\n\t\t\tcallbacks: [new N8nLlmTracing(this)],\n\t\t\tmodelKwargs,\n\t\t\tonFailedAttempt: makeN8nLlmFailedAttemptHandler(this, openAiFailedAttemptHandler),\n\t\t});\n\n\t\treturn {\n\t\t\tresponse: model,\n\t\t};\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAA+C;AAC/C,0BAMO;AAEP,4BAA8B;AAC9B,0BAA6C;AAE7C,wBAA6B;AAC7B,4BAA2C;AAC3C,wCAA+C;AAC/C,2BAA8B;AAEvB,MAAM,aAAkC;AAAA,EAAxC;AACN,mBAAU;AAAA,MACT,YAAY;AAAA,QACX;AAAA,MACD;AAAA,IACD;AAEA,uBAAoC;AAAA,MACnC,aAAa;AAAA,MAEb,MAAM;AAAA,MACN,MAAM,EAAE,OAAO,wBAAwB,MAAM,4BAA4B;AAAA,MACzE,OAAO,CAAC,WAAW;AAAA,MACnB,SAAS,CAAC,GAAG,KAAK,GAAG;AAAA,MACrB,aAAa;AAAA,MACb,UAAU;AAAA,QACT,MAAM;AAAA,MACP;AAAA,MACA,OAAO;AAAA,QACN,YAAY,CAAC,IAAI;AAAA,QACjB,eAAe;AAAA,UACd,IAAI,CAAC,mBAAmB,YAAY;AAAA,UACpC,mBAAmB,CAAC,2BAA2B;AAAA,QAChD;AAAA,QACA,WAAW;AAAA,UACV,sBAAsB;AAAA,YACrB;AAAA,cACC,KAAK;AAAA,YACN;AAAA,UACD;AAAA,QACD;AAAA,MACD;AAAA,MAEA,QAAQ,CAAC;AAAA,MAET,SAAS,CAAC,wCAAoB,eAAe;AAAA,MAC7C,aAAa,CAAC,OAAO;AAAA,MACrB,aAAa;AAAA,QACZ;AAAA,UACC,MAAM;AAAA,UACN,UAAU;AAAA,QACX;AAAA,MACD;AAAA,MACA,iBAAiB;AAAA,QAChB,wBAAwB;AAAA,QACxB,SACC;AAAA,MACF;AAAA,MACA,YAAY;AAAA,YACX,kDAA6B,CAAC,wCAAoB,SAAS,wCAAoB,OAAO,CAAC;AAAA,QACvF;AAAA,UACC,aACC;AAAA,UACD,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,gBAAgB;AAAA,YACf,MAAM;AAAA,cACL,2BAA2B,CAAC,aAAa;AAAA,YAC1C;AAAA,UACD;AAAA,QACD;AAAA,QACA;AAAA,UACC,aAAa;AAAA,UACb,MAAM;AAAA,UACN,MAAM;AAAA,UACN,aACC;AAAA,UACD,aAAa;AAAA,YACZ,aAAa;AAAA,cACZ,SAAS;AAAA,gBACR,SAAS;AAAA,kBACR,QAAQ;AAAA,kBACR,KAAK;AAAA,gBACN;AAAA,gBACA,QAAQ;AAAA,kBACP,aAAa;AAAA,oBACZ;AAAA,sBACC,MAAM;AAAA,sBACN,YAAY;AAAA,wBACX,UAAU;AAAA,sBACX;AAAA,oBACD;AAAA,oBACA;AAAA,sBACC,MAAM;AAAA,sBACN,YAAY;AAAA;AAAA,wBAEX,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sBAQP;AAAA,oBACD;AAAA,oBACA;AAAA,sBACC,MAAM;AAAA,sBACN,YAAY;AAAA,wBACX,MAAM;AAAA,wBACN,OAAO;AAAA,sBACR;AAAA,oBACD;AAAA,oBACA;AAAA,sBACC,MAAM;AAAA,sBACN,YAAY;AAAA,wBACX,KAAK;AAAA,sBACN;AAAA,oBACD;AAAA,kBACD;AAAA,gBACD;AAAA,cACD;AAAA,YACD;AAAA,UACD;AAAA,UACA,SAAS;AAAA,YACR,MAAM;AAAA,cACL,MAAM;AAAA,cACN,UAAU;AAAA,YACX;AAAA,UACD;AAAA,UACA,SAAS;AAAA,UACT,gBAAgB;AAAA,YACf,MAAM;AAAA,cACL,YAAY,CAAC,EAAE,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC;AAAA,YACpC;AAAA,UACD;AAAA,QACD;AAAA,QACA;AAAA,UACC,aAAa;AAAA,UACb,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS,EAAE,MAAM,QAAQ,OAAO,eAAe;AAAA,UAC/C,UAAU;AAAA,UACV,OAAO;AAAA,YACN;AAAA,cACC,aAAa;AAAA,cACb,MAAM;AAAA,cACN,MAAM;AAAA,cACN,aAAa;AAAA,cACb,aAAa;AAAA,gBACZ,kBAAkB;AAAA,gBAClB,YAAY;AAAA,cACb;AAAA,YACD;AAAA,YACA;AAAA,cACC,aAAa;AAAA,cACb,MAAM;AAAA,cACN,MAAM;AAAA,cACN,aAAa;AAAA,YACd;AAAA,UACD;AAAA,UACA,aAAa;AAAA,UACb,gBAAgB;AAAA,YACf,MAAM;AAAA,cACL,YAAY,CAAC,EAAE,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC;AAAA,YACpC;AAAA,UACD;AAAA,QACD;AAAA,QACA;AAAA,UACC,aACC;AAAA,UACD,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,gBAAgB;AAAA,YACf,MAAM;AAAA,cACL,oBAAoB,CAAC,EAAE,MAAM,EAAE,QAAQ,KAAK,EAAE,CAAC;AAAA,YAChD;AAAA,UACD;AAAA,QACD;AAAA,QACA;AAAA,UACC,aAAa;AAAA,UACb,MAAM;AAAA,UACN,aAAa;AAAA,UACb,aAAa;AAAA,UACb,MAAM;AAAA,UACN,SAAS,CAAC;AAAA,UACV,SAAS;AAAA,YACR;AAAA,cACC,aAAa;AAAA,cACb,MAAM;AAAA,cACN,SAAS;AAAA,cACT,aAAa;AAAA,cACb,MAAM;AAAA,cACN,gBAAgB;AAAA,gBACf,MAAM;AAAA,kBACL,YAAY,CAAC,EAAE,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC;AAAA,gBACpC;AAAA,cACD;AAAA,YACD;AAAA,YACA;AAAA,cACC,aAAa;AAAA,cACb,MAAM;AAAA,cACN,SAAS;AAAA,cACT,aAAa,EAAE,UAAU,GAAG,UAAU,IAAI,iBAAiB,EAAE;AAAA,cAC7D,aACC;AAAA,cACD,MAAM;AAAA,YACP;AAAA,YACA;AAAA,cACC,aAAa;AAAA,cACb,MAAM;AAAA,cACN,SAAS;AAAA,cACT,aACC;AAAA,cACD,MAAM;AAAA,cACN,aAAa;AAAA,gBACZ,UAAU;AAAA,cACX;AAAA,YACD;AAAA,YACA;AAAA,cACC,aAAa;AAAA,cACb,MAAM;AAAA,cACN,SAAS;AAAA,cACT,MAAM;AAAA,cACN,SAAS;AAAA,gBACR;AAAA,kBACC,MAAM;AAAA,kBACN,OAAO;AAAA,kBACP,aAAa;AAAA,gBACd;AAAA,gBACA;AAAA,kBACC,MAAM;AAAA,kBACN,OAAO;AAAA,kBACP,aACC;AAAA,gBACF;AAAA,cACD;AAAA,YACD;AAAA,YACA;AAAA,cACC,aAAa;AAAA,cACb,MAAM;AAAA,cACN,SAAS;AAAA,cACT,aAAa,EAAE,UAAU,GAAG,UAAU,IAAI,iBAAiB,EAAE;AAAA,cAC7D,aACC;AAAA,cACD,MAAM;AAAA,YACP;AAAA,YACA;AAAA,cACC,aAAa;AAAA,cACb,MAAM;AAAA,cACN,SAAS;AAAA,cACT,aAAa,EAAE,UAAU,GAAG,UAAU,GAAG,iBAAiB,EAAE;AAAA,cAC5D,aACC;AAAA,cACD,MAAM;AAAA,YACP;AAAA,YACA;AAAA,cACC,aAAa;AAAA,cACb,MAAM;AAAA,cACN,SAAS;AAAA,cACT,aACC;AAAA,cACD,MAAM;AAAA,cACN,SAAS;AAAA,gBACR;AAAA,kBACC,MAAM;AAAA,kBACN,OAAO;AAAA,kBACP,aAAa;AAAA,gBACd;AAAA,gBACA;AAAA,kBACC,MAAM;AAAA,kBACN,OAAO;AAAA,kBACP,aAAa;AAAA,gBACd;AAAA,gBACA;AAAA,kBACC,MAAM;AAAA,kBACN,OAAO;AAAA,kBACP,aACC;AAAA,gBACF;AAAA,cACD;AAAA,cACA,gBAAgB;AAAA,gBACf,MAAM;AAAA;AAAA,kBAEL,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,0CAA0C,EAAE,CAAC;AAAA,gBAC1E;AAAA,cACD;AAAA,YACD;AAAA,YACA;AAAA,cACC,aAAa;AAAA,cACb,MAAM;AAAA,cACN,SAAS;AAAA,cACT,aAAa;AAAA,cACb,MAAM;AAAA,YACP;AAAA,YACA;AAAA,cACC,aAAa;AAAA,cACb,MAAM;AAAA,cACN,SAAS;AAAA,cACT,aAAa;AAAA,cACb,MAAM;AAAA,YACP;AAAA,YACA;AAAA,cACC,aAAa;AAAA,cACb,MAAM;AAAA,cACN,SAAS;AAAA,cACT,aAAa,EAAE,UAAU,GAAG,UAAU,GAAG,iBAAiB,EAAE;AAAA,cAC5D,aACC;AAAA,cACD,MAAM;AAAA,YACP;AAAA,UACD;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA;AAAA,EAEA,MAAM,WAAuC,WAAwC;AACpF,UAAM,cAAc,MAAM,KAAK,eAAe,WAAW;AAEzD,UAAM,UAAU,KAAK,QAAQ,EAAE;AAC/B,UAAM,YACL,WAAW,MACP,KAAK,iBAAiB,eAAe,SAAS,IAC9C,KAAK,iBAAiB,SAAS,SAAS;AAE7C,UAAM,UAAU,KAAK,iBAAiB,WAAW,WAAW,CAAC,CAAC;AAa9D,UAAM,gBAA+B,CAAC;AAEtC,QAAI,QAAQ,SAAS;AACpB,oBAAc,UAAU,QAAQ;AAAA,IACjC,WAAW,YAAY,KAAK;AAC3B,oBAAc,UAAU,YAAY;AAAA,IACrC;AAEA,QAAI,cAAc,SAAS;AAC1B,oBAAc,eAAe;AAAA,QAC5B,gBAAY,qCAAc,cAAc,WAAW,2BAA2B;AAAA,MAC/E;AAAA,IACD;AAGA,UAAM,cAGF,CAAC;AACL,QAAI,QAAQ,eAAgB,aAAY,kBAAkB,EAAE,MAAM,QAAQ,eAAe;AACzF,QAAI,QAAQ,mBAAmB,CAAC,OAAO,UAAU,MAAM,EAAE,SAAS,QAAQ,eAAe;AACxF,kBAAY,mBAAmB,QAAQ;AAExC,UAAM,QAAQ,IAAI,yBAAW;AAAA,MAC5B,QAAQ,YAAY;AAAA,MACpB,OAAO;AAAA,MACP,GAAG;AAAA,MACH,SAAS,QAAQ,WAAW;AAAA,MAC5B,YAAY,QAAQ,cAAc;AAAA,MAClC;AAAA,MACA,WAAW,CAAC,IAAI,mCAAc,IAAI,CAAC;AAAA,MACnC;AAAA,MACA,qBAAiB,kEAA+B,MAAM,gDAA0B;AAAA,IACjF,CAAC;AAED,WAAO;AAAA,MACN,UAAU;AAAA,IACX;AAAA,EACD;AACD;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../nodes/llms/LMChatOpenAi/LmChatOpenAi.node.ts"],"sourcesContent":["import { ChatOpenAI, type ClientOptions } from '@langchain/openai';\nimport {\n\tNodeConnectionTypes,\n\ttype INodeType,\n\ttype INodeTypeDescription,\n\ttype ISupplyDataFunctions,\n\ttype SupplyData,\n} from 'n8n-workflow';\n\nimport { getProxyAgent } from '@utils/httpProxyAgent';\nimport { getConnectionHintNoticeField } from '@utils/sharedFields';\n\nimport { searchModels } from './methods/loadModels';\nimport { openAiFailedAttemptHandler } from '../../vendors/OpenAi/helpers/error-handling';\nimport { makeN8nLlmFailedAttemptHandler } from '../n8nLlmFailedAttemptHandler';\nimport { N8nLlmTracing } from '../N8nLlmTracing';\n\nexport class LmChatOpenAi implements INodeType {\n\tmethods = {\n\t\tlistSearch: {\n\t\t\tsearchModels,\n\t\t},\n\t};\n\n\tdescription: INodeTypeDescription = {\n\t\tdisplayName: 'OpenAI Chat Model',\n\n\t\tname: 'lmChatOpenAi',\n\t\ticon: { light: 'file:openAiLight.svg', dark: 'file:openAiLight.dark.svg' },\n\t\tgroup: ['transform'],\n\t\tversion: [1, 1.1, 1.2],\n\t\tdescription: 'For advanced usage with an AI chain',\n\t\tdefaults: {\n\t\t\tname: 'OpenAI Chat Model',\n\t\t},\n\t\tcodex: {\n\t\t\tcategories: ['AI'],\n\t\t\tsubcategories: {\n\t\t\t\tAI: ['Language Models', 'Root Nodes'],\n\t\t\t\t'Language Models': ['Chat Models (Recommended)'],\n\t\t\t},\n\t\t\tresources: {\n\t\t\t\tprimaryDocumentation: [\n\t\t\t\t\t{\n\t\t\t\t\t\turl: 'https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.lmchatopenai/',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t},\n\n\t\tinputs: [],\n\n\t\toutputs: [NodeConnectionTypes.AiLanguageModel],\n\t\toutputNames: ['Model'],\n\t\tcredentials: [\n\t\t\t{\n\t\t\t\tname: 'openAiApi',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t\trequestDefaults: {\n\t\t\tignoreHttpStatusErrors: true,\n\t\t\tbaseURL:\n\t\t\t\t'={{ $parameter.options?.baseURL?.split(\"/\").slice(0,-1).join(\"/\") || $credentials?.url?.split(\"/\").slice(0,-1).join(\"/\") || \"https://api.openai.com\" }}',\n\t\t},\n\t\tproperties: [\n\t\t\tgetConnectionHintNoticeField([NodeConnectionTypes.AiChain, NodeConnectionTypes.AiAgent]),\n\t\t\t{\n\t\t\t\tdisplayName:\n\t\t\t\t\t'If using JSON response format, you must include word \"json\" in the prompt in your chain or agent. Also, make sure to select latest models released post November 2023.',\n\t\t\t\tname: 'notice',\n\t\t\t\ttype: 'notice',\n\t\t\t\tdefault: '',\n\t\t\t\tdisplayOptions: {\n\t\t\t\t\tshow: {\n\t\t\t\t\t\t'/options.responseFormat': ['json_object'],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Model',\n\t\t\t\tname: 'model',\n\t\t\t\ttype: 'options',\n\t\t\t\tdescription:\n\t\t\t\t\t'The model which will generate the completion. <a href=\"https://beta.openai.com/docs/models/overview\">Learn more</a>.',\n\t\t\t\ttypeOptions: {\n\t\t\t\t\tloadOptions: {\n\t\t\t\t\t\trouting: {\n\t\t\t\t\t\t\trequest: {\n\t\t\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t\t\t\turl: '={{ $parameter.options?.baseURL?.split(\"/\").slice(-1).pop() || $credentials?.url?.split(\"/\").slice(-1).pop() || \"v1\" }}/models',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\toutput: {\n\t\t\t\t\t\t\t\tpostReceive: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: 'rootProperty',\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tproperty: 'data',\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: 'filter',\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t// If the baseURL is not set or is set to api.openai.com, include only chat models\n\t\t\t\t\t\t\t\t\t\t\tpass: `={{\n\t\t\t\t\t\t\t\t\t\t\t\t($parameter.options?.baseURL && !$parameter.options?.baseURL?.startsWith('https://api.openai.com/')) ||\n\t\t\t\t\t\t\t\t\t\t\t\t($credentials?.url && !$credentials.url.startsWith('https://api.openai.com/')) ||\n\t\t\t\t\t\t\t\t\t\t\t\t$responseItem.id.startsWith('ft:') ||\n\t\t\t\t\t\t\t\t\t\t\t\t$responseItem.id.startsWith('o1') ||\n\t\t\t\t\t\t\t\t\t\t\t\t$responseItem.id.startsWith('o3') ||\n\t\t\t\t\t\t\t\t\t\t\t\t($responseItem.id.startsWith('gpt-') && !$responseItem.id.includes('instruct'))\n\t\t\t\t\t\t\t\t\t\t\t}}`,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: 'setKeyValue',\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tname: '={{$responseItem.id}}',\n\t\t\t\t\t\t\t\t\t\t\tvalue: '={{$responseItem.id}}',\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: 'sort',\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tkey: 'name',\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\trouting: {\n\t\t\t\t\tsend: {\n\t\t\t\t\t\ttype: 'body',\n\t\t\t\t\t\tproperty: 'model',\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tdefault: 'gpt-4o-mini',\n\t\t\t\tdisplayOptions: {\n\t\t\t\t\thide: {\n\t\t\t\t\t\t'@version': [{ _cnd: { gte: 1.2 } }],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Model',\n\t\t\t\tname: 'model',\n\t\t\t\ttype: 'resourceLocator',\n\t\t\t\tdefault: { mode: 'list', value: 'gpt-4.1-mini' },\n\t\t\t\trequired: true,\n\t\t\t\tmodes: [\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'From List',\n\t\t\t\t\t\tname: 'list',\n\t\t\t\t\t\ttype: 'list',\n\t\t\t\t\t\tplaceholder: 'Select a model...',\n\t\t\t\t\t\ttypeOptions: {\n\t\t\t\t\t\t\tsearchListMethod: 'searchModels',\n\t\t\t\t\t\t\tsearchable: true,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'ID',\n\t\t\t\t\t\tname: 'id',\n\t\t\t\t\t\ttype: 'string',\n\t\t\t\t\t\tplaceholder: 'gpt-4.1-mini',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tdescription: 'The model. Choose from the list, or specify an ID.',\n\t\t\t\tdisplayOptions: {\n\t\t\t\t\thide: {\n\t\t\t\t\t\t'@version': [{ _cnd: { lte: 1.1 } }],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName:\n\t\t\t\t\t'When using non-OpenAI models via \"Base URL\" override, not all models might be chat-compatible or support other features, like tools calling or JSON response format',\n\t\t\t\tname: 'notice',\n\t\t\t\ttype: 'notice',\n\t\t\t\tdefault: '',\n\t\t\t\tdisplayOptions: {\n\t\t\t\t\tshow: {\n\t\t\t\t\t\t'/options.baseURL': [{ _cnd: { exists: true } }],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Options',\n\t\t\t\tname: 'options',\n\t\t\t\tplaceholder: 'Add Option',\n\t\t\t\tdescription: 'Additional options to add',\n\t\t\t\ttype: 'collection',\n\t\t\t\tdefault: {},\n\t\t\t\toptions: [\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Base URL',\n\t\t\t\t\t\tname: 'baseURL',\n\t\t\t\t\t\tdefault: 'https://api.openai.com/v1',\n\t\t\t\t\t\tdescription: 'Override the default base URL for the API',\n\t\t\t\t\t\ttype: 'string',\n\t\t\t\t\t\tdisplayOptions: {\n\t\t\t\t\t\t\thide: {\n\t\t\t\t\t\t\t\t'@version': [{ _cnd: { gte: 1.1 } }],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Frequency Penalty',\n\t\t\t\t\t\tname: 'frequencyPenalty',\n\t\t\t\t\t\tdefault: 0,\n\t\t\t\t\t\ttypeOptions: { maxValue: 2, minValue: -2, numberPrecision: 1 },\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim\",\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Maximum Number of Tokens',\n\t\t\t\t\t\tname: 'maxTokens',\n\t\t\t\t\t\tdefault: -1,\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t'The maximum number of tokens to generate in the completion. Most models have a context length of 2048 tokens (except for the newest models, which support 32,768).',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t\ttypeOptions: {\n\t\t\t\t\t\t\tmaxValue: 32768,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Response Format',\n\t\t\t\t\t\tname: 'responseFormat',\n\t\t\t\t\t\tdefault: 'text',\n\t\t\t\t\t\ttype: 'options',\n\t\t\t\t\t\toptions: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tname: 'Text',\n\t\t\t\t\t\t\t\tvalue: 'text',\n\t\t\t\t\t\t\t\tdescription: 'Regular text response',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tname: 'JSON',\n\t\t\t\t\t\t\t\tvalue: 'json_object',\n\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t'Enables JSON mode, which should guarantee the message the model generates is valid JSON',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Presence Penalty',\n\t\t\t\t\t\tname: 'presencePenalty',\n\t\t\t\t\t\tdefault: 0,\n\t\t\t\t\t\ttypeOptions: { maxValue: 2, minValue: -2, numberPrecision: 1 },\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics\",\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Sampling Temperature',\n\t\t\t\t\t\tname: 'temperature',\n\t\t\t\t\t\tdefault: 0.7,\n\t\t\t\t\t\ttypeOptions: { maxValue: 2, minValue: 0, numberPrecision: 1 },\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t'Controls randomness: Lowering results in less random completions. As the temperature approaches zero, the model will become deterministic and repetitive.',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Reasoning Effort',\n\t\t\t\t\t\tname: 'reasoningEffort',\n\t\t\t\t\t\tdefault: 'medium',\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t'Controls the amount of reasoning tokens to use. A value of \"low\" will favor speed and economical token usage, \"high\" will favor more complete reasoning at the cost of more tokens generated and slower responses.',\n\t\t\t\t\t\ttype: 'options',\n\t\t\t\t\t\toptions: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tname: 'Low',\n\t\t\t\t\t\t\t\tvalue: 'low',\n\t\t\t\t\t\t\t\tdescription: 'Favors speed and economical token usage',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tname: 'Medium',\n\t\t\t\t\t\t\t\tvalue: 'medium',\n\t\t\t\t\t\t\t\tdescription: 'Balance between speed and reasoning accuracy',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tname: 'High',\n\t\t\t\t\t\t\t\tvalue: 'high',\n\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t'Favors more complete reasoning at the cost of more tokens generated and slower responses',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t\tdisplayOptions: {\n\t\t\t\t\t\t\tshow: {\n\t\t\t\t\t\t\t\t// reasoning_effort is only available on o1, o1-versioned, or on o3-mini and beyond, and gpt-5 models. Not on o1-mini or other GPT-models.\n\t\t\t\t\t\t\t\t'/model': [{ _cnd: { regex: '(^o1([-\\\\d]+)?$)|(^o[3-9].*)|(^gpt-5.*)' } }],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Timeout',\n\t\t\t\t\t\tname: 'timeout',\n\t\t\t\t\t\tdefault: 60000,\n\t\t\t\t\t\tdescription: 'Maximum amount of time a request is allowed to take in milliseconds',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Max Retries',\n\t\t\t\t\t\tname: 'maxRetries',\n\t\t\t\t\t\tdefault: 2,\n\t\t\t\t\t\tdescription: 'Maximum number of retries to attempt',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Top P',\n\t\t\t\t\t\tname: 'topP',\n\t\t\t\t\t\tdefault: 1,\n\t\t\t\t\t\ttypeOptions: { maxValue: 1, minValue: 0, numberPrecision: 1 },\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t'Controls diversity via nucleus sampling: 0.5 means half of all likelihood-weighted options are considered. We generally recommend altering this or temperature but not both.',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t};\n\n\tasync supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {\n\t\tconst credentials = await this.getCredentials('openAiApi');\n\n\t\tconst version = this.getNode().typeVersion;\n\t\tconst modelName =\n\t\t\tversion >= 1.2\n\t\t\t\t? (this.getNodeParameter('model.value', itemIndex) as string)\n\t\t\t\t: (this.getNodeParameter('model', itemIndex) as string);\n\n\t\tconst options = this.getNodeParameter('options', itemIndex, {}) as {\n\t\t\tbaseURL?: string;\n\t\t\tfrequencyPenalty?: number;\n\t\t\tmaxTokens?: number;\n\t\t\tmaxRetries: number;\n\t\t\ttimeout: number;\n\t\t\tpresencePenalty?: number;\n\t\t\ttemperature?: number;\n\t\t\ttopP?: number;\n\t\t\tresponseFormat?: 'text' | 'json_object';\n\t\t\treasoningEffort?: 'low' | 'medium' | 'high';\n\t\t};\n\n\t\tconst configuration: ClientOptions = {};\n\n\t\tif (options.baseURL) {\n\t\t\tconfiguration.baseURL = options.baseURL;\n\t\t} else if (credentials.url) {\n\t\t\tconfiguration.baseURL = credentials.url as string;\n\t\t}\n\n\t\tif (configuration.baseURL) {\n\t\t\tconfiguration.fetchOptions = {\n\t\t\t\tdispatcher: getProxyAgent(configuration.baseURL ?? 'https://api.openai.com/v1'),\n\t\t\t};\n\t\t}\n\t\tif (\n\t\t\tcredentials.header &&\n\t\t\ttypeof credentials.headerName === 'string' &&\n\t\t\tcredentials.headerName &&\n\t\t\ttypeof credentials.headerValue === 'string'\n\t\t) {\n\t\t\tconfiguration.defaultHeaders = {\n\t\t\t\t[credentials.headerName]: credentials.headerValue,\n\t\t\t};\n\t\t}\n\n\t\t// Extra options to send to OpenAI, that are not directly supported by LangChain\n\t\tconst modelKwargs: {\n\t\t\tresponse_format?: object;\n\t\t\treasoning_effort?: 'low' | 'medium' | 'high';\n\t\t} = {};\n\t\tif (options.responseFormat) modelKwargs.response_format = { type: options.responseFormat };\n\t\tif (options.reasoningEffort && ['low', 'medium', 'high'].includes(options.reasoningEffort))\n\t\t\tmodelKwargs.reasoning_effort = options.reasoningEffort;\n\n\t\tconst model = new ChatOpenAI({\n\t\t\tapiKey: credentials.apiKey as string,\n\t\t\tmodel: modelName,\n\t\t\t...options,\n\t\t\ttimeout: options.timeout ?? 60000,\n\t\t\tmaxRetries: options.maxRetries ?? 2,\n\t\t\tconfiguration,\n\t\t\tcallbacks: [new N8nLlmTracing(this)],\n\t\t\tmodelKwargs,\n\t\t\tonFailedAttempt: makeN8nLlmFailedAttemptHandler(this, openAiFailedAttemptHandler),\n\t\t});\n\n\t\treturn {\n\t\t\tresponse: model,\n\t\t};\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
@@ -22,7 +22,10 @@ __export(ToolCode_node_exports, {
|
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(ToolCode_node_exports);
|
|
24
24
|
var import_tools = require("@langchain/core/tools");
|
|
25
|
+
var import_config = require("@n8n/config");
|
|
26
|
+
var import_di = require("@n8n/di");
|
|
25
27
|
var import_JavaScriptSandbox = require("n8n-nodes-base/dist/nodes/Code/JavaScriptSandbox");
|
|
28
|
+
var import_JsTaskRunnerSandbox = require("n8n-nodes-base/dist/nodes/Code/JsTaskRunnerSandbox");
|
|
26
29
|
var import_PythonSandbox = require("n8n-nodes-base/dist/nodes/Code/PythonSandbox");
|
|
27
30
|
var import_Sandbox = require("n8n-nodes-base/dist/nodes/Code/Sandbox");
|
|
28
31
|
var import_n8n_workflow = require("n8n-workflow");
|
|
@@ -188,6 +191,8 @@ class ToolCode {
|
|
|
188
191
|
async supplyData(itemIndex) {
|
|
189
192
|
const node = this.getNode();
|
|
190
193
|
const workflowMode = this.getMode();
|
|
194
|
+
const runnersConfig = import_di.Container.get(import_config.TaskRunnersConfig);
|
|
195
|
+
const isRunnerEnabled = runnersConfig.enabled;
|
|
191
196
|
const { typeVersion } = node;
|
|
192
197
|
const name = typeVersion <= 1.1 ? this.getNodeParameter("name", itemIndex) : (0, import_n8n_workflow.nodeNameToToolName)(node);
|
|
193
198
|
const description = this.getNodeParameter("description", itemIndex);
|
|
@@ -215,8 +220,23 @@ class ToolCode {
|
|
|
215
220
|
return sandbox;
|
|
216
221
|
};
|
|
217
222
|
const runFunction = async (query) => {
|
|
218
|
-
|
|
219
|
-
|
|
223
|
+
if (language === "javaScript" && isRunnerEnabled) {
|
|
224
|
+
const sandbox = new import_JsTaskRunnerSandbox.JsTaskRunnerSandbox(
|
|
225
|
+
code,
|
|
226
|
+
"runOnceForAllItems",
|
|
227
|
+
workflowMode,
|
|
228
|
+
this,
|
|
229
|
+
void 0,
|
|
230
|
+
{
|
|
231
|
+
query
|
|
232
|
+
}
|
|
233
|
+
);
|
|
234
|
+
const executionData = await sandbox.runCodeForTool();
|
|
235
|
+
return executionData;
|
|
236
|
+
} else {
|
|
237
|
+
const sandbox = getSandbox(query, itemIndex);
|
|
238
|
+
return await sandbox.runCode();
|
|
239
|
+
}
|
|
220
240
|
};
|
|
221
241
|
const toolHandler = async (query) => {
|
|
222
242
|
const { index } = this.addInputData(import_n8n_workflow.NodeConnectionTypes.AiTool, [[{ json: { query } }]]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../nodes/tools/ToolCode/ToolCode.node.ts"],"sourcesContent":["import { DynamicStructuredTool, DynamicTool } from '@langchain/core/tools';\nimport type { JSONSchema7 } from 'json-schema';\nimport { JavaScriptSandbox } from 'n8n-nodes-base/dist/nodes/Code/JavaScriptSandbox';\nimport { PythonSandbox } from 'n8n-nodes-base/dist/nodes/Code/PythonSandbox';\nimport type { Sandbox } from 'n8n-nodes-base/dist/nodes/Code/Sandbox';\nimport { getSandboxContext } from 'n8n-nodes-base/dist/nodes/Code/Sandbox';\nimport type {\n\tINodeType,\n\tINodeTypeDescription,\n\tISupplyDataFunctions,\n\tSupplyData,\n\tExecutionError,\n\tIDataObject,\n} from 'n8n-workflow';\nimport {\n\tjsonParse,\n\tNodeConnectionTypes,\n\tNodeOperationError,\n\tnodeNameToToolName,\n} from 'n8n-workflow';\n\nimport {\n\tbuildInputSchemaField,\n\tbuildJsonSchemaExampleField,\n\tbuildJsonSchemaExampleNotice,\n\tschemaTypeField,\n} from '@utils/descriptions';\nimport { convertJsonSchemaToZod, generateSchemaFromExample } from '@utils/schemaParsing';\nimport { getConnectionHintNoticeField } from '@utils/sharedFields';\n\nimport type { DynamicZodObject } from '../../../types/zod.types';\n\nconst jsonSchemaExampleField = buildJsonSchemaExampleField({\n\tshowExtraProps: { specifyInputSchema: [true] },\n});\n\nconst jsonSchemaExampleNotice = buildJsonSchemaExampleNotice({\n\tshowExtraProps: {\n\t\tspecifyInputSchema: [true],\n\t\t'@version': [{ _cnd: { gte: 1.3 } }],\n\t},\n});\n\nconst jsonSchemaField = buildInputSchemaField({ showExtraProps: { specifyInputSchema: [true] } });\n\nexport class ToolCode implements INodeType {\n\tdescription: INodeTypeDescription = {\n\t\tdisplayName: 'Code Tool',\n\t\tname: 'toolCode',\n\t\ticon: 'fa:code',\n\t\ticonColor: 'black',\n\t\tgroup: ['transform'],\n\t\tversion: [1, 1.1, 1.2, 1.3],\n\t\tdescription: 'Write a tool in JS or Python',\n\t\tdefaults: {\n\t\t\tname: 'Code Tool',\n\t\t},\n\t\tcodex: {\n\t\t\tcategories: ['AI'],\n\t\t\tsubcategories: {\n\t\t\t\tAI: ['Tools'],\n\t\t\t\tTools: ['Recommended Tools'],\n\t\t\t},\n\t\t\tresources: {\n\t\t\t\tprimaryDocumentation: [\n\t\t\t\t\t{\n\t\t\t\t\t\turl: 'https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.toolcode/',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t},\n\n\t\tinputs: [],\n\n\t\toutputs: [NodeConnectionTypes.AiTool],\n\t\toutputNames: ['Tool'],\n\t\tproperties: [\n\t\t\tgetConnectionHintNoticeField([NodeConnectionTypes.AiAgent]),\n\t\t\t{\n\t\t\t\tdisplayName:\n\t\t\t\t\t'See an example of a conversational agent with custom tool written in JavaScript <a href=\"/templates/1963\" target=\"_blank\">here</a>.',\n\t\t\t\tname: 'noticeTemplateExample',\n\t\t\t\ttype: 'notice',\n\t\t\t\tdefault: '',\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Name',\n\t\t\t\tname: 'name',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: '',\n\t\t\t\tplaceholder: 'My_Tool',\n\t\t\t\tdisplayOptions: {\n\t\t\t\t\tshow: {\n\t\t\t\t\t\t'@version': [1],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Name',\n\t\t\t\tname: 'name',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: '',\n\t\t\t\tplaceholder: 'e.g. My_Tool',\n\t\t\t\tvalidateType: 'string-alphanumeric',\n\t\t\t\tdescription:\n\t\t\t\t\t'The name of the function to be called, could contain letters, numbers, and underscores only',\n\t\t\t\tdisplayOptions: {\n\t\t\t\t\tshow: {\n\t\t\t\t\t\t'@version': [1.1],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Description',\n\t\t\t\tname: 'description',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: '',\n\t\t\t\tplaceholder:\n\t\t\t\t\t'Call this tool to get a random color. The input should be a string with comma separted names of colors to exclude.',\n\t\t\t\ttypeOptions: {\n\t\t\t\t\trows: 3,\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t{\n\t\t\t\tdisplayName: 'Language',\n\t\t\t\tname: 'language',\n\t\t\t\ttype: 'options',\n\t\t\t\tnoDataExpression: true,\n\t\t\t\toptions: [\n\t\t\t\t\t{\n\t\t\t\t\t\tname: 'JavaScript',\n\t\t\t\t\t\tvalue: 'javaScript',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tname: 'Python (Beta)',\n\t\t\t\t\t\tvalue: 'python',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tdefault: 'javaScript',\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'JavaScript',\n\t\t\t\tname: 'jsCode',\n\t\t\t\ttype: 'string',\n\t\t\t\tdisplayOptions: {\n\t\t\t\t\tshow: {\n\t\t\t\t\t\tlanguage: ['javaScript'],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\ttypeOptions: {\n\t\t\t\t\teditor: 'jsEditor',\n\t\t\t\t},\n\t\t\t\tdefault:\n\t\t\t\t\t'// Example: convert the incoming query to uppercase and return it\\nreturn query.toUpperCase()',\n\t\t\t\t// TODO: Add proper text here later\n\t\t\t\thint: 'You can access the input the tool receives via the input property \"query\". The returned value should be a single string.',\n\t\t\t\t// eslint-disable-next-line n8n-nodes-base/node-param-description-missing-final-period\n\t\t\t\tdescription: 'E.g. Converts any text to uppercase',\n\t\t\t\tnoDataExpression: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Python',\n\t\t\t\tname: 'pythonCode',\n\t\t\t\ttype: 'string',\n\t\t\t\tdisplayOptions: {\n\t\t\t\t\tshow: {\n\t\t\t\t\t\tlanguage: ['python'],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\ttypeOptions: {\n\t\t\t\t\teditor: 'codeNodeEditor', // TODO: create a separate `pythonEditor` component\n\t\t\t\t\teditorLanguage: 'python',\n\t\t\t\t},\n\t\t\t\tdefault:\n\t\t\t\t\t'# Example: convert the incoming query to uppercase and return it\\nreturn query.upper()',\n\t\t\t\t// TODO: Add proper text here later\n\t\t\t\thint: 'You can access the input the tool receives via the input property \"query\". The returned value should be a single string.',\n\t\t\t\t// eslint-disable-next-line n8n-nodes-base/node-param-description-missing-final-period\n\t\t\t\tdescription: 'E.g. Converts any text to uppercase',\n\t\t\t\tnoDataExpression: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Specify Input Schema',\n\t\t\t\tname: 'specifyInputSchema',\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdescription:\n\t\t\t\t\t'Whether to specify the schema for the function. This would require the LLM to provide the input in the correct format and would validate it against the schema.',\n\t\t\t\tnoDataExpression: true,\n\t\t\t\tdefault: false,\n\t\t\t},\n\t\t\t{ ...schemaTypeField, displayOptions: { show: { specifyInputSchema: [true] } } },\n\t\t\tjsonSchemaExampleField,\n\t\t\tjsonSchemaExampleNotice,\n\t\t\tjsonSchemaField,\n\t\t],\n\t};\n\n\tasync supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {\n\t\tconst node = this.getNode();\n\t\tconst workflowMode = this.getMode();\n\n\t\tconst { typeVersion } = node;\n\t\tconst name =\n\t\t\ttypeVersion <= 1.1\n\t\t\t\t? (this.getNodeParameter('name', itemIndex) as string)\n\t\t\t\t: nodeNameToToolName(node);\n\n\t\tconst description = this.getNodeParameter('description', itemIndex) as string;\n\n\t\tconst useSchema = this.getNodeParameter('specifyInputSchema', itemIndex) as boolean;\n\n\t\tconst language = this.getNodeParameter('language', itemIndex) as string;\n\t\tlet code = '';\n\t\tif (language === 'javaScript') {\n\t\t\tcode = this.getNodeParameter('jsCode', itemIndex) as string;\n\t\t} else {\n\t\t\tcode = this.getNodeParameter('pythonCode', itemIndex) as string;\n\t\t}\n\n\t\tconst getSandbox = (query: string | IDataObject, index = 0) => {\n\t\t\tconst context = getSandboxContext.call(this, index);\n\t\t\tcontext.query = query;\n\n\t\t\tlet sandbox: Sandbox;\n\t\t\tif (language === 'javaScript') {\n\t\t\t\tsandbox = new JavaScriptSandbox(context, code, this.helpers);\n\t\t\t} else {\n\t\t\t\tsandbox = new PythonSandbox(context, code, this.helpers);\n\t\t\t}\n\n\t\t\tsandbox.on(\n\t\t\t\t'output',\n\t\t\t\tworkflowMode === 'manual'\n\t\t\t\t\t? this.sendMessageToUI.bind(this)\n\t\t\t\t\t: (...args: unknown[]) =>\n\t\t\t\t\t\t\tconsole.log(`[Workflow \"${this.getWorkflow().id}\"][Node \"${node.name}\"]`, ...args),\n\t\t\t);\n\t\t\treturn sandbox;\n\t\t};\n\n\t\tconst runFunction = async (query: string | IDataObject): Promise<string> => {\n\t\t\tconst sandbox = getSandbox(query, itemIndex);\n\t\t\treturn await sandbox.runCode<string>();\n\t\t};\n\n\t\tconst toolHandler = async (query: string | IDataObject): Promise<string> => {\n\t\t\tconst { index } = this.addInputData(NodeConnectionTypes.AiTool, [[{ json: { query } }]]);\n\n\t\t\tlet response: string = '';\n\t\t\tlet executionError: ExecutionError | undefined;\n\t\t\ttry {\n\t\t\t\tresponse = await runFunction(query);\n\t\t\t} catch (error: unknown) {\n\t\t\t\texecutionError = new NodeOperationError(this.getNode(), error as ExecutionError);\n\t\t\t\tresponse = `There was an error: \"${executionError.message}\"`;\n\t\t\t}\n\n\t\t\tif (typeof response === 'number') {\n\t\t\t\tresponse = (response as number).toString();\n\t\t\t}\n\n\t\t\tif (typeof response !== 'string') {\n\t\t\t\t// TODO: Do some more testing. Issues here should actually fail the workflow\n\t\t\t\texecutionError = new NodeOperationError(this.getNode(), 'Wrong output type returned', {\n\t\t\t\t\tdescription: `The response property should be a string, but it is an ${typeof response}`,\n\t\t\t\t});\n\t\t\t\tresponse = `There was an error: \"${executionError.message}\"`;\n\t\t\t}\n\n\t\t\tif (executionError) {\n\t\t\t\tvoid this.addOutputData(NodeConnectionTypes.AiTool, index, executionError);\n\t\t\t} else {\n\t\t\t\tvoid this.addOutputData(NodeConnectionTypes.AiTool, index, [[{ json: { response } }]]);\n\t\t\t}\n\n\t\t\treturn response;\n\t\t};\n\n\t\tconst commonToolOptions = {\n\t\t\tname,\n\t\t\tdescription,\n\t\t\tfunc: toolHandler,\n\t\t};\n\n\t\tlet tool: DynamicTool | DynamicStructuredTool | undefined = undefined;\n\n\t\tif (useSchema) {\n\t\t\ttry {\n\t\t\t\t// We initialize these even though one of them will always be empty\n\t\t\t\t// it makes it easier to navigate the ternary operator\n\t\t\t\tconst jsonExample = this.getNodeParameter('jsonSchemaExample', itemIndex, '') as string;\n\t\t\t\tconst inputSchema = this.getNodeParameter('inputSchema', itemIndex, '') as string;\n\n\t\t\t\tconst schemaType = this.getNodeParameter('schemaType', itemIndex) as 'fromJson' | 'manual';\n\n\t\t\t\tconst jsonSchema =\n\t\t\t\t\tschemaType === 'fromJson'\n\t\t\t\t\t\t? generateSchemaFromExample(jsonExample, this.getNode().typeVersion >= 1.3)\n\t\t\t\t\t\t: jsonParse<JSONSchema7>(inputSchema);\n\n\t\t\t\tconst zodSchema = convertJsonSchemaToZod<DynamicZodObject>(jsonSchema);\n\n\t\t\t\ttool = new DynamicStructuredTool({\n\t\t\t\t\tschema: zodSchema,\n\t\t\t\t\t...commonToolOptions,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tthrow new NodeOperationError(\n\t\t\t\t\tthis.getNode(),\n\t\t\t\t\t'Error during parsing of JSON Schema. \\n ' + error,\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\ttool = new DynamicTool(commonToolOptions);\n\t\t}\n\n\t\treturn {\n\t\t\tresponse: tool,\n\t\t};\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../nodes/tools/ToolCode/ToolCode.node.ts"],"sourcesContent":["import { DynamicStructuredTool, DynamicTool } from '@langchain/core/tools';\nimport { TaskRunnersConfig } from '@n8n/config';\nimport { Container } from '@n8n/di';\nimport type { JSONSchema7 } from 'json-schema';\nimport { JavaScriptSandbox } from 'n8n-nodes-base/dist/nodes/Code/JavaScriptSandbox';\nimport { JsTaskRunnerSandbox } from 'n8n-nodes-base/dist/nodes/Code/JsTaskRunnerSandbox';\nimport { PythonSandbox } from 'n8n-nodes-base/dist/nodes/Code/PythonSandbox';\nimport type { Sandbox } from 'n8n-nodes-base/dist/nodes/Code/Sandbox';\nimport { getSandboxContext } from 'n8n-nodes-base/dist/nodes/Code/Sandbox';\nimport type {\n\tExecutionError,\n\tIDataObject,\n\tINodeType,\n\tINodeTypeDescription,\n\tISupplyDataFunctions,\n\tSupplyData,\n} from 'n8n-workflow';\n\nimport {\n\tjsonParse,\n\tNodeConnectionTypes,\n\tnodeNameToToolName,\n\tNodeOperationError,\n} from 'n8n-workflow';\nimport {\n\tbuildInputSchemaField,\n\tbuildJsonSchemaExampleField,\n\tbuildJsonSchemaExampleNotice,\n\tschemaTypeField,\n} from '@utils/descriptions';\nimport { convertJsonSchemaToZod, generateSchemaFromExample } from '@utils/schemaParsing';\nimport { getConnectionHintNoticeField } from '@utils/sharedFields';\n\nimport type { DynamicZodObject } from '../../../types/zod.types';\n\nconst jsonSchemaExampleField = buildJsonSchemaExampleField({\n\tshowExtraProps: { specifyInputSchema: [true] },\n});\n\nconst jsonSchemaExampleNotice = buildJsonSchemaExampleNotice({\n\tshowExtraProps: {\n\t\tspecifyInputSchema: [true],\n\t\t'@version': [{ _cnd: { gte: 1.3 } }],\n\t},\n});\n\nconst jsonSchemaField = buildInputSchemaField({ showExtraProps: { specifyInputSchema: [true] } });\n\nexport class ToolCode implements INodeType {\n\tdescription: INodeTypeDescription = {\n\t\tdisplayName: 'Code Tool',\n\t\tname: 'toolCode',\n\t\ticon: 'fa:code',\n\t\ticonColor: 'black',\n\t\tgroup: ['transform'],\n\t\tversion: [1, 1.1, 1.2, 1.3],\n\t\tdescription: 'Write a tool in JS or Python',\n\t\tdefaults: {\n\t\t\tname: 'Code Tool',\n\t\t},\n\t\tcodex: {\n\t\t\tcategories: ['AI'],\n\t\t\tsubcategories: {\n\t\t\t\tAI: ['Tools'],\n\t\t\t\tTools: ['Recommended Tools'],\n\t\t\t},\n\t\t\tresources: {\n\t\t\t\tprimaryDocumentation: [\n\t\t\t\t\t{\n\t\t\t\t\t\turl: 'https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.toolcode/',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t},\n\n\t\tinputs: [],\n\n\t\toutputs: [NodeConnectionTypes.AiTool],\n\t\toutputNames: ['Tool'],\n\t\tproperties: [\n\t\t\tgetConnectionHintNoticeField([NodeConnectionTypes.AiAgent]),\n\t\t\t{\n\t\t\t\tdisplayName:\n\t\t\t\t\t'See an example of a conversational agent with custom tool written in JavaScript <a href=\"/templates/1963\" target=\"_blank\">here</a>.',\n\t\t\t\tname: 'noticeTemplateExample',\n\t\t\t\ttype: 'notice',\n\t\t\t\tdefault: '',\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Name',\n\t\t\t\tname: 'name',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: '',\n\t\t\t\tplaceholder: 'My_Tool',\n\t\t\t\tdisplayOptions: {\n\t\t\t\t\tshow: {\n\t\t\t\t\t\t'@version': [1],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Name',\n\t\t\t\tname: 'name',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: '',\n\t\t\t\tplaceholder: 'e.g. My_Tool',\n\t\t\t\tvalidateType: 'string-alphanumeric',\n\t\t\t\tdescription:\n\t\t\t\t\t'The name of the function to be called, could contain letters, numbers, and underscores only',\n\t\t\t\tdisplayOptions: {\n\t\t\t\t\tshow: {\n\t\t\t\t\t\t'@version': [1.1],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Description',\n\t\t\t\tname: 'description',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: '',\n\t\t\t\tplaceholder:\n\t\t\t\t\t'Call this tool to get a random color. The input should be a string with comma separted names of colors to exclude.',\n\t\t\t\ttypeOptions: {\n\t\t\t\t\trows: 3,\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t{\n\t\t\t\tdisplayName: 'Language',\n\t\t\t\tname: 'language',\n\t\t\t\ttype: 'options',\n\t\t\t\tnoDataExpression: true,\n\t\t\t\toptions: [\n\t\t\t\t\t{\n\t\t\t\t\t\tname: 'JavaScript',\n\t\t\t\t\t\tvalue: 'javaScript',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tname: 'Python (Beta)',\n\t\t\t\t\t\tvalue: 'python',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tdefault: 'javaScript',\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'JavaScript',\n\t\t\t\tname: 'jsCode',\n\t\t\t\ttype: 'string',\n\t\t\t\tdisplayOptions: {\n\t\t\t\t\tshow: {\n\t\t\t\t\t\tlanguage: ['javaScript'],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\ttypeOptions: {\n\t\t\t\t\teditor: 'jsEditor',\n\t\t\t\t},\n\t\t\t\tdefault:\n\t\t\t\t\t'// Example: convert the incoming query to uppercase and return it\\nreturn query.toUpperCase()',\n\t\t\t\t// TODO: Add proper text here later\n\t\t\t\thint: 'You can access the input the tool receives via the input property \"query\". The returned value should be a single string.',\n\t\t\t\t// eslint-disable-next-line n8n-nodes-base/node-param-description-missing-final-period\n\t\t\t\tdescription: 'E.g. Converts any text to uppercase',\n\t\t\t\tnoDataExpression: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Python',\n\t\t\t\tname: 'pythonCode',\n\t\t\t\ttype: 'string',\n\t\t\t\tdisplayOptions: {\n\t\t\t\t\tshow: {\n\t\t\t\t\t\tlanguage: ['python'],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\ttypeOptions: {\n\t\t\t\t\teditor: 'codeNodeEditor', // TODO: create a separate `pythonEditor` component\n\t\t\t\t\teditorLanguage: 'python',\n\t\t\t\t},\n\t\t\t\tdefault:\n\t\t\t\t\t'# Example: convert the incoming query to uppercase and return it\\nreturn query.upper()',\n\t\t\t\t// TODO: Add proper text here later\n\t\t\t\thint: 'You can access the input the tool receives via the input property \"query\". The returned value should be a single string.',\n\t\t\t\t// eslint-disable-next-line n8n-nodes-base/node-param-description-missing-final-period\n\t\t\t\tdescription: 'E.g. Converts any text to uppercase',\n\t\t\t\tnoDataExpression: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Specify Input Schema',\n\t\t\t\tname: 'specifyInputSchema',\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdescription:\n\t\t\t\t\t'Whether to specify the schema for the function. This would require the LLM to provide the input in the correct format and would validate it against the schema.',\n\t\t\t\tnoDataExpression: true,\n\t\t\t\tdefault: false,\n\t\t\t},\n\t\t\t{ ...schemaTypeField, displayOptions: { show: { specifyInputSchema: [true] } } },\n\t\t\tjsonSchemaExampleField,\n\t\t\tjsonSchemaExampleNotice,\n\t\t\tjsonSchemaField,\n\t\t],\n\t};\n\n\tasync supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {\n\t\tconst node = this.getNode();\n\t\tconst workflowMode = this.getMode();\n\n\t\tconst runnersConfig = Container.get(TaskRunnersConfig);\n\t\tconst isRunnerEnabled = runnersConfig.enabled;\n\n\t\tconst { typeVersion } = node;\n\t\tconst name =\n\t\t\ttypeVersion <= 1.1\n\t\t\t\t? (this.getNodeParameter('name', itemIndex) as string)\n\t\t\t\t: nodeNameToToolName(node);\n\n\t\tconst description = this.getNodeParameter('description', itemIndex) as string;\n\n\t\tconst useSchema = this.getNodeParameter('specifyInputSchema', itemIndex) as boolean;\n\n\t\tconst language = this.getNodeParameter('language', itemIndex) as string;\n\t\tlet code = '';\n\t\tif (language === 'javaScript') {\n\t\t\tcode = this.getNodeParameter('jsCode', itemIndex) as string;\n\t\t} else {\n\t\t\tcode = this.getNodeParameter('pythonCode', itemIndex) as string;\n\t\t}\n\n\t\t// @deprecated - TODO: Remove this after a new python runner is implemented\n\t\tconst getSandbox = (query: string | IDataObject, index = 0) => {\n\t\t\tconst context = getSandboxContext.call(this, index);\n\t\t\tcontext.query = query;\n\n\t\t\tlet sandbox: Sandbox;\n\t\t\tif (language === 'javaScript') {\n\t\t\t\tsandbox = new JavaScriptSandbox(context, code, this.helpers);\n\t\t\t} else {\n\t\t\t\tsandbox = new PythonSandbox(context, code, this.helpers);\n\t\t\t}\n\n\t\t\tsandbox.on(\n\t\t\t\t'output',\n\t\t\t\tworkflowMode === 'manual'\n\t\t\t\t\t? this.sendMessageToUI.bind(this)\n\t\t\t\t\t: (...args: unknown[]) =>\n\t\t\t\t\t\t\tconsole.log(`[Workflow \"${this.getWorkflow().id}\"][Node \"${node.name}\"]`, ...args),\n\t\t\t);\n\t\t\treturn sandbox;\n\t\t};\n\n\t\tconst runFunction = async (query: string | IDataObject): Promise<unknown> => {\n\t\t\tif (language === 'javaScript' && isRunnerEnabled) {\n\t\t\t\tconst sandbox = new JsTaskRunnerSandbox(\n\t\t\t\t\tcode,\n\t\t\t\t\t'runOnceForAllItems',\n\t\t\t\t\tworkflowMode,\n\t\t\t\t\tthis,\n\t\t\t\t\tundefined,\n\t\t\t\t\t{\n\t\t\t\t\t\tquery,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\tconst executionData = await sandbox.runCodeForTool();\n\t\t\t\treturn executionData;\n\t\t\t} else {\n\t\t\t\t// use old vm2-based sandbox for python or when without runner enabled\n\t\t\t\tconst sandbox = getSandbox(query, itemIndex);\n\t\t\t\treturn await sandbox.runCode<string>();\n\t\t\t}\n\t\t};\n\n\t\tconst toolHandler = async (query: string | IDataObject): Promise<string> => {\n\t\t\tconst { index } = this.addInputData(NodeConnectionTypes.AiTool, [[{ json: { query } }]]);\n\n\t\t\tlet response: any = '';\n\t\t\tlet executionError: ExecutionError | undefined;\n\t\t\ttry {\n\t\t\t\tresponse = await runFunction(query);\n\t\t\t} catch (error: unknown) {\n\t\t\t\texecutionError = new NodeOperationError(this.getNode(), error as ExecutionError);\n\t\t\t\tresponse = `There was an error: \"${executionError.message}\"`;\n\t\t\t}\n\n\t\t\tif (typeof response === 'number') {\n\t\t\t\tresponse = (response as number).toString();\n\t\t\t}\n\n\t\t\tif (typeof response !== 'string') {\n\t\t\t\t// TODO: Do some more testing. Issues here should actually fail the workflow\n\t\t\t\texecutionError = new NodeOperationError(this.getNode(), 'Wrong output type returned', {\n\t\t\t\t\tdescription: `The response property should be a string, but it is an ${typeof response}`,\n\t\t\t\t});\n\t\t\t\tresponse = `There was an error: \"${executionError.message}\"`;\n\t\t\t}\n\n\t\t\tif (executionError) {\n\t\t\t\tvoid this.addOutputData(NodeConnectionTypes.AiTool, index, executionError);\n\t\t\t} else {\n\t\t\t\tvoid this.addOutputData(NodeConnectionTypes.AiTool, index, [[{ json: { response } }]]);\n\t\t\t}\n\n\t\t\treturn response;\n\t\t};\n\n\t\tconst commonToolOptions = {\n\t\t\tname,\n\t\t\tdescription,\n\t\t\tfunc: toolHandler,\n\t\t};\n\n\t\tlet tool: DynamicTool | DynamicStructuredTool | undefined = undefined;\n\n\t\tif (useSchema) {\n\t\t\ttry {\n\t\t\t\t// We initialize these even though one of them will always be empty\n\t\t\t\t// it makes it easier to navigate the ternary operator\n\t\t\t\tconst jsonExample = this.getNodeParameter('jsonSchemaExample', itemIndex, '') as string;\n\t\t\t\tconst inputSchema = this.getNodeParameter('inputSchema', itemIndex, '') as string;\n\n\t\t\t\tconst schemaType = this.getNodeParameter('schemaType', itemIndex) as 'fromJson' | 'manual';\n\n\t\t\t\tconst jsonSchema =\n\t\t\t\t\tschemaType === 'fromJson'\n\t\t\t\t\t\t? generateSchemaFromExample(jsonExample, this.getNode().typeVersion >= 1.3)\n\t\t\t\t\t\t: jsonParse<JSONSchema7>(inputSchema);\n\n\t\t\t\tconst zodSchema = convertJsonSchemaToZod<DynamicZodObject>(jsonSchema);\n\n\t\t\t\ttool = new DynamicStructuredTool({\n\t\t\t\t\tschema: zodSchema,\n\t\t\t\t\t...commonToolOptions,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tthrow new NodeOperationError(\n\t\t\t\t\tthis.getNode(),\n\t\t\t\t\t'Error during parsing of JSON Schema. \\n ' + error,\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\ttool = new DynamicTool(commonToolOptions);\n\t\t}\n\n\t\treturn {\n\t\t\tresponse: tool,\n\t\t};\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var edit_operation_exports = {};
|
|
20
|
+
__export(edit_operation_exports, {
|
|
21
|
+
description: () => description,
|
|
22
|
+
execute: () => execute
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(edit_operation_exports);
|
|
25
|
+
var import_n8n_workflow = require("n8n-workflow");
|
|
26
|
+
var import_utils = require("../../helpers/utils");
|
|
27
|
+
var import_transport = require("../../transport");
|
|
28
|
+
function isImagesParameter(param) {
|
|
29
|
+
if (typeof param !== "object" || param === null) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
const paramObj = param;
|
|
33
|
+
if (!("values" in paramObj)) {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
if (!Array.isArray(paramObj.values)) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return paramObj.values.every((item) => {
|
|
40
|
+
if (typeof item !== "object" || item === null) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
const itemObj = item;
|
|
44
|
+
if (!("binaryPropertyName" in itemObj)) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
return typeof itemObj.binaryPropertyName === "string" || itemObj.binaryPropertyName === void 0;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function isGenerateContentResponse(response) {
|
|
51
|
+
if (typeof response !== "object" || response === null) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
const responseObj = response;
|
|
55
|
+
if (!("candidates" in responseObj) || !Array.isArray(responseObj.candidates)) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
return responseObj.candidates.every((candidate) => {
|
|
59
|
+
if (typeof candidate !== "object" || candidate === null) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
const candidateObj = candidate;
|
|
63
|
+
if (!("content" in candidateObj) || typeof candidateObj.content !== "object" || candidateObj.content === null) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
const contentObj = candidateObj.content;
|
|
67
|
+
return "parts" in contentObj && Array.isArray(contentObj.parts);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
const properties = [
|
|
71
|
+
{
|
|
72
|
+
displayName: "Prompt",
|
|
73
|
+
name: "prompt",
|
|
74
|
+
type: "string",
|
|
75
|
+
placeholder: "e.g. combine the first image with the second image",
|
|
76
|
+
description: "Instruction describing how to edit the image",
|
|
77
|
+
default: "",
|
|
78
|
+
typeOptions: {
|
|
79
|
+
rows: 2
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
displayName: "Images",
|
|
84
|
+
name: "images",
|
|
85
|
+
type: "fixedCollection",
|
|
86
|
+
placeholder: "Add Image",
|
|
87
|
+
typeOptions: {
|
|
88
|
+
multipleValues: true,
|
|
89
|
+
multipleValueButtonText: "Add Image"
|
|
90
|
+
},
|
|
91
|
+
default: { values: [{ binaryPropertyName: "data" }] },
|
|
92
|
+
description: "Add one or more binary fields to include images with your prompt",
|
|
93
|
+
options: [
|
|
94
|
+
{
|
|
95
|
+
displayName: "Image",
|
|
96
|
+
name: "values",
|
|
97
|
+
values: [
|
|
98
|
+
{
|
|
99
|
+
displayName: "Binary Field Name",
|
|
100
|
+
name: "binaryPropertyName",
|
|
101
|
+
type: "string",
|
|
102
|
+
default: "data",
|
|
103
|
+
placeholder: "e.g. data",
|
|
104
|
+
description: "The name of the binary field containing the image data"
|
|
105
|
+
}
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
displayName: "Options",
|
|
112
|
+
name: "options",
|
|
113
|
+
placeholder: "Add Option",
|
|
114
|
+
type: "collection",
|
|
115
|
+
default: {},
|
|
116
|
+
options: [
|
|
117
|
+
{
|
|
118
|
+
displayName: "Put Output in Field",
|
|
119
|
+
name: "binaryPropertyOutput",
|
|
120
|
+
type: "string",
|
|
121
|
+
default: "edited",
|
|
122
|
+
hint: "The name of the output field to put the binary file data in"
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
];
|
|
127
|
+
const displayOptions = {
|
|
128
|
+
show: {
|
|
129
|
+
operation: ["edit"],
|
|
130
|
+
resource: ["image"]
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
const description = (0, import_n8n_workflow.updateDisplayOptions)(displayOptions, properties);
|
|
134
|
+
async function execute(i) {
|
|
135
|
+
const prompt = this.getNodeParameter("prompt", i, "");
|
|
136
|
+
const binaryPropertyOutput = this.getNodeParameter("options.binaryPropertyOutput", i, "edited");
|
|
137
|
+
const outputKey = typeof binaryPropertyOutput === "string" ? binaryPropertyOutput : "data";
|
|
138
|
+
const imagesParam = this.getNodeParameter("images", i, {
|
|
139
|
+
values: [{ binaryPropertyName: "data" }]
|
|
140
|
+
});
|
|
141
|
+
if (!isImagesParameter(imagesParam)) {
|
|
142
|
+
throw new Error("Invalid images parameter format");
|
|
143
|
+
}
|
|
144
|
+
const imagesUi = imagesParam.values ?? [];
|
|
145
|
+
const imageFieldNames = imagesUi.map((v) => v.binaryPropertyName).filter((n) => Boolean(n));
|
|
146
|
+
const fileParts = [];
|
|
147
|
+
for (const fieldName of imageFieldNames) {
|
|
148
|
+
const bin = this.helpers.assertBinaryData(i, fieldName);
|
|
149
|
+
const buf = await this.helpers.getBinaryDataBuffer(i, fieldName);
|
|
150
|
+
const uploaded = await import_utils.uploadFile.call(this, buf, bin.mimeType);
|
|
151
|
+
fileParts.push({ fileData: { fileUri: uploaded.fileUri, mimeType: uploaded.mimeType } });
|
|
152
|
+
}
|
|
153
|
+
const model = "models/gemini-2.5-flash-image-preview";
|
|
154
|
+
const generationConfig = {
|
|
155
|
+
responseModalities: ["IMAGE"]
|
|
156
|
+
};
|
|
157
|
+
const body = {
|
|
158
|
+
contents: [
|
|
159
|
+
{
|
|
160
|
+
role: "user",
|
|
161
|
+
parts: [...fileParts, { text: prompt }]
|
|
162
|
+
}
|
|
163
|
+
],
|
|
164
|
+
generationConfig
|
|
165
|
+
};
|
|
166
|
+
const response = await import_transport.apiRequest.call(
|
|
167
|
+
this,
|
|
168
|
+
"POST",
|
|
169
|
+
`/v1beta/${model}:generateContent`,
|
|
170
|
+
{
|
|
171
|
+
body
|
|
172
|
+
}
|
|
173
|
+
);
|
|
174
|
+
if (!isGenerateContentResponse(response)) {
|
|
175
|
+
throw new Error("Invalid response format from Gemini API");
|
|
176
|
+
}
|
|
177
|
+
const promises = response.candidates.map(async (candidate) => {
|
|
178
|
+
const imagePart = candidate.content.parts.find((part) => "inlineData" in part);
|
|
179
|
+
if (!imagePart?.inlineData?.data) {
|
|
180
|
+
throw new Error("No image data returned from Gemini API");
|
|
181
|
+
}
|
|
182
|
+
const bufferOut = Buffer.from(imagePart.inlineData.data, "base64");
|
|
183
|
+
const binaryOut = await this.helpers.prepareBinaryData(
|
|
184
|
+
bufferOut,
|
|
185
|
+
"image.png",
|
|
186
|
+
imagePart.inlineData.mimeType
|
|
187
|
+
);
|
|
188
|
+
return {
|
|
189
|
+
binary: {
|
|
190
|
+
[outputKey]: binaryOut
|
|
191
|
+
},
|
|
192
|
+
json: {
|
|
193
|
+
...binaryOut,
|
|
194
|
+
data: void 0
|
|
195
|
+
},
|
|
196
|
+
pairedItem: { item: i }
|
|
197
|
+
};
|
|
198
|
+
});
|
|
199
|
+
return await Promise.all(promises);
|
|
200
|
+
}
|
|
201
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
202
|
+
0 && (module.exports = {
|
|
203
|
+
description,
|
|
204
|
+
execute
|
|
205
|
+
});
|
|
206
|
+
//# sourceMappingURL=edit.operation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../nodes/vendors/GoogleGemini/actions/image/edit.operation.ts"],"sourcesContent":["import type { IExecuteFunctions, INodeExecutionData, INodeProperties } from 'n8n-workflow';\nimport { updateDisplayOptions } from 'n8n-workflow';\n\nimport type { GenerateContentResponse } from '../../helpers/interfaces';\nimport { uploadFile } from '../../helpers/utils';\nimport { apiRequest } from '../../transport';\n\ninterface ImagesParameter {\n\tvalues?: Array<{ binaryPropertyName?: string }>;\n}\n\nfunction isImagesParameter(param: unknown): param is ImagesParameter {\n\tif (typeof param !== 'object' || param === null) {\n\t\treturn false;\n\t}\n\n\tconst paramObj = param as Record<string, unknown>;\n\n\tif (!('values' in paramObj)) {\n\t\treturn true; // values is optional\n\t}\n\n\tif (!Array.isArray(paramObj.values)) {\n\t\treturn false;\n\t}\n\n\treturn paramObj.values.every((item: unknown) => {\n\t\tif (typeof item !== 'object' || item === null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst itemObj = item as Record<string, unknown>;\n\n\t\tif (!('binaryPropertyName' in itemObj)) {\n\t\t\treturn true; // binaryPropertyName is optional\n\t\t}\n\n\t\treturn (\n\t\t\ttypeof itemObj.binaryPropertyName === 'string' || itemObj.binaryPropertyName === undefined\n\t\t);\n\t});\n}\n\nfunction isGenerateContentResponse(response: unknown): response is GenerateContentResponse {\n\tif (typeof response !== 'object' || response === null) {\n\t\treturn false;\n\t}\n\n\tconst responseObj = response as Record<string, unknown>;\n\n\tif (!('candidates' in responseObj) || !Array.isArray(responseObj.candidates)) {\n\t\treturn false;\n\t}\n\n\treturn responseObj.candidates.every((candidate: unknown) => {\n\t\tif (typeof candidate !== 'object' || candidate === null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst candidateObj = candidate as Record<string, unknown>;\n\n\t\tif (\n\t\t\t!('content' in candidateObj) ||\n\t\t\ttypeof candidateObj.content !== 'object' ||\n\t\t\tcandidateObj.content === null\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst contentObj = candidateObj.content as Record<string, unknown>;\n\n\t\treturn 'parts' in contentObj && Array.isArray(contentObj.parts);\n\t});\n}\n\nconst properties: INodeProperties[] = [\n\t{\n\t\tdisplayName: 'Prompt',\n\t\tname: 'prompt',\n\t\ttype: 'string',\n\t\tplaceholder: 'e.g. combine the first image with the second image',\n\t\tdescription: 'Instruction describing how to edit the image',\n\t\tdefault: '',\n\t\ttypeOptions: {\n\t\t\trows: 2,\n\t\t},\n\t},\n\t{\n\t\tdisplayName: 'Images',\n\t\tname: 'images',\n\t\ttype: 'fixedCollection',\n\t\tplaceholder: 'Add Image',\n\t\ttypeOptions: {\n\t\t\tmultipleValues: true,\n\t\t\tmultipleValueButtonText: 'Add Image',\n\t\t},\n\t\tdefault: { values: [{ binaryPropertyName: 'data' }] },\n\t\tdescription: 'Add one or more binary fields to include images with your prompt',\n\t\toptions: [\n\t\t\t{\n\t\t\t\tdisplayName: 'Image',\n\t\t\t\tname: 'values',\n\t\t\t\tvalues: [\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Binary Field Name',\n\t\t\t\t\t\tname: 'binaryPropertyName',\n\t\t\t\t\t\ttype: 'string',\n\t\t\t\t\t\tdefault: 'data',\n\t\t\t\t\t\tplaceholder: 'e.g. data',\n\t\t\t\t\t\tdescription: 'The name of the binary field containing the image data',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t},\n\t{\n\t\tdisplayName: 'Options',\n\t\tname: 'options',\n\t\tplaceholder: 'Add Option',\n\t\ttype: 'collection',\n\t\tdefault: {},\n\t\toptions: [\n\t\t\t{\n\t\t\t\tdisplayName: 'Put Output in Field',\n\t\t\t\tname: 'binaryPropertyOutput',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: 'edited',\n\t\t\t\thint: 'The name of the output field to put the binary file data in',\n\t\t\t},\n\t\t],\n\t},\n];\n\nconst displayOptions = {\n\tshow: {\n\t\toperation: ['edit'],\n\t\tresource: ['image'],\n\t},\n};\n\nexport const description = updateDisplayOptions(displayOptions, properties);\n\nexport async function execute(this: IExecuteFunctions, i: number): Promise<INodeExecutionData[]> {\n\tconst prompt = this.getNodeParameter('prompt', i, '');\n\tconst binaryPropertyOutput = this.getNodeParameter('options.binaryPropertyOutput', i, 'edited');\n\tconst outputKey = typeof binaryPropertyOutput === 'string' ? binaryPropertyOutput : 'data';\n\n\t// Collect image binary field names from collection\n\tconst imagesParam = this.getNodeParameter('images', i, {\n\t\tvalues: [{ binaryPropertyName: 'data' }],\n\t});\n\n\tif (!isImagesParameter(imagesParam)) {\n\t\tthrow new Error('Invalid images parameter format');\n\t}\n\n\tconst imagesUi = imagesParam.values ?? [];\n\tconst imageFieldNames = imagesUi\n\t\t.map((v) => v.binaryPropertyName)\n\t\t.filter((n): n is string => Boolean(n));\n\n\t// Upload all images and gather fileData parts\n\tconst fileParts = [] as Array<{ fileData: { fileUri: string; mimeType: string } }>;\n\tfor (const fieldName of imageFieldNames) {\n\t\tconst bin = this.helpers.assertBinaryData(i, fieldName);\n\t\tconst buf = await this.helpers.getBinaryDataBuffer(i, fieldName);\n\t\tconst uploaded = await uploadFile.call(this, buf, bin.mimeType);\n\t\tfileParts.push({ fileData: { fileUri: uploaded.fileUri, mimeType: uploaded.mimeType } });\n\t}\n\n\tconst model = 'models/gemini-2.5-flash-image-preview';\n\tconst generationConfig = {\n\t\tresponseModalities: ['IMAGE'],\n\t};\n\n\tconst body = {\n\t\tcontents: [\n\t\t\t{\n\t\t\t\trole: 'user',\n\t\t\t\tparts: [...fileParts, { text: prompt }],\n\t\t\t},\n\t\t],\n\t\tgenerationConfig,\n\t};\n\n\tconst response: unknown = await apiRequest.call(\n\t\tthis,\n\t\t'POST',\n\t\t`/v1beta/${model}:generateContent`,\n\t\t{\n\t\t\tbody,\n\t\t},\n\t);\n\n\tif (!isGenerateContentResponse(response)) {\n\t\tthrow new Error('Invalid response format from Gemini API');\n\t}\n\n\tconst promises = response.candidates.map(async (candidate) => {\n\t\tconst imagePart = candidate.content.parts.find((part) => 'inlineData' in part);\n\n\t\t// Check if imagePart exists and has inlineData with actual data\n\t\tif (!imagePart?.inlineData?.data) {\n\t\t\tthrow new Error('No image data returned from Gemini API');\n\t\t}\n\n\t\tconst bufferOut = Buffer.from(imagePart.inlineData.data, 'base64');\n\t\tconst binaryOut = await this.helpers.prepareBinaryData(\n\t\t\tbufferOut,\n\t\t\t'image.png',\n\t\t\timagePart.inlineData.mimeType,\n\t\t);\n\t\treturn {\n\t\t\tbinary: {\n\t\t\t\t[outputKey]: binaryOut,\n\t\t\t},\n\t\t\tjson: {\n\t\t\t\t...binaryOut,\n\t\t\t\tdata: undefined,\n\t\t\t},\n\t\t\tpairedItem: { item: i },\n\t\t};\n\t});\n\n\treturn await Promise.all(promises);\n}\n"],"mappings":"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","names":[]}
|