newrelic
Version:
New Relic agent
127 lines (114 loc) • 5.92 kB
JavaScript
/*
* Copyright 2026 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
const AiMonitoringSubscriber = require('./base')
const LlmErrorMessage = require('#agentlib/llm-events/error-message.js')
class AiMonitoringChatSubscriber extends AiMonitoringSubscriber {
/**
* @param {object} params constructor params object
* @param {object} params.agent A New Relic Node.js agent instance.
* @param {object} params.logger An agent logger instance.
* @param {string} params.packageName The package name being instrumented.
* This is what a developer would provide to the `require` function.
* @param {string} params.channelName A unique name for the diagnostics channel
* that will be created and monitored.
* @param {string} params.name name of segment for a given subscriber
* @param {string} params.trackingPrefix prefix for the tracking metric for a given subscriber
*/
constructor({ agent, logger, packageName, channelName, name, trackingPrefix }) {
super({ agent, logger, packageName, channelName, name, trackingPrefix })
}
/**
* Function that must be implemented by inherited subscriber to create an llm completion message.
*
* @param {object} params to function
* @param {Context} params.ctx active context
* @param {object} params.request request made to method on a given llm library
* @param {object} params.response response from method on a given llm library
* @param {object|string} params.message the message object/string used to create llm completion message
* @param {string} params.completionId the id of the llm completion summary for a given conversation
* @param {number} params.index index of message for a given conversation
* returns {object} a llm completion message instance for the given LLM
*/
createCompletionMessage({ ctx, request, response, message, completionId, index }) {
throw new Error('createCompletionMessage must be implemented by your subscriber')
}
/**
* Function that must be implemented by inherited subscriber to create the llm completion summary.
*
* @param {object} params to function
* @param {Context} params.ctx active context
* @param {object} params.request request made to method on a given llm library
* @param {object} params.response response from method on a given llm library
* @param {object} [params.err] error if present
* @param {number} [params.timeOfFirstToken] Timestamp of when the first streaming token was sent.
* @param {object} [params.metadata] used only for langchain events at the moment
* @param {Array} [params.tags] used only for langchain events at the moment
* returns {object} a llm completion summary instance for the given LLM
*/
createCompletionSummary({ ctx, request, response, err, timeOfFirstToken, metadata, tags }) {
throw new Error('createCompletionSummary must be implemented by your subscriber')
}
/**
* Function that must be implemented by inherited subscriber to retrieve the relevant
* messages for a given chat conversation.
*
* @param {object} params to function
* @param {object} params.request request to instrumented function
* @param {object} params.response response to instrumented function
* returns {Array} an array of relevant messages used to construct llm completion messages
*/
getMessages({ request, response }) {
throw new Error('getMessages must be implemented by your subscriber')
}
/**
* Function to create both llm completion summary and messages
* as well as assign errors to transaction if present
* @param {object} params to function
* @param {Context} params.ctx active context
* @param {object} params.request request made to method on a given llm library
* @param {object} params.response response from method on a given llm library
* @param {object} [params.err] error if present
* @param {number} [params.timeOfFirstToken] Timestamp of when the first streaming token was sent.
* @param {object} params.metadata used only for langchain events at the moment
* @param {Array} params.tags used only for langchain events at the moment
*/
recordChatCompletionEvents({ ctx, request, response, err, timeOfFirstToken, metadata = {}, tags = [] }) {
if (!this.aiEnabled) {
this.logger.debug('config.ai_monitoring.enabled is set to false, not creating chat completion events.')
return
}
if (!(ctx?.segment || ctx?.transaction)) {
this.logger.debug('Empty context, not creating completion events.')
return
}
// Explicitly end segment to provide consistent duration
// for both LLM events and the segment
ctx.segment.end()
const completionSummary = this.createCompletionSummary({ ctx, request, response, timeOfFirstToken, err, metadata, tags })
this.recordEvent({ type: 'LlmChatCompletionSummary', msg: completionSummary })
const messages = this.getMessages({ request, response })
for (let i = 0; i < messages.length; i++) {
const message = messages[i]
const completionMessage = this.createCompletionMessage({ ctx, completionId: completionSummary.id, index: i, message, request, response })
this.recordEvent({ type: 'LlmChatCompletionMessage', msg: completionMessage })
}
if (err) {
const llmError = this.createErrorMessage({ cause: err, summary: completionSummary, response })
this.agent.errors.add(ctx.transaction, err, llmError)
}
}
/**
* Creates an LLM error message instance. Override in vendor-specific
* subscribers to return vendor-specific error message subclasses.
*
* @param {object} params Constructor parameters for LlmErrorMessage
* @returns {LlmErrorMessage} an LLM error message instance
*/
createErrorMessage(params) {
return new LlmErrorMessage(params)
}
}
module.exports = AiMonitoringChatSubscriber