newrelic 11.11.0 → 11.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NEWS.md +36 -0
- package/THIRD_PARTY_NOTICES.md +2 -2
- package/api.js +34 -1
- package/lib/config/default.js +16 -3
- package/lib/feature_flags.js +3 -3
- package/lib/instrumentation/langchain/common.js +1 -3
- package/lib/instrumentation/langchain/nr-hooks.js +6 -0
- package/lib/instrumentation/langchain/runnable.js +190 -38
- package/lib/instrumentation/langchain/tools.js +1 -1
- package/lib/instrumentation/langchain/vectorstore.js +108 -0
- package/lib/instrumentation/restify.js +1 -1
- package/lib/instrumentations.js +3 -1
- package/lib/llm-events/error-message.js +6 -1
- package/lib/llm-events/langchain/chat-completion-message.js +5 -1
- package/lib/llm-events/langchain/event.js +1 -2
- package/lib/llm-events/langchain/tool.js +7 -2
- package/lib/llm-events/langchain/vector-search-result.js +7 -4
- package/lib/llm-events/langchain/vector-search.js +5 -2
- package/lib/llm-events/openai/chat-completion-message.js +16 -2
- package/lib/llm-events/openai/embedding.js +8 -1
- package/lib/llm-events/openai/event.js +1 -1
- package/lib/metrics/names.js +4 -2
- package/lib/shim/shim.js +15 -0
- package/package.json +3 -3
package/NEWS.md
CHANGED
|
@@ -1,3 +1,39 @@
|
|
|
1
|
+
### v11.13.0 (2024-03-13)
|
|
2
|
+
|
|
3
|
+
#### Features
|
|
4
|
+
|
|
5
|
+
* Added AI Monitoring(AIM) for OpenAI, Amazon Bedrock, and Langchain.
|
|
6
|
+
* To enable set `config.ai_monitoring.enabled` or `NEW_RELIC_AI_MONITORING_ENABLED` to `true`.
|
|
7
|
+
* It is recommended for a full experience to set `config.span_events.max_samples_stored` to `10000`, `config.custom_insights_events.max_samples_stored` to `100000`, and `config.distributed_tracing.enabled` should not be set to false.
|
|
8
|
+
* **Note**: Enabling [high-security mode](https://docs.newrelic.com/docs/apm/agents/manage-apm-agents/configuration/high-security-mode/) will disable the AIM instrumentation and events.
|
|
9
|
+
* Added instrumentation for VectorStore.similaritySearch for langchain.js ([#2049](https://github.com/newrelic/node-newrelic/pull/2049)) ([3c50606](https://github.com/newrelic/node-newrelic/commit/3c50606a57a8dc18c91fe36e3b180bafd15a4cbc))
|
|
10
|
+
* Added setLlmTokenCountCallback API endpoint to register a callback for calculating token count when none is provided ([#2065](https://github.com/newrelic/node-newrelic/pull/2065)) ([d2faf1a](https://github.com/newrelic/node-newrelic/commit/d2faf1a4942aacefd8b92e0bc2dc11bd8b687328))
|
|
11
|
+
* Added token_count to LlmEmbedding and LlmChatCompletionMessage for openai ([#2061](https://github.com/newrelic/node-newrelic/pull/2061)) ([47a925e](https://github.com/newrelic/node-newrelic/commit/47a925eccce8e5b5d682b46277bbef5191f8b553))
|
|
12
|
+
* Added tool_id to langchain error event ([#2064](https://github.com/newrelic/node-newrelic/pull/2064)) ([793abe8](https://github.com/newrelic/node-newrelic/commit/793abe8c9854b80fb1cf42f839a7db6a646cdc0b))
|
|
13
|
+
|
|
14
|
+
#### Miscellaneous chores
|
|
15
|
+
|
|
16
|
+
* Added flag for verbosity during docs publishing ([#2060](https://github.com/newrelic/node-newrelic/pull/2060)) ([fe0d41a](https://github.com/newrelic/node-newrelic/commit/fe0d41a86cba8da6173f5935e61c62b7dca75344))
|
|
17
|
+
* Added some delays to docs publishing ([#2062](https://github.com/newrelic/node-newrelic/pull/2062)) ([a602fd4](https://github.com/newrelic/node-newrelic/commit/a602fd48e929437e4bc933c4ce276989013c53f1))
|
|
18
|
+
* Changed casing of OpenAI in vendor property ([#2068](https://github.com/newrelic/node-newrelic/pull/2068)) ([64b4ca2](https://github.com/newrelic/node-newrelic/commit/64b4ca2750c9de8d9028fef515c15019e358a5d9))
|
|
19
|
+
* Changed token_count to only use tokenCountCallback ([#2070](https://github.com/newrelic/node-newrelic/pull/2070)) ([66f94b0](https://github.com/newrelic/node-newrelic/commit/66f94b05aa7018af6fa1db1d2d87de9013f23a5b))
|
|
20
|
+
* Removed langchain_instrumentation feature flag as the instrumentation is feature complete ([#2073](https://github.com/newrelic/node-newrelic/pull/2073)) ([925a463](https://github.com/newrelic/node-newrelic/commit/925a463c708b29c78567aecf78ac89ac226b7995))
|
|
21
|
+
* updated @newrelic/aws-sdk and @newrelic/koa to latest ([#2074](https://github.com/newrelic/node-newrelic/pull/2074)) ([0747ca4](https://github.com/newrelic/node-newrelic/commit/0747ca4f0f6615e1fbfcc734a36059960facf23e))
|
|
22
|
+
|
|
23
|
+
### v11.12.0 (2024-03-04)
|
|
24
|
+
|
|
25
|
+
#### Features
|
|
26
|
+
|
|
27
|
+
* Added instrumentation for `chain.stream` for langchain js. ([#2052](https://github.com/newrelic/node-newrelic/pull/2052)) ([03abfce](https://github.com/newrelic/node-newrelic/commit/03abfce666d3c48abd0994c44817bb5cbe8655a9))
|
|
28
|
+
|
|
29
|
+
#### Miscellaneous chores
|
|
30
|
+
|
|
31
|
+
* Added @azure/openai and @langchain/community/llms/bedrock as tracking packages so we can measure usage with angler ([#2053](https://github.com/newrelic/node-newrelic/pull/2053)) ([4830ea3](https://github.com/newrelic/node-newrelic/commit/4830ea3285726f8cc8fb60820c4cf2d3790e5250))
|
|
32
|
+
|
|
33
|
+
#### Tests
|
|
34
|
+
|
|
35
|
+
* Updated aws-sdk branch to main as the necessary code was merged. ([#2051](https://github.com/newrelic/node-newrelic/pull/2051)) ([1e52a5c](https://github.com/newrelic/node-newrelic/commit/1e52a5cd7a8acc8970dbdc6b1bf774c06aa6f5fb))
|
|
36
|
+
|
|
1
37
|
### v11.11.0 (2024-02-27)
|
|
2
38
|
|
|
3
39
|
#### Features
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -504,7 +504,7 @@ This product includes source derived from [@grpc/proto-loader](https://github.co
|
|
|
504
504
|
|
|
505
505
|
### @newrelic/aws-sdk
|
|
506
506
|
|
|
507
|
-
This product includes source derived from [@newrelic/aws-sdk](https://github.com/newrelic/node-newrelic-aws-sdk) ([v7.
|
|
507
|
+
This product includes source derived from [@newrelic/aws-sdk](https://github.com/newrelic/node-newrelic-aws-sdk) ([v7.3.0](https://github.com/newrelic/node-newrelic-aws-sdk/tree/v7.3.0)), distributed under the [Apache-2.0 License](https://github.com/newrelic/node-newrelic-aws-sdk/blob/v7.3.0/LICENSE):
|
|
508
508
|
|
|
509
509
|
```
|
|
510
510
|
Apache License
|
|
@@ -712,7 +712,7 @@ This product includes source derived from [@newrelic/aws-sdk](https://github.com
|
|
|
712
712
|
|
|
713
713
|
### @newrelic/koa
|
|
714
714
|
|
|
715
|
-
This product includes source derived from [@newrelic/koa](https://github.com/newrelic/node-newrelic-koa) ([
|
|
715
|
+
This product includes source derived from [@newrelic/koa](https://github.com/newrelic/node-newrelic-koa) ([v9.0.0](https://github.com/newrelic/node-newrelic-koa/tree/v9.0.0)), distributed under the [Apache-2.0 License](https://github.com/newrelic/node-newrelic-koa/blob/v9.0.0/LICENSE):
|
|
716
716
|
|
|
717
717
|
```
|
|
718
718
|
Apache License
|
package/api.js
CHANGED
|
@@ -1839,7 +1839,7 @@ API.prototype.setErrorGroupCallback = function setErrorGroupCallback(callback) {
|
|
|
1839
1839
|
)
|
|
1840
1840
|
metric.incrementCallCount()
|
|
1841
1841
|
|
|
1842
|
-
if (!this.shim.isFunction(callback) || this.shim.
|
|
1842
|
+
if (!this.shim.isFunction(callback) || this.shim.isAsyncFunction(callback)) {
|
|
1843
1843
|
logger.warn(
|
|
1844
1844
|
'Error Group callback must be a synchronous function, Error Group attribute will not be added'
|
|
1845
1845
|
)
|
|
@@ -1849,4 +1849,37 @@ API.prototype.setErrorGroupCallback = function setErrorGroupCallback(callback) {
|
|
|
1849
1849
|
this.agent.errors.errorGroupCallback = callback
|
|
1850
1850
|
}
|
|
1851
1851
|
|
|
1852
|
+
/**
|
|
1853
|
+
* Registers a callback which will be used for calculating token counts on Llm events when they are not
|
|
1854
|
+
* available. This function will typically only be used if `ai_monitoring.record_content.enabled` is false
|
|
1855
|
+
* and you want to still capture token counts for Llm events.
|
|
1856
|
+
*
|
|
1857
|
+
* Provided callbacks must return an integer value for the token count for a given piece of content.
|
|
1858
|
+
*
|
|
1859
|
+
* @param {Function} callback - synchronous function called to calculate token count for content.
|
|
1860
|
+
* @example
|
|
1861
|
+
* // @param {string} model - name of model (i.e. gpt-3.5-turbo)
|
|
1862
|
+
* // @param {string} content - prompt or completion response
|
|
1863
|
+
* function tokenCallback(model, content) {
|
|
1864
|
+
* // calculate tokens based on model and content
|
|
1865
|
+
* // return token count
|
|
1866
|
+
* return 40
|
|
1867
|
+
* }
|
|
1868
|
+
*/
|
|
1869
|
+
API.prototype.setLlmTokenCountCallback = function setLlmTokenCountCallback(callback) {
|
|
1870
|
+
const metric = this.agent.metrics.getOrCreateMetric(
|
|
1871
|
+
NAMES.SUPPORTABILITY.API + '/setLlmTokenCountCallback'
|
|
1872
|
+
)
|
|
1873
|
+
metric.incrementCallCount()
|
|
1874
|
+
|
|
1875
|
+
if (!this.shim.isFunction(callback) || this.shim.isAsyncFunction(callback)) {
|
|
1876
|
+
logger.warn(
|
|
1877
|
+
'Llm token count callback must be a synchronous function, callback will not be registered.'
|
|
1878
|
+
)
|
|
1879
|
+
return
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
this.agent.llm.tokenCountCallback = callback
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1852
1885
|
module.exports = API
|
package/lib/config/default.js
CHANGED
|
@@ -1359,6 +1359,19 @@ defaultConfig.definition = () => ({
|
|
|
1359
1359
|
formatter: boolean,
|
|
1360
1360
|
default: false
|
|
1361
1361
|
},
|
|
1362
|
+
|
|
1363
|
+
/**
|
|
1364
|
+
* When enabled, the content of LLM messages will be included in the
|
|
1365
|
+
* recorded spans (i.e. delivered to the New Relic collector). This is
|
|
1366
|
+
* enabled by default.
|
|
1367
|
+
*/
|
|
1368
|
+
record_content: {
|
|
1369
|
+
enabled: {
|
|
1370
|
+
formatter: boolean,
|
|
1371
|
+
default: true
|
|
1372
|
+
}
|
|
1373
|
+
},
|
|
1374
|
+
|
|
1362
1375
|
/**
|
|
1363
1376
|
* Toggles the capturing of Llm events when using streaming
|
|
1364
1377
|
* based methods in AIM supported libraries(i.e.- openai, AWS bedrock, langchain)
|
|
@@ -1390,8 +1403,8 @@ defaultConfig.config = () => {
|
|
|
1390
1403
|
*
|
|
1391
1404
|
* @param {object} definition configuration definition for a given leaf node
|
|
1392
1405
|
* @param {object} config object that is used to construct the agent config
|
|
1393
|
-
* @param {Array} [paths
|
|
1394
|
-
* @param {int} [objectKeys
|
|
1406
|
+
* @param {Array} [paths] an array to capture the leaf node keys to properly assign defaults for nested objects
|
|
1407
|
+
* @param {int} [objectKeys] amount of keys in a given leaf node
|
|
1395
1408
|
*/
|
|
1396
1409
|
function buildConfig(definition, config, paths = [], objectKeys = 1) {
|
|
1397
1410
|
let keysSeen = 0
|
|
@@ -1429,7 +1442,7 @@ function buildConfig(definition, config, paths = [], objectKeys = 1) {
|
|
|
1429
1442
|
* @param {object} params.config object that is used to construct the agent config
|
|
1430
1443
|
* @param {string} params.key name of the configuration option
|
|
1431
1444
|
* @param {string} params.value value the configuration option
|
|
1432
|
-
* @param {Array} [params.paths
|
|
1445
|
+
* @param {Array} [params.paths] an array to capture the leaf node keys to properly assign defaults for nested objects
|
|
1433
1446
|
*/
|
|
1434
1447
|
function assignConfigValue({ config, key, value, paths }) {
|
|
1435
1448
|
if (paths.length) {
|
package/lib/feature_flags.js
CHANGED
|
@@ -12,8 +12,7 @@ exports.prerelease = {
|
|
|
12
12
|
reverse_naming_rules: false,
|
|
13
13
|
undici_async_tracking: true,
|
|
14
14
|
unresolved_promise_cleanup: true,
|
|
15
|
-
legacy_context_manager: false
|
|
16
|
-
langchain_instrumentation: false
|
|
15
|
+
legacy_context_manager: false
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
// flags that are no longer used for released features
|
|
@@ -35,7 +34,8 @@ exports.released = [
|
|
|
35
34
|
'certificate_bundle',
|
|
36
35
|
'async_local_context',
|
|
37
36
|
'undici_instrumentation',
|
|
38
|
-
'aws_bedrock_instrumentation'
|
|
37
|
+
'aws_bedrock_instrumentation',
|
|
38
|
+
'langchain_instrumentation'
|
|
39
39
|
]
|
|
40
40
|
|
|
41
41
|
// flags that are no longer used for unreleased features
|
|
@@ -59,7 +59,5 @@ common.recordEvent = function recordEvent({ agent, type, msg, pkgVersion }) {
|
|
|
59
59
|
* @returns {boolean} flag if we should skip instrumentation
|
|
60
60
|
*/
|
|
61
61
|
common.shouldSkipInstrumentation = function shouldSkipInstrumentation(config) {
|
|
62
|
-
return
|
|
63
|
-
config.ai_monitoring.enabled === true && config.feature_flag.langchain_instrumentation === true
|
|
64
|
-
)
|
|
62
|
+
return config.ai_monitoring.enabled !== true
|
|
65
63
|
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
const toolsInstrumentation = require('./tools')
|
|
8
8
|
const cbManagerInstrumentation = require('./callback-manager')
|
|
9
9
|
const runnableInstrumentation = require('./runnable')
|
|
10
|
+
const vectorstoreInstrumentation = require('./vectorstore')
|
|
10
11
|
|
|
11
12
|
module.exports = [
|
|
12
13
|
{
|
|
@@ -23,5 +24,10 @@ module.exports = [
|
|
|
23
24
|
type: 'generic',
|
|
24
25
|
moduleName: '@langchain/core/dist/runnables/base',
|
|
25
26
|
onRequire: runnableInstrumentation
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
type: 'generic',
|
|
30
|
+
moduleName: '@langchain/core/vectorstores',
|
|
31
|
+
onRequire: vectorstoreInstrumentation
|
|
26
32
|
}
|
|
27
33
|
]
|
|
@@ -6,9 +6,8 @@
|
|
|
6
6
|
'use strict'
|
|
7
7
|
|
|
8
8
|
const common = require('./common')
|
|
9
|
-
const {
|
|
10
|
-
|
|
11
|
-
} = require('../../metrics/names')
|
|
9
|
+
const { AI } = require('../../metrics/names')
|
|
10
|
+
const { LANGCHAIN } = AI
|
|
12
11
|
const {
|
|
13
12
|
LangChainCompletionMessage,
|
|
14
13
|
LangChainCompletionSummary
|
|
@@ -28,10 +27,33 @@ module.exports = function initialize(shim, langchain) {
|
|
|
28
27
|
return
|
|
29
28
|
}
|
|
30
29
|
|
|
30
|
+
instrumentInvokeChain({ langchain, shim })
|
|
31
|
+
|
|
32
|
+
if (agent.config.ai_monitoring.streaming.enabled) {
|
|
33
|
+
instrumentStream({ langchain, shim })
|
|
34
|
+
} else {
|
|
35
|
+
shim.logger.warn(
|
|
36
|
+
'`ai_monitoring.streaming.enabled` is set to `false`, stream will not be instrumented.'
|
|
37
|
+
)
|
|
38
|
+
agent.metrics.getOrCreateMetric(AI.STREAMING_DISABLED).incrementCallCount()
|
|
39
|
+
agent.metrics
|
|
40
|
+
.getOrCreateMetric(`${LANGCHAIN.TRACKING_PREFIX}/${pkgVersion}`)
|
|
41
|
+
.incrementCallCount()
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Instruments and records span and relevant LLM events for `chain.invoke`
|
|
47
|
+
*
|
|
48
|
+
* @param {object} params function params
|
|
49
|
+
* @param {object} params.langchain `@langchain/core/runnables/base` export
|
|
50
|
+
* @param {Shim} params.shim instace of shim
|
|
51
|
+
*/
|
|
52
|
+
function instrumentInvokeChain({ langchain, shim }) {
|
|
31
53
|
shim.record(
|
|
32
54
|
langchain.RunnableSequence.prototype,
|
|
33
55
|
'invoke',
|
|
34
|
-
function wrapCall(shim,
|
|
56
|
+
function wrapCall(shim, _invoke, fnName, args) {
|
|
35
57
|
const [request, params] = args
|
|
36
58
|
const metadata = params?.metadata ?? {}
|
|
37
59
|
const tags = params?.tags ?? []
|
|
@@ -41,53 +63,183 @@ module.exports = function initialize(shim, langchain) {
|
|
|
41
63
|
promise: true,
|
|
42
64
|
// eslint-disable-next-line max-params
|
|
43
65
|
after(_shim, _fn, _name, err, output, segment) {
|
|
44
|
-
|
|
45
|
-
const completionSummary = new LangChainCompletionSummary({
|
|
46
|
-
agent,
|
|
47
|
-
messages: [{ output }],
|
|
48
|
-
metadata,
|
|
49
|
-
tags,
|
|
66
|
+
recordChatCompletionEvents({
|
|
50
67
|
segment,
|
|
51
|
-
|
|
52
|
-
runId: segment[langchainRunId]
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
common.recordEvent({
|
|
56
|
-
agent,
|
|
57
|
-
type: 'LlmChatCompletionSummary',
|
|
58
|
-
pkgVersion,
|
|
59
|
-
msg: completionSummary
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
// output can be BaseMessage with a content property https://js.langchain.com/docs/modules/model_io/concepts#messages
|
|
63
|
-
// or an output parser https://js.langchain.com/docs/modules/model_io/concepts#output-parsers
|
|
64
|
-
recordCompletions({
|
|
68
|
+
messages: [output],
|
|
65
69
|
events: [request, output],
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
70
|
+
metadata,
|
|
71
|
+
tags,
|
|
72
|
+
err,
|
|
69
73
|
shim
|
|
70
74
|
})
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Instruments and records span and relevant LLM events for `chain.stream`
|
|
83
|
+
*
|
|
84
|
+
* @param {object} params function params
|
|
85
|
+
* @param {object} params.langchain `@langchain/core/runnables/base` export
|
|
86
|
+
* @param {Shim} params.shim instace of shim
|
|
87
|
+
*/
|
|
88
|
+
function instrumentStream({ langchain, shim }) {
|
|
89
|
+
shim.record(
|
|
90
|
+
langchain.RunnableSequence.prototype,
|
|
91
|
+
'stream',
|
|
92
|
+
function wrapStream(shim, _stream, fnName, args) {
|
|
93
|
+
const [request, params] = args
|
|
94
|
+
const metadata = params?.metadata ?? {}
|
|
95
|
+
const tags = params?.tags ?? []
|
|
71
96
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
97
|
+
return new RecorderSpec({
|
|
98
|
+
name: `${LANGCHAIN.CHAIN}/${fnName}`,
|
|
99
|
+
promise: true,
|
|
100
|
+
// eslint-disable-next-line max-params
|
|
101
|
+
after(_shim, _fn, _name, err, output, segment) {
|
|
102
|
+
// Input error occurred which means a stream was not created.
|
|
103
|
+
// Skip instrumenting streaming and create Llm Events from
|
|
104
|
+
// the data we have
|
|
105
|
+
if (output?.next) {
|
|
106
|
+
wrapNextHandler({ shim, output, segment, request, metadata, tags })
|
|
107
|
+
} else {
|
|
108
|
+
recordChatCompletionEvents({
|
|
109
|
+
segment,
|
|
110
|
+
messages: [],
|
|
111
|
+
events: [request],
|
|
112
|
+
metadata,
|
|
113
|
+
tags,
|
|
75
114
|
err,
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
cause: err,
|
|
79
|
-
summary: completionSummary
|
|
80
|
-
})
|
|
81
|
-
)
|
|
115
|
+
shim
|
|
116
|
+
})
|
|
82
117
|
}
|
|
83
|
-
|
|
84
|
-
segment.transaction.trace.attributes.addAttribute(DESTINATIONS.TRANS_EVENT, 'llm', true)
|
|
85
118
|
}
|
|
86
119
|
})
|
|
87
120
|
}
|
|
88
121
|
)
|
|
89
122
|
}
|
|
90
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Wraps the next method on the IterableReadableStream. It will also record the Llm
|
|
126
|
+
* events when the stream is done processing.
|
|
127
|
+
*
|
|
128
|
+
* @param {object} params function params
|
|
129
|
+
* @param {Shim} params.shim shim instance
|
|
130
|
+
* @param {TraceSegment} params.segment active segment
|
|
131
|
+
* @param {function} params.output IterableReadableStream
|
|
132
|
+
* @param {string} params.request the prompt message
|
|
133
|
+
* @param {object} params.metadata metadata for the call
|
|
134
|
+
* @param {Array} params.tags tags for the call
|
|
135
|
+
*/
|
|
136
|
+
function wrapNextHandler({ shim, output, segment, request, metadata, tags }) {
|
|
137
|
+
shim.wrap(output, 'next', function wrapIterator(shim, orig) {
|
|
138
|
+
let content = ''
|
|
139
|
+
return async function wrappedIterator() {
|
|
140
|
+
try {
|
|
141
|
+
const result = await orig.apply(this, arguments)
|
|
142
|
+
// only create Llm events when stream iteration is done
|
|
143
|
+
if (result?.done) {
|
|
144
|
+
recordChatCompletionEvents({
|
|
145
|
+
segment,
|
|
146
|
+
messages: [content],
|
|
147
|
+
events: [request, content],
|
|
148
|
+
metadata,
|
|
149
|
+
tags,
|
|
150
|
+
shim
|
|
151
|
+
})
|
|
152
|
+
} else {
|
|
153
|
+
content += result.value
|
|
154
|
+
}
|
|
155
|
+
return result
|
|
156
|
+
} catch (error) {
|
|
157
|
+
recordChatCompletionEvents({
|
|
158
|
+
segment,
|
|
159
|
+
messages: [content],
|
|
160
|
+
events: [request, content],
|
|
161
|
+
metadata,
|
|
162
|
+
tags,
|
|
163
|
+
err: error,
|
|
164
|
+
shim
|
|
165
|
+
})
|
|
166
|
+
throw error
|
|
167
|
+
} finally {
|
|
168
|
+
// update segment duration on every stream iteration to extend
|
|
169
|
+
// the timer
|
|
170
|
+
segment.touch()
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
})
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Ends active segment, creates LlmChatCompletionSummary, and LlmChatCompletionMessage(s), and handles errors if they exists
|
|
178
|
+
*
|
|
179
|
+
* @param {object} params function params
|
|
180
|
+
* @param {TraceSegment} params.segment active segment
|
|
181
|
+
* @param {Array} params.messages response messages
|
|
182
|
+
* @param {Array} params.events prompt and response messages
|
|
183
|
+
* @param {object} params.metadata metadata for the call
|
|
184
|
+
* @param {Array} params.tags tags for the call
|
|
185
|
+
* @param {Error} params.err error object from call
|
|
186
|
+
* @param {Shim} params.shim shim instance
|
|
187
|
+
*/
|
|
188
|
+
function recordChatCompletionEvents({ segment, messages, events, metadata, tags, err, shim }) {
|
|
189
|
+
const { pkgVersion, agent } = shim
|
|
190
|
+
segment.end()
|
|
191
|
+
const completionSummary = new LangChainCompletionSummary({
|
|
192
|
+
agent,
|
|
193
|
+
messages,
|
|
194
|
+
metadata,
|
|
195
|
+
tags,
|
|
196
|
+
segment,
|
|
197
|
+
error: err != null,
|
|
198
|
+
runId: segment[langchainRunId]
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
common.recordEvent({
|
|
202
|
+
agent,
|
|
203
|
+
type: 'LlmChatCompletionSummary',
|
|
204
|
+
pkgVersion,
|
|
205
|
+
msg: completionSummary
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
// output can be BaseMessage with a content property https://js.langchain.com/docs/modules/model_io/concepts#messages
|
|
209
|
+
// or an output parser https://js.langchain.com/docs/modules/model_io/concepts#output-parsers
|
|
210
|
+
recordCompletions({
|
|
211
|
+
events,
|
|
212
|
+
completionSummary,
|
|
213
|
+
agent,
|
|
214
|
+
segment,
|
|
215
|
+
shim
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
if (err) {
|
|
219
|
+
agent.errors.add(
|
|
220
|
+
segment.transaction,
|
|
221
|
+
err,
|
|
222
|
+
new LlmErrorMessage({
|
|
223
|
+
response: {},
|
|
224
|
+
cause: err,
|
|
225
|
+
summary: completionSummary
|
|
226
|
+
})
|
|
227
|
+
)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
segment.transaction.trace.attributes.addAttribute(DESTINATIONS.TRANS_EVENT, 'llm', true)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Records the LlmChatCompletionMessage(s)
|
|
235
|
+
*
|
|
236
|
+
* @param {object} params function params
|
|
237
|
+
* @param {Array} params.events prompt and response messages
|
|
238
|
+
* @param {LangChainCompletionSummary} params.completionSummary LlmChatCompletionSummary event
|
|
239
|
+
* @param {Agent} params.agent instance of agent
|
|
240
|
+
* @param {TraceSegment} params.segment active segment
|
|
241
|
+
* @param {Shim} params.shim shim instance
|
|
242
|
+
*/
|
|
91
243
|
function recordCompletions({ events, completionSummary, agent, segment, shim }) {
|
|
92
244
|
for (let i = 0; i < events.length; i += 1) {
|
|
93
245
|
let msg = events[i]
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict'
|
|
7
|
+
const {
|
|
8
|
+
AI: { LANGCHAIN }
|
|
9
|
+
} = require('../../metrics/names')
|
|
10
|
+
const { LangChainVectorSearch, LangChainVectorSearchResult } = require('../../llm-events/langchain')
|
|
11
|
+
const { recordEvent, shouldSkipInstrumentation } = require('./common')
|
|
12
|
+
const { DESTINATIONS } = require('../../config/attribute-filter')
|
|
13
|
+
const { RecorderSpec } = require('../../shim/specs')
|
|
14
|
+
const LlmErrorMessage = require('../../llm-events/error-message')
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Generates a LangChainVectorSearch for entire search request.
|
|
18
|
+
* Also iterates over documents in output and generates a
|
|
19
|
+
* LangChainVectorSearchResult for each document.
|
|
20
|
+
*
|
|
21
|
+
* @param {object} params input params
|
|
22
|
+
* @param {string} params.request vector search query
|
|
23
|
+
* @param {number} params.k vector search top k
|
|
24
|
+
* @param {object} params.output vector search documents
|
|
25
|
+
* @param {Agent} params.agent NR agent instance
|
|
26
|
+
* @param {TraceSegment} params.segment active segment from vector search
|
|
27
|
+
* @param {string} params.pkgVersion langchain version
|
|
28
|
+
* @param {err} params.err if it exists
|
|
29
|
+
*/
|
|
30
|
+
function recordVectorSearch({ request, k, output, agent, segment, pkgVersion, err }) {
|
|
31
|
+
const vectorSearch = new LangChainVectorSearch({
|
|
32
|
+
agent,
|
|
33
|
+
segment,
|
|
34
|
+
query: request,
|
|
35
|
+
k,
|
|
36
|
+
documents: output,
|
|
37
|
+
error: err !== null
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
recordEvent({ agent, type: 'LlmVectorSearch', pkgVersion, msg: vectorSearch })
|
|
41
|
+
|
|
42
|
+
output.forEach((document, sequence) => {
|
|
43
|
+
const vectorSearchResult = new LangChainVectorSearchResult({
|
|
44
|
+
agent,
|
|
45
|
+
segment,
|
|
46
|
+
metadata: document.metadata,
|
|
47
|
+
pageContent: document.pageContent,
|
|
48
|
+
sequence,
|
|
49
|
+
search_id: vectorSearch.id
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
recordEvent({
|
|
53
|
+
agent,
|
|
54
|
+
type: 'LlmVectorSearchResult',
|
|
55
|
+
pkgVersion,
|
|
56
|
+
msg: vectorSearchResult
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
if (err) {
|
|
61
|
+
agent.errors.add(
|
|
62
|
+
segment.transaction,
|
|
63
|
+
err,
|
|
64
|
+
new LlmErrorMessage({
|
|
65
|
+
response: output,
|
|
66
|
+
cause: err,
|
|
67
|
+
vectorsearch: vectorSearch
|
|
68
|
+
})
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = function initialize(shim, vectorstores) {
|
|
74
|
+
const { agent, pkgVersion } = shim
|
|
75
|
+
|
|
76
|
+
if (shouldSkipInstrumentation(agent.config)) {
|
|
77
|
+
shim.logger.debug(
|
|
78
|
+
'langchain instrumentation is disabled. To enable set `config.ai_monitoring.enabled` to true'
|
|
79
|
+
)
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
shim.record(
|
|
84
|
+
vectorstores.VectorStore.prototype,
|
|
85
|
+
'similaritySearch',
|
|
86
|
+
function wrapCall(shim, similaritySearch, fnName, args) {
|
|
87
|
+
const [request, k] = args
|
|
88
|
+
|
|
89
|
+
return new RecorderSpec({
|
|
90
|
+
name: `${LANGCHAIN.VECTORSTORE}/${fnName}`,
|
|
91
|
+
promise: true,
|
|
92
|
+
// eslint-disable-next-line max-params
|
|
93
|
+
after(_shim, _fn, _name, err, output, segment) {
|
|
94
|
+
if (!output) {
|
|
95
|
+
// If we get an error, it is possible that `output = null`.
|
|
96
|
+
// In that case, we define it to be an empty array.
|
|
97
|
+
output = []
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
segment.end()
|
|
101
|
+
recordVectorSearch({ request, k, output, agent, segment, pkgVersion, err })
|
|
102
|
+
|
|
103
|
+
segment.transaction.trace.attributes.addAttribute(DESTINATIONS.TRANS_EVENT, 'llm', true)
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
}
|
|
@@ -93,7 +93,7 @@ function wrapMiddleware(shim, middleware, _name, route) {
|
|
|
93
93
|
})
|
|
94
94
|
|
|
95
95
|
const wrappedMw = shim.recordMiddleware(middleware, spec)
|
|
96
|
-
if (middleware
|
|
96
|
+
if (shim.isAsyncFunction(middleware)) {
|
|
97
97
|
return async function asyncShim() {
|
|
98
98
|
return wrappedMw.apply(this, arguments)
|
|
99
99
|
}
|
package/lib/instrumentations.js
CHANGED
|
@@ -55,6 +55,8 @@ module.exports = function instrumentations() {
|
|
|
55
55
|
'loglevel': { type: MODULE_TYPE.TRACKING },
|
|
56
56
|
'npmlog': { type: MODULE_TYPE.TRACKING },
|
|
57
57
|
'fancy-log': { type: MODULE_TYPE.TRACKING },
|
|
58
|
-
'knex': { type: MODULE_TYPE.TRACKING }
|
|
58
|
+
'knex': { type: MODULE_TYPE.TRACKING },
|
|
59
|
+
'@azure/openai': { type: MODULE_TYPE.TRACKING },
|
|
60
|
+
'@langchain/community/llms/bedrock': { type: MODULE_TYPE.TRACKING }
|
|
59
61
|
}
|
|
60
62
|
}
|
|
@@ -18,14 +18,19 @@ module.exports = class LlmErrorMessage {
|
|
|
18
18
|
* conversation if it was a chat completion conversation.
|
|
19
19
|
* @param {LlmEmbedding} [params.embedding] Details about the conversation
|
|
20
20
|
* if it was an embedding conversation.
|
|
21
|
+
* @param {LlmVectorStoreSearch} [params.vectorsearch] Details about the vector
|
|
22
|
+
* search if it was a vector search event.
|
|
23
|
+
* @param {LlmTool} [params.tool] Details about the tool event if it was a tool event.
|
|
21
24
|
*/
|
|
22
|
-
constructor({ response, cause, summary, embedding } = {}) {
|
|
25
|
+
constructor({ response, cause, summary, embedding, vectorsearch, tool } = {}) {
|
|
23
26
|
this['http.statusCode'] = response?.status ?? cause?.status
|
|
24
27
|
this['error.message'] = cause?.message
|
|
25
28
|
this['error.code'] = response?.code ?? cause?.error?.code
|
|
26
29
|
this['error.param'] = response?.param ?? cause?.error?.param
|
|
27
30
|
this.completion_id = summary?.id
|
|
28
31
|
this.embedding_id = embedding?.id
|
|
32
|
+
this.vector_store_id = vectorsearch?.id
|
|
33
|
+
this.tool_id = tool?.id
|
|
29
34
|
}
|
|
30
35
|
|
|
31
36
|
get [Symbol.toStringTag]() {
|
|
@@ -36,6 +36,7 @@ class LangChainCompletionMessage extends LangChainEvent {
|
|
|
36
36
|
constructor(params = defaultParams) {
|
|
37
37
|
params = Object.assign({}, defaultParams, params)
|
|
38
38
|
super(params)
|
|
39
|
+
const { agent } = params
|
|
39
40
|
|
|
40
41
|
if (params.runId) {
|
|
41
42
|
this.id = `${params.runId}-${params.sequence}`
|
|
@@ -43,10 +44,13 @@ class LangChainCompletionMessage extends LangChainEvent {
|
|
|
43
44
|
this.id = `${this.id}-${params.sequence}`
|
|
44
45
|
}
|
|
45
46
|
|
|
46
|
-
this.content = params.content
|
|
47
47
|
this.sequence = params.sequence
|
|
48
48
|
this.completion_id = params.completionId
|
|
49
49
|
this.is_response = params.isResponse ?? false
|
|
50
|
+
|
|
51
|
+
if (agent.config.ai_monitoring.record_content.enabled === true) {
|
|
52
|
+
this.content = params.content
|
|
53
|
+
}
|
|
50
54
|
}
|
|
51
55
|
}
|
|
52
56
|
|
|
@@ -51,7 +51,6 @@ class LangChainEvent extends BaseEvent {
|
|
|
51
51
|
ingest_source = 'Node'
|
|
52
52
|
vendor = 'langchain'
|
|
53
53
|
virtual_llm = true
|
|
54
|
-
error = false
|
|
55
54
|
|
|
56
55
|
constructor(params = defaultParams) {
|
|
57
56
|
params = Object.assign({}, defaultParams, params)
|
|
@@ -66,7 +65,7 @@ class LangChainEvent extends BaseEvent {
|
|
|
66
65
|
this.langchainMeta = params.metadata
|
|
67
66
|
this.metadata = agent
|
|
68
67
|
this.tags = Array.isArray(params.tags) ? params.tags.join(',') : params.tags
|
|
69
|
-
this.error = params.error ??
|
|
68
|
+
this.error = params.error ?? null
|
|
70
69
|
|
|
71
70
|
if (params.virtual !== undefined) {
|
|
72
71
|
if (params.virtual !== true && params.virtual !== false) {
|
|
@@ -9,8 +9,8 @@ const LangChainEvent = require('./event')
|
|
|
9
9
|
class LangChainTool extends LangChainEvent {
|
|
10
10
|
constructor(params) {
|
|
11
11
|
super(params)
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
const { agent } = params
|
|
13
|
+
|
|
14
14
|
this.name = params.name
|
|
15
15
|
this.description = params.description
|
|
16
16
|
this.duration = params?.segment?.getDurationInMillis()
|
|
@@ -18,6 +18,11 @@ class LangChainTool extends LangChainEvent {
|
|
|
18
18
|
delete this.request_id
|
|
19
19
|
delete this.virtual_llm
|
|
20
20
|
delete this.conversation_id
|
|
21
|
+
|
|
22
|
+
if (agent.config.ai_monitoring.record_content.enabled === true) {
|
|
23
|
+
this.input = params.input
|
|
24
|
+
this.output = params.output
|
|
25
|
+
}
|
|
21
26
|
}
|
|
22
27
|
}
|
|
23
28
|
|
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
'use strict'
|
|
7
7
|
|
|
8
8
|
const LangChainEvent = require('./event')
|
|
9
|
-
const crypto = require('crypto')
|
|
10
9
|
|
|
11
10
|
/**
|
|
12
11
|
* @typedef {object} LangChainVectorSearchResultParams
|
|
13
12
|
* @augments LangChainEventParams
|
|
14
13
|
* @property {string} pageContent The stringified contents of the pageContent attribute on each returned search result document.
|
|
15
14
|
* @property {number} [sequence=0] The index of the document in the search result documents list.
|
|
15
|
+
* @property {string} search_id The identifier from the LangChainVectorSearch event.
|
|
16
16
|
*/
|
|
17
17
|
/**
|
|
18
18
|
* @type {LangChainVectorSearchResultParams}
|
|
@@ -23,14 +23,17 @@ const defaultParams = {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
class LangChainVectorSearchResult extends LangChainEvent {
|
|
26
|
-
search_id = crypto.randomUUID()
|
|
27
|
-
|
|
28
26
|
constructor(params) {
|
|
29
27
|
params = Object.assign({}, defaultParams, params)
|
|
30
28
|
super(params)
|
|
29
|
+
const { agent } = params
|
|
31
30
|
|
|
32
|
-
this.
|
|
31
|
+
this.search_id = params.search_id
|
|
33
32
|
this.sequence = params.sequence
|
|
33
|
+
|
|
34
|
+
if (agent.config.ai_monitoring.record_content.enabled === true) {
|
|
35
|
+
this.page_content = params.pageContent
|
|
36
|
+
}
|
|
34
37
|
}
|
|
35
38
|
}
|
|
36
39
|
|
|
@@ -28,12 +28,15 @@ class LangChainVectorSearch extends LangChainEvent {
|
|
|
28
28
|
constructor(params) {
|
|
29
29
|
params = Object.assign({}, defaultParams, params)
|
|
30
30
|
super(params)
|
|
31
|
-
const { segment } = params
|
|
31
|
+
const { agent, segment } = params
|
|
32
32
|
|
|
33
33
|
this.duration = segment?.getDurationInMillis()
|
|
34
|
-
this['request.query'] = params.query
|
|
35
34
|
this['request.k'] = params.k
|
|
36
35
|
this['response.number_of_documents'] = params.documents?.length
|
|
36
|
+
|
|
37
|
+
if (agent.config.ai_monitoring.record_content.enabled === true) {
|
|
38
|
+
this['request.query'] = params.query
|
|
39
|
+
}
|
|
37
40
|
}
|
|
38
41
|
}
|
|
39
42
|
|
|
@@ -10,10 +10,24 @@ module.exports = class LlmChatCompletionMessage extends LlmEvent {
|
|
|
10
10
|
constructor({ agent, segment, request = {}, response = {}, index = 0, message, completionId }) {
|
|
11
11
|
super({ agent, segment, request, response })
|
|
12
12
|
this.id = `${response.id}-${index}`
|
|
13
|
-
this.content = message?.content
|
|
14
13
|
this.role = message?.role
|
|
15
14
|
this.sequence = index
|
|
16
15
|
this.completion_id = completionId
|
|
17
|
-
this.is_response = response?.choices?.[0]?.message?.content ===
|
|
16
|
+
this.is_response = response?.choices?.[0]?.message?.content === message?.content
|
|
17
|
+
|
|
18
|
+
if (agent.config.ai_monitoring.record_content.enabled === true) {
|
|
19
|
+
this.content = message?.content
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const tokenCB = agent.llm?.tokenCountCallback
|
|
23
|
+
if (typeof tokenCB !== 'function') {
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (this.is_response) {
|
|
28
|
+
this.token_count = tokenCB(this['response.model'], message?.content)
|
|
29
|
+
} else {
|
|
30
|
+
this.token_count = tokenCB(request.model || request.engine, message?.content)
|
|
31
|
+
}
|
|
18
32
|
}
|
|
19
33
|
}
|
|
@@ -10,6 +10,13 @@ module.exports = class LlmEmbedding extends LlmEvent {
|
|
|
10
10
|
constructor({ agent, segment, request = {}, response = {}, withError = false }) {
|
|
11
11
|
super({ agent, segment, request, response, responseAttrs: true })
|
|
12
12
|
this.error = withError
|
|
13
|
-
|
|
13
|
+
|
|
14
|
+
if (agent.config.ai_monitoring.record_content.enabled === true) {
|
|
15
|
+
this.input = request.input?.toString()
|
|
16
|
+
}
|
|
17
|
+
this.token_count = agent.llm?.tokenCountCallback?.(
|
|
18
|
+
this['request.model'],
|
|
19
|
+
request.input?.toString()
|
|
20
|
+
)
|
|
14
21
|
}
|
|
15
22
|
}
|
|
@@ -19,7 +19,7 @@ module.exports = class LlmEvent extends BaseEvent {
|
|
|
19
19
|
this.span_id = segment?.id
|
|
20
20
|
this.transaction_id = segment?.transaction?.id
|
|
21
21
|
this['response.model'] = response.model
|
|
22
|
-
this.vendor = '
|
|
22
|
+
this.vendor = 'openai'
|
|
23
23
|
this.ingest_source = 'Node'
|
|
24
24
|
this.metadata = agent
|
|
25
25
|
|
package/lib/metrics/names.js
CHANGED
|
@@ -170,7 +170,8 @@ const AI = {
|
|
|
170
170
|
EMBEDDING: 'Llm/embedding',
|
|
171
171
|
COMPLETION: 'Llm/completion',
|
|
172
172
|
TOOL: 'Llm/tool',
|
|
173
|
-
CHAIN: 'Llm/chain'
|
|
173
|
+
CHAIN: 'Llm/chain',
|
|
174
|
+
VECTORSTORE: 'Llm/vectorstore'
|
|
174
175
|
}
|
|
175
176
|
|
|
176
177
|
AI.OPENAI = {
|
|
@@ -184,7 +185,8 @@ AI.LANGCHAIN = {
|
|
|
184
185
|
EMBEDDING: `${AI.EMBEDDING}/Langchain`,
|
|
185
186
|
COMPLETION: `${AI.COMPLETION}/Langchain`,
|
|
186
187
|
TOOL: `${AI.TOOL}/Langchain`,
|
|
187
|
-
CHAIN: `${AI.CHAIN}/Langchain
|
|
188
|
+
CHAIN: `${AI.CHAIN}/Langchain`,
|
|
189
|
+
VECTORSTORE: `${AI.VECTORSTORE}/Langchain`
|
|
188
190
|
}
|
|
189
191
|
|
|
190
192
|
const RESTIFY = {
|
package/lib/shim/shim.js
CHANGED
|
@@ -125,6 +125,7 @@ Shim.prototype.getName = getName
|
|
|
125
125
|
Shim.prototype.isObject = isObject
|
|
126
126
|
Shim.prototype.isFunction = isFunction
|
|
127
127
|
Shim.prototype.isPromise = isPromise
|
|
128
|
+
Shim.prototype.isAsyncFunction = isAsyncFunction
|
|
128
129
|
Shim.prototype.isString = isString
|
|
129
130
|
Shim.prototype.isNumber = isNumber
|
|
130
131
|
Shim.prototype.isBoolean = isBoolean
|
|
@@ -1345,6 +1346,20 @@ function isPromise(obj) {
|
|
|
1345
1346
|
return obj && typeof obj.then === 'function'
|
|
1346
1347
|
}
|
|
1347
1348
|
|
|
1349
|
+
/**
|
|
1350
|
+
* Determines if function is an async function.
|
|
1351
|
+
* Note it does not test if the return value of function is a
|
|
1352
|
+
* promise or async function
|
|
1353
|
+
*
|
|
1354
|
+
* @memberof Shim.prototype
|
|
1355
|
+
* @param fn
|
|
1356
|
+
* @param (function) function to test if async
|
|
1357
|
+
* @returns {boolean} True if the function is an async function
|
|
1358
|
+
*/
|
|
1359
|
+
function isAsyncFunction(fn) {
|
|
1360
|
+
return fn.constructor.name === 'AsyncFunction'
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1348
1363
|
/**
|
|
1349
1364
|
* Determines if the given value is null.
|
|
1350
1365
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "newrelic",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.13.0",
|
|
4
4
|
"author": "New Relic Node.js agent team <nodejs@newrelic.com>",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"contributors": [
|
|
@@ -193,8 +193,8 @@
|
|
|
193
193
|
"dependencies": {
|
|
194
194
|
"@grpc/grpc-js": "^1.9.4",
|
|
195
195
|
"@grpc/proto-loader": "^0.7.5",
|
|
196
|
-
"@newrelic/aws-sdk": "^7.
|
|
197
|
-
"@newrelic/koa": "^
|
|
196
|
+
"@newrelic/aws-sdk": "^7.3.0",
|
|
197
|
+
"@newrelic/koa": "^9.0.0",
|
|
198
198
|
"@newrelic/ritm": "^7.2.0",
|
|
199
199
|
"@newrelic/security-agent": "^1.0.1",
|
|
200
200
|
"@newrelic/superagent": "^7.0.1",
|