Skip to content

Commit 4b263e9

Browse files
committed
LangChain/LangGraph llm event refactor
1 parent 87c3ca8 commit 4b263e9

35 files changed

+391
-554
lines changed

lib/llm-events-new/base.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ class LlmEvent {
3131
* @param {object} params.segment Current segment
3232
* @param {object} params.transaction Current and active transaction
3333
* @param {string} params.vendor Lowercase vendor name, e.g. "openai"
34-
* @param {string} params.responseModel Model name from response
35-
* @param {string} params.requestId ID from request/response headers
34+
* @param {string} [params.responseModel] Model name from response
35+
* @param {string} [params.requestId] ID from request/response headers
3636
* @param {boolean} [params.error] Set to `true` if an error occurred during creation call, omitted if no error occurred
3737
*/
3838
constructor({ agent, segment, transaction, vendor, responseModel, requestId, error }) {
@@ -41,6 +41,9 @@ class LlmEvent {
4141
this.trace_id = transaction?.traceId
4242
this.vendor = vendor
4343
this.metadata = agent
44+
// TODO: Does not appear in AIM spec, but was a
45+
// requirement for LangChain instrumentation back in 2024?
46+
// this.appName = agent.config.applications()[0]
4447

4548
// Omit `error` property if no error occurred
4649
if (error === true) {

lib/llm-events-new/chat-summary.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,13 @@ class LlmChatCompletionSummary extends LlmEvent {
4141
responseOrg, temperature, maxTokens, numMsgs, finishReason }) {
4242
super({ agent, segment, transaction, vendor, responseModel, requestId, error })
4343

44-
this['request.model'] = requestModel
45-
this['request.max_tokens'] = maxTokens
46-
this['request.temperature'] = temperature
47-
this['response.number_of_messages'] = numMsgs
44+
if (requestModel) this['request.model'] = requestModel
45+
if (maxTokens) this['request.max_tokens'] = maxTokens
46+
if (temperature) this['request.temperature'] = temperature
4847
if (finishReason) this['response.choices.finish_reason'] = finishReason
4948
if (responseOrg) this['response.organization'] = responseOrg
49+
50+
this['response.number_of_messages'] = numMsgs
5051
this.timestamp = segment.timer.start
5152
this.duration = segment.getDurationInMillis()
5253
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2026 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
'use strict'
7+
const LlmChatCompletionMessage = require('../chat-message')
8+
const { isSimpleObject } = require('../../util/objects')
9+
10+
/**
11+
* Encapsulates a LangChain LlmChatCompletionMessage.
12+
*/
13+
class LangChainLlmChatCompletionMessage extends LlmChatCompletionMessage {
14+
virtual_llm = true
15+
/**
16+
* @param {object} params constructor parameters
17+
* @param {Agent} params.agent New Relic agent instance
18+
* @param {object} params.segment Current segment
19+
* @param {object} params.transaction Current and active transaction
20+
* @param {string} params.runId LangChain run ID (will be used as response ID)
21+
* @param {number} params.sequence Index (beginning at 0) associated with
22+
* each message including the prompt and responses
23+
* @param {string} params.content Content of the message
24+
* @param {string} [params.role] Role of the message creator (e.g. `user`, `assistant`, `tool`)
25+
* @param {string} params.completionId ID of the `LlmChatCompletionSummary` event that
26+
* this message event is connected to
27+
* @param {boolean} [params.isResponse] `true` if a message is the result of a chat
28+
* completion and not an input message - omitted in `false` cases
29+
* @param {object} params.metadata LangChain metadata object
30+
* @param {object[]} params.tags LangChain tags
31+
*/
32+
constructor({ agent, segment, transaction, runId, sequence, role, content, completionId, isResponse, metadata, tags }) {
33+
super({ agent,
34+
segment,
35+
transaction,
36+
vendor: 'langchain',
37+
responseId: runId,
38+
requestId: runId,
39+
sequence,
40+
content,
41+
role,
42+
completionId,
43+
isResponse })
44+
45+
// TODO: Does not appear in AIM spec, but was a
46+
// requirement for LangChain instrumentation back in 2024?
47+
this.appName = agent.config.applications()[0]
48+
this.langchainMeta = metadata
49+
this.tags = Array.isArray(tags) ? tags.join(',') : tags
50+
}
51+
52+
// eslint-disable-next-line accessor-pairs
53+
set langchainMeta(value) {
54+
if (isSimpleObject(value) === false) {
55+
return
56+
}
57+
for (const [key, val] of Object.entries(value)) {
58+
this[`metadata.${key}`] = val
59+
}
60+
}
61+
}
62+
63+
module.exports = LangChainLlmChatCompletionMessage
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2026 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
'use strict'
7+
const LlmChatCompletionSummary = require('../chat-summary')
8+
const { isSimpleObject } = require('../../util/objects')
9+
10+
/**
11+
* Encapsulates a LangChain LlmChatCompletionSummary.
12+
*/
13+
class LangChainLlmChatCompletionSummary extends LlmChatCompletionSummary {
14+
virtual_llm = true
15+
constructor({ agent, segment, transaction, error, numMsgs = 0, runId, metadata = {}, tags = '' }) {
16+
super({ agent,
17+
segment,
18+
transaction,
19+
vendor: 'langchain',
20+
requestId: runId,
21+
error,
22+
numMsgs })
23+
24+
// TODO: Does not appear in AIM spec, but was a
25+
// requirement for LangChain instrumentation back in 2024?
26+
this.appName = agent.config.applications()[0]
27+
this.langchainMeta = metadata
28+
this.tags = Array.isArray(tags) ? tags.join(',') : tags
29+
}
30+
31+
// eslint-disable-next-line accessor-pairs
32+
set langchainMeta(value) {
33+
if (isSimpleObject(value) === false) {
34+
return
35+
}
36+
for (const [key, val] of Object.entries(value)) {
37+
this[`metadata.${key}`] = val
38+
}
39+
}
40+
}
41+
42+
module.exports = LangChainLlmChatCompletionSummary
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2026 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
'use strict'
7+
8+
const LlmChatCompletionMessage = require('./chat-message')
9+
const LlmChatCompletionSummary = require('./chat-summary')
10+
const LlmTool = require('./tool')
11+
const LlmVectorSearch = require('./vector-search')
12+
const LlmVectorSearchResult = require('./vector-search-result')
13+
14+
module.exports = {
15+
LlmChatCompletionMessage,
16+
LlmChatCompletionSummary,
17+
LlmTool,
18+
LlmVectorSearch,
19+
LlmVectorSearchResult
20+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2026 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
const LlmTool = require('../tool')
7+
const { isSimpleObject } = require('../../util/objects')
8+
9+
/**
10+
* Encapsulates a LangChain LlmTool event.
11+
*/
12+
class LangChainLlmTool extends LlmTool {
13+
constructor({ agent, segment, transaction, runId, input, output, toolName, aiAgentName, description, metadata = {}, tags = '', error }) {
14+
super({ agent, segment, transaction, vendor: 'langchain', runId, input, output, toolName, aiAgentName, error })
15+
16+
// TODO: Does not appear in AIM spec, but was a
17+
// requirement for LangChain instrumentation back in 2024?
18+
this.appName = agent.config.applications()[0]
19+
this.langchainMeta = metadata
20+
this.tags = Array.isArray(tags) ? tags.join(',') : tags
21+
this.description = description
22+
}
23+
24+
// eslint-disable-next-line accessor-pairs
25+
set langchainMeta(value) {
26+
if (isSimpleObject(value) === false) {
27+
return
28+
}
29+
for (const [key, val] of Object.entries(value)) {
30+
this[`metadata.${key}`] = val
31+
}
32+
}
33+
}
34+
35+
module.exports = LangChainLlmTool
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2026 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
const LlmEvent = require('../base')
7+
const { isSimpleObject } = require('../../util/objects')
8+
9+
/**
10+
* An event that captures data about a VectorStore `similaritySearch` call in LangChain.
11+
*
12+
* An instance of `LlmVectorSearchResult` represents a single document returned by
13+
* the similarity search.
14+
*/
15+
class LangChainLlmVectorSearchResult extends LlmEvent {
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.searchId UUID to identify the search
22+
* @param {number} params.sequence Index of the document in the search result documents list
23+
* @param {string} params.pageContent Stringified contents of the `pageContent` attribute on each returned search result document
24+
* @param {object} params.metadata The metadata object on each returned search result document
25+
*/
26+
constructor({ agent, segment, transaction, searchId, sequence = 0, pageContent = '', metadata = {} }) {
27+
super({ agent, segment, transaction, vendor: 'langchain' })
28+
29+
this.search_id = searchId
30+
this.sequence = sequence
31+
32+
if (agent.config.ai_monitoring.record_content.enabled === true) {
33+
this.page_content = pageContent
34+
}
35+
36+
// TODO: Does not appear in AIM spec, but was a
37+
// requirement for LangChain instrumentation back in 2024?
38+
this.appName = agent.config.applications()[0]
39+
this.langchainMeta = metadata
40+
}
41+
42+
// eslint-disable-next-line accessor-pairs
43+
set langchainMeta(value) {
44+
if (isSimpleObject(value) === false) {
45+
return
46+
}
47+
for (const [key, val] of Object.entries(value)) {
48+
this[`metadata.${key}`] = val
49+
}
50+
}
51+
}
52+
53+
module.exports = LangChainLlmVectorSearchResult
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2026 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
const LlmEvent = require('../base')
7+
8+
/**
9+
* An event that captures data about a VectorStore `similaritySearch` call in LangChain.
10+
*
11+
* An instance of `LlmVectorSearch` represents the entire search request.
12+
*/
13+
class LangChainLlmVectorSearch extends LlmEvent {
14+
/**
15+
* @param {object} params constructor parameters
16+
* @param {Agent} params.agent New Relic agent instance
17+
* @param {object} params.segment Current segment
18+
* @param {object} params.transaction Current and active transaction
19+
* @param {string} params.query Vector search query
20+
* @param {number} params.k Vector search top k
21+
* @param {number} params.numDocs Number of documents in returned response
22+
* @param {boolean} [params.error] Set to `true` if an error occurred during creation call, omitted if no error occurred
23+
*/
24+
constructor({ agent, segment, transaction, k, numDocs = 0, query, error }) {
25+
super({ agent, segment, transaction, vendor: 'langchain', error })
26+
this.duration = segment.getDurationInMillis()
27+
this['request.k'] = k
28+
this['response.number_of_documents'] = numDocs
29+
30+
if (agent.config.ai_monitoring.record_content.enabled === true) {
31+
this['request.query'] = query
32+
}
33+
34+
// TODO: Does not appear in AIM spec, but was a
35+
// requirement for LangChain instrumentation back in 2024?
36+
this.appName = agent.config.applications()[0]
37+
}
38+
}
39+
40+
module.exports = LangChainLlmVectorSearch
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2026 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
const LlmEvent = require('../base')
7+
8+
/**
9+
* Encapsulates a `@langchain/langgraph` LlmAgent event.
10+
*/
11+
class LangGraphLlmAgent extends LlmEvent {
12+
/**
13+
*
14+
* @param {object} params constructor parameters
15+
* @param {Agent} params.agent New Relic agent instance
16+
* @param {object} params.segment Current segment
17+
* @param {object} params.transaction Current and active transaction
18+
* @param {string} params.aiAgentName Name of the AI agent which can typically be captured through framework context
19+
* @param {boolean} [params.error] Set to `true` if an error occurred during creation call, omitted if no error occurred
20+
*/
21+
constructor({ agent, segment, transaction, aiAgentName, error }) {
22+
super({ agent, segment, transaction, vendor: 'langgraph', error })
23+
this.name = aiAgentName ?? 'agent'
24+
}
25+
}
26+
27+
module.exports = LangGraphLlmAgent

lib/llm-events-new/tool.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2026 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
const LlmEvent = require('./base')
7+
8+
/**
9+
* An event that captures data about a tool call made by an AI agent.
10+
*/
11+
class LlmTool extends LlmEvent {
12+
/**
13+
*
14+
* @param {object} params constructor parameters
15+
* @param {Agent} params.agent New Relic agent instance
16+
* @param {object} params.segment Current segment
17+
* @param {object} params.transaction Current and active transaction
18+
* @param {string} params.vendor Lowercase vendor name, e.g. "openai"
19+
* @param {string} params.input Argument(s) input to the tool before it is run (including the argument name and value if available)
20+
* @param {string} params.output Output data returned after the tool call has completed
21+
* @param {string} params.toolName Name of the tool being run
22+
* @param {string} params.aiAgentName Name of the AI agent associated with the tool call
23+
* @param {string} params.runId ID assigned by the framework to identify the tool call
24+
* @param {boolean} [params.error] Set to `true` if an error occurred during creation call, omitted if no error occurred
25+
*/
26+
constructor({ agent, segment, transaction, vendor, runId, input, output, toolName, aiAgentName, error }) {
27+
super({ agent, segment, transaction, vendor, error })
28+
// If user defined metadata is not present or available, it can be omitted from the event.
29+
// All other attributes listed below MUST be captured and added to the event as the AI
30+
// Monitoring UX depends on their presence.
31+
this.name = toolName
32+
this.agent_name = aiAgentName
33+
this.run_id = runId
34+
35+
if (agent.config.ai_monitoring.record_content.enabled === true) {
36+
this.input = input
37+
this.output = output
38+
}
39+
}
40+
}
41+
42+
module.exports = LlmTool

0 commit comments

Comments
 (0)