diff --git a/lib/llm-events/langchain/attach-attributes.js b/lib/llm-events/langchain/attach-attributes.js new file mode 100644 index 0000000000..4ed9a37cbf --- /dev/null +++ b/lib/llm-events/langchain/attach-attributes.js @@ -0,0 +1,41 @@ +/* + * Copyright 2026 New Relic Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +'use strict' + +module.exports = attachAttributes + +const { isSimpleObject } = require('#agentlib/util/objects.js') + +/** + * Applies common attributes to the various LangChain object types. We can't + * inline this to a common base class, because they all need to inherit from + * disparate base classes. + * + * @param {object} params Function parameters + * @param {object} params.target The LLM event object to decorate. + * @param {object} [params.agent] The agent instance. + * @param {object} [params.metadata] A set of key-value pairs to attach. + * @param {string|string[]} [params.tags] A comma separated list of tags, or an + * array of string tags. + */ +function attachAttributes({ target, agent = null, metadata = null, tags = null }) { + if (agent) { + target.appName = agent.config.applications()[0] + } + + if (isSimpleObject(metadata) === true) { + target.langchainMeta = metadata + for (const [k, v] of Object.entries(metadata)) { + target[`metadata.${k}`] = v + } + } + + if (Array.isArray(tags) === true) { + target.tags = tags.join(',') + } else if (typeof tags === 'string') { + target.tags = tags + } +} diff --git a/lib/llm-events/langchain/chat-completion-message.js b/lib/llm-events/langchain/chat-completion-message.js index d2715976cc..1de05a508e 100644 --- a/lib/llm-events/langchain/chat-completion-message.js +++ b/lib/llm-events/langchain/chat-completion-message.js @@ -5,13 +5,14 @@ 'use strict' const LlmChatCompletionMessage = require('../chat-completion-message') -const { isSimpleObject } = require('../../util/objects') +const attachAttributes = require('./attach-attributes') /** * Encapsulates a LangChain LlmChatCompletionMessage. */ class LangChainLlmChatCompletionMessage extends LlmChatCompletionMessage { virtual_llm = true + /** * @param {object} params constructor parameters * @param {Agent} params.agent New Relic agent instance @@ -42,20 +43,7 @@ class LangChainLlmChatCompletionMessage extends LlmChatCompletionMessage { completionId, isResponse }) - // Does not appear in AIM spec as of 2/2026, but seemed - // to be a requirement back in 1/2024 (e.g. LangChain CDD). - this.appName = agent.config.applications()[0] - - // `metadata.` and `tags` do not appear in - // the AIM spec, but were a requirement for the - // initial LangChain instrumentation. - if (isSimpleObject(metadata)) { - this.langchainMeta = metadata - for (const [key, val] of Object.entries(metadata)) { - this[`metadata.${key}`] = val - } - } - this.tags = Array.isArray(tags) ? tags.join(',') : tags + attachAttributes({ target: this, agent, metadata, tags }) } } diff --git a/lib/llm-events/langchain/chat-completion-summary.js b/lib/llm-events/langchain/chat-completion-summary.js index d60e8409f3..b36be1226a 100644 --- a/lib/llm-events/langchain/chat-completion-summary.js +++ b/lib/llm-events/langchain/chat-completion-summary.js @@ -4,14 +4,16 @@ */ 'use strict' + const LlmChatCompletionSummary = require('../chat-completion-summary') -const { isSimpleObject } = require('../../util/objects') +const attachAttributes = require('./attach-attributes') /** * Encapsulates a LangChain LlmChatCompletionSummary. */ class LangChainLlmChatCompletionSummary extends LlmChatCompletionSummary { virtual_llm = true + constructor({ agent, segment, transaction, error, numMsgs = 0, runId, metadata = {}, tags = '' }) { super({ agent, segment, @@ -21,20 +23,7 @@ class LangChainLlmChatCompletionSummary extends LlmChatCompletionSummary { error, numMsgs }) - // Does not appear in AIM spec as of 2/2026, but seemed - // to be a requirement back in 1/2024 (e.g. LangChain CDD). - this.appName = agent.config.applications()[0] - - // `metadata.` and `tags` do not appear in - // the AIM spec, but were a requirement for the - // initial LangChain instrumentation. - if (isSimpleObject(metadata)) { - this.langchainMeta = metadata - for (const [key, val] of Object.entries(metadata)) { - this[`metadata.${key}`] = val - } - } - this.tags = Array.isArray(tags) ? tags.join(',') : tags + attachAttributes({ target: this, agent, metadata, tags }) } } diff --git a/lib/llm-events/langchain/tool.js b/lib/llm-events/langchain/tool.js index 42577a5e19..d6e237cd43 100644 --- a/lib/llm-events/langchain/tool.js +++ b/lib/llm-events/langchain/tool.js @@ -6,7 +6,7 @@ 'use strict' const LlmTool = require('../tool') -const { isSimpleObject } = require('../../util/objects') +const attachAttributes = require('./attach-attributes') /** * Encapsulates a LangChain LlmTool event. @@ -35,12 +35,6 @@ module.exports = class LangChainLlmTool extends LlmTool { // appear in the AIM spec, but were a requirement for // the initial LangChain instrumentation. this.description = description - if (isSimpleObject(metadata)) { - this.langchainMeta = metadata - for (const [key, val] of Object.entries(metadata)) { - this[`metadata.${key}`] = val - } - } - this.tags = Array.isArray(tags) ? tags.join(',') : tags + attachAttributes({ target: this, metadata, tags }) } } diff --git a/lib/llm-events/langchain/vector-search-result.js b/lib/llm-events/langchain/vector-search-result.js index d06128aeb1..554f83927b 100644 --- a/lib/llm-events/langchain/vector-search-result.js +++ b/lib/llm-events/langchain/vector-search-result.js @@ -6,7 +6,7 @@ 'use strict' const LlmEvent = require('../event-base') -const { isSimpleObject } = require('../../util/objects') +const attachAttributes = require('./attach-attributes') /** * An event that captures data about a VectorStore `similaritySearch` call in LangChain. @@ -35,18 +35,6 @@ module.exports = class LangChainLlmVectorSearchResult extends LlmEvent { this.page_content = pageContent } - // Does not appear in AIM spec as of 2/2026, but seemed - // to be a requirement back in 1/2024 (e.g. LangChain CDD). - this.appName = agent.config.applications()[0] - - // `metadata.` and `tags` do not appear in - // the AIM spec, but were a requirement for the - // initial LangChain instrumentation. - if (isSimpleObject(metadata)) { - this.langchainMeta = metadata - for (const [key, val] of Object.entries(metadata)) { - this[`metadata.${key}`] = val - } - } + attachAttributes({ target: this, agent, metadata }) } } diff --git a/lib/llm-events/langchain/vector-search.js b/lib/llm-events/langchain/vector-search.js index 7b5aca5f4f..c7b2914979 100644 --- a/lib/llm-events/langchain/vector-search.js +++ b/lib/llm-events/langchain/vector-search.js @@ -6,6 +6,7 @@ 'use strict' const LlmEvent = require('../event-base') +const attachAttributes = require('./attach-attributes') /** * An event that captures data about a VectorStore `similaritySearch` call in LangChain. @@ -33,8 +34,6 @@ module.exports = class LangChainLlmVectorSearch extends LlmEvent { this['request.query'] = query } - // Does not appear in AIM spec as of 2/2026, but seemed - // to be a requirement back in 1/2024 (e.g. LangChain CDD). - this.appName = agent.config.applications()[0] + attachAttributes({ target: this, agent }) } }