Skip to content

Commit b2612ec

Browse files
refactor: LangChain LLM event refactor (newrelic#3745)
Co-authored-by: James Sumners <jsumners@newrelic.com>
1 parent 4a7cafc commit b2612ec

23 files changed

+284
-438
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2026 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
'use strict'
7+
8+
module.exports = attachAttributes
9+
10+
const { isSimpleObject } = require('#agentlib/util/objects.js')
11+
12+
/**
13+
* Applies common attributes to the various LangChain object types. We can't
14+
* inline this to a common base class, because they all need to inherit from
15+
* disparate base classes.
16+
*
17+
* @param {object} params Function parameters
18+
* @param {object} params.target The LLM event object to decorate.
19+
* @param {object} [params.agent] The agent instance.
20+
* @param {object} [params.metadata] A set of key-value pairs to attach.
21+
* @param {string|string[]} [params.tags] A comma separated list of tags, or an
22+
* array of string tags.
23+
*/
24+
function attachAttributes({ target, agent = null, metadata = null, tags = null }) {
25+
if (agent) {
26+
target.appName = agent.config.applications()[0]
27+
}
28+
29+
if (isSimpleObject(metadata) === true) {
30+
target.langchainMeta = metadata
31+
for (const [k, v] of Object.entries(metadata)) {
32+
target[`metadata.${k}`] = v
33+
}
34+
}
35+
36+
if (Array.isArray(tags) === true) {
37+
target.tags = tags.join(',')
38+
} else if (typeof tags === 'string') {
39+
target.tags = tags
40+
}
41+
}
Lines changed: 39 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,50 @@
11
/*
2-
* Copyright 2024 New Relic Corporation. All rights reserved.
2+
* Copyright 2026 New Relic Corporation. All rights reserved.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

66
'use strict'
7+
const LlmChatCompletionMessage = require('../chat-completion-message')
8+
const attachAttributes = require('./attach-attributes')
79

8-
const LangChainEvent = require('./event')
9-
const { makeId } = require('../../util/hashes')
10-
11-
/**
12-
* @typedef {object} LangChainCompletionMessageParams
13-
* @augments LangChainEventParams
14-
* @property {string} content The text of the response received from LangChain.
15-
* @property {number} [sequence=0] The order of the message in the response.
16-
* @property {string} [completionId] An identifier for the message.
17-
* @property {boolean|undefined} [isResponse] Indicates if the completion
18-
* message represents a response from the remote service.
19-
*/
2010
/**
21-
* @type {LangChainCompletionMessageParams}
11+
* Encapsulates a LangChain LlmChatCompletionMessage.
2212
*/
23-
const defaultParams = {
24-
content: '',
25-
role: null,
26-
sequence: 0,
27-
completionId: makeId(36),
28-
isResponse: undefined
29-
}
30-
31-
class LangChainCompletionMessage extends LangChainEvent {
32-
content
33-
role
34-
sequence
35-
completion_id
36-
is_response
37-
38-
constructor(params = defaultParams) {
39-
params = Object.assign({}, defaultParams, params)
40-
super(params)
41-
const { agent, runId, segment, sequence, role, content, completionId, isResponse } = params
42-
43-
if (runId) {
44-
this.id = `${runId}-${sequence}`
45-
} else {
46-
this.id = `${this.id}-${sequence}`
47-
}
48-
49-
this.sequence = sequence
50-
this.completion_id = completionId
51-
52-
this.is_response = isResponse ?? false
53-
if (this.is_response === false) {
54-
// `timestamp` is only required for input/request messages.
55-
this.timestamp = segment.timer.start
56-
}
57-
58-
if (role) {
59-
this.role = role
60-
} else {
61-
// As a backup, we can infer the role based on if it
62-
// is a response or not.
63-
this.role = this.is_response ? 'assistant' : 'user'
64-
}
65-
66-
if (agent.config.ai_monitoring.record_content.enabled === true) {
67-
this.content = content
68-
}
13+
class LangChainLlmChatCompletionMessage extends LlmChatCompletionMessage {
14+
virtual_llm = true
15+
16+
/**
17+
* @param {object} params constructor parameters
18+
* @param {Agent} params.agent New Relic agent instance
19+
* @param {object} params.segment Current segment
20+
* @param {object} params.transaction Current and active transaction
21+
* @param {string} params.runId LangChain run ID (will be used as response ID)
22+
* @param {number} params.sequence Index (beginning at 0) associated with
23+
* each message including the prompt and responses
24+
* @param {string} params.content Content of the message
25+
* @param {string} [params.role] Role of the message creator (e.g. `user`, `assistant`, `tool`)
26+
* @param {string} params.completionId ID of the `LlmChatCompletionSummary` event that
27+
* this message event is connected to
28+
* @param {boolean} [params.isResponse] `true` if a message is the result of a chat
29+
* completion and not an input message - omitted in `false` cases
30+
* @param {object} params.metadata LangChain metadata object
31+
* @param {string[]|string} params.tags LangChain tags, can be an array of strings or a comma-seperated string
32+
*/
33+
constructor({ agent, segment, transaction, runId, sequence, role, content, completionId, isResponse, metadata = {}, tags = '' }) {
34+
super({ agent,
35+
segment,
36+
transaction,
37+
vendor: 'langchain',
38+
responseId: runId,
39+
requestId: runId,
40+
sequence,
41+
content,
42+
role,
43+
completionId,
44+
isResponse })
45+
46+
attachAttributes({ target: this, agent, metadata, tags })
6947
}
7048
}
7149

72-
module.exports = LangChainCompletionMessage
50+
module.exports = LangChainLlmChatCompletionMessage
Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,30 @@
11
/*
2-
* Copyright 2024 New Relic Corporation. All rights reserved.
2+
* Copyright 2026 New Relic Corporation. All rights reserved.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

66
'use strict'
77

8-
const LangChainEvent = require('./event')
8+
const LlmChatCompletionSummary = require('../chat-completion-summary')
9+
const attachAttributes = require('./attach-attributes')
910

1011
/**
11-
* @typedef {object} LangChainCompletionSummaryParams
12-
* @augments LangChainEventParams
13-
* @property {string[]|string} [tags] A set of tags applied to the LangChain
14-
* event. If provided as a simple string, it should be a comma separated value
15-
* string.
16-
* @property {object[]} messages The set of messages that were returned as the
17-
* LangChain result.
12+
* Encapsulates a LangChain LlmChatCompletionSummary.
1813
*/
19-
/**
20-
* @type {LangChainCompletionSummaryParams}
21-
*/
22-
const defaultParams = {
23-
tags: [],
24-
messages: []
25-
}
26-
27-
class LangChainCompletionSummary extends LangChainEvent {
28-
duration
29-
'response.number_of_messages' = 0
14+
class LangChainLlmChatCompletionSummary extends LlmChatCompletionSummary {
15+
virtual_llm = true
3016

31-
constructor(params = defaultParams) {
32-
params = Object.assign({}, defaultParams, params)
33-
super(params)
34-
const { segment } = params
17+
constructor({ agent, segment, transaction, error, numMsgs = 0, runId, metadata = {}, tags = '' }) {
18+
super({ agent,
19+
segment,
20+
transaction,
21+
vendor: 'langchain',
22+
requestId: runId,
23+
error,
24+
numMsgs })
3525

36-
this.duration = segment.getDurationInMillis()
37-
this['response.number_of_messages'] = params.messages?.length
38-
this.timestamp = segment.timer.start
26+
attachAttributes({ target: this, agent, metadata, tags })
3927
}
4028
}
4129

42-
module.exports = LangChainCompletionSummary
30+
module.exports = LangChainLlmChatCompletionSummary

lib/llm-events/langchain/event.js

Lines changed: 0 additions & 85 deletions
This file was deleted.

lib/llm-events/langchain/index.js

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
/*
2-
* Copyright 2024 New Relic Corporation. All rights reserved.
2+
* Copyright 2026 New Relic Corporation. All rights reserved.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
'use strict'
7-
86
module.exports = {
9-
LangChainEvent: require('./event'),
10-
LangChainCompletionMessage: require('./chat-completion-message'),
11-
LangChainCompletionSummary: require('./chat-completion-summary'),
12-
LangChainVectorSearch: require('./vector-search'),
13-
LangChainVectorSearchResult: require('./vector-search-result'),
14-
LangChainTool: require('./tool')
7+
LlmChatCompletionMessage: require('./chat-completion-message'),
8+
LlmChatCompletionSummary: require('./chat-completion-summary'),
9+
LlmTool: require('./tool'),
10+
LlmVectorSearch: require('./vector-search'),
11+
LlmVectorSearchResult: require('./vector-search-result')
1512
}

lib/llm-events/langchain/tool.js

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,40 @@
11
/*
2-
* Copyright 2024 New Relic Corporation. All rights reserved.
2+
* Copyright 2026 New Relic Corporation. All rights reserved.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

66
'use strict'
7-
const LangChainEvent = require('./event')
87

9-
class LangChainTool extends LangChainEvent {
10-
constructor(params) {
11-
super(params)
12-
const { agent } = params
8+
const LlmTool = require('../tool')
9+
const attachAttributes = require('./attach-attributes')
1310

14-
this.name = params.name
15-
this.description = params.description
16-
this.duration = params?.segment?.getDurationInMillis()
17-
this.run_id = this.request_id
18-
delete this.request_id
19-
delete this.virtual_llm
20-
delete this.conversation_id
11+
/**
12+
* Encapsulates a LangChain LlmTool event.
13+
*/
14+
module.exports = class LangChainLlmTool extends LlmTool {
15+
/**
16+
*
17+
* @param {object} params constructor parameters
18+
* @param {Agent} params.agent New Relic agent instance
19+
* @param {object} params.segment Current segment
20+
* @param {object} params.transaction Current and active transaction
21+
* @param {string} params.input Argument(s) input to the tool before it is run (including the argument name and value if available)
22+
* @param {string} params.output Output data returned after the tool call has completed
23+
* @param {string} params.toolName Name of the tool being run
24+
* @param {string} params.aiAgentName Name of the AI agent associated with the tool call
25+
* @param {string} params.runId ID assigned by the framework to identify the tool call
26+
* @param {string} params.description Description of the tool used
27+
* @param {object} [params.metadata] LangChain metadata object
28+
* @param {string[]|string} [params.tags] LangChain tags, can be an array of strings or a comma-seperated string
29+
* @param {boolean} [params.error] Set to `true` if an error occurred during creation call, omitted if no error occurred
30+
*/
31+
constructor({ agent, segment, transaction, runId, input, output, toolName, aiAgentName, description, metadata = {}, tags = '', error }) {
32+
super({ agent, segment, transaction, vendor: 'langchain', runId, input, output, toolName, aiAgentName, error })
2133

22-
if (agent.config.ai_monitoring.record_content.enabled === true) {
23-
this.input = params.input
24-
this.output = params.output
25-
}
34+
// `metadata.<key>`, `tags`, and `description` do not
35+
// appear in the AIM spec, but were a requirement for
36+
// the initial LangChain instrumentation.
37+
this.description = description
38+
attachAttributes({ target: this, metadata, tags })
2639
}
2740
}
28-
29-
module.exports = LangChainTool

0 commit comments

Comments
 (0)