|
1 | 1 | /* |
2 | | - * Copyright 2024 New Relic Corporation. All rights reserved. |
| 2 | + * Copyright 2026 New Relic Corporation. All rights reserved. |
3 | 3 | * SPDX-License-Identifier: Apache-2.0 |
4 | 4 | */ |
5 | 5 |
|
6 | 6 | 'use strict' |
| 7 | +const LlmChatCompletionMessage = require('../chat-completion-message') |
| 8 | +const { isSimpleObject } = require('../../util/objects') |
7 | 9 |
|
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 | | - */ |
20 | 10 | /** |
21 | | - * @type {LangChainCompletionMessageParams} |
| 11 | + * Encapsulates a LangChain LlmChatCompletionMessage. |
22 | 12 | */ |
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 |
| 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 {string[]|string} params.tags LangChain tags, can be an array of strings or a comma-seperated string |
| 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 | + // Does not appear in AIM spec as of 2/2026, but seemed |
| 46 | + // to be a requirement back in 1/2024 (e.g. LangChain CDD). |
| 47 | + this.appName = agent.config.applications()[0] |
| 48 | + |
| 49 | + // `metadata.<key>` and `tags` do not appear in |
| 50 | + // the AIM spec, but were a requirement for the |
| 51 | + // initial LangChain instrumentation. |
| 52 | + if (isSimpleObject(metadata)) { |
| 53 | + this.langchainMeta = metadata |
| 54 | + for (const [key, val] of Object.entries(metadata)) { |
| 55 | + this[`metadata.${key}`] = val |
| 56 | + } |
68 | 57 | } |
| 58 | + this.tags = Array.isArray(tags) ? tags.join(',') : tags |
69 | 59 | } |
70 | 60 | } |
71 | 61 |
|
72 | | -module.exports = LangChainCompletionMessage |
| 62 | +module.exports = LangChainLlmChatCompletionMessage |
0 commit comments