dd-trace 5.59.0 → 5.61.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/index.d.ts +6 -0
- package/package.json +7 -7
- package/packages/datadog-code-origin/index.js +3 -0
- package/packages/datadog-instrumentations/src/apollo-server.js +14 -3
- package/packages/datadog-instrumentations/src/azure-functions.js +5 -0
- package/packages/datadog-instrumentations/src/azure-service-bus.js +38 -0
- package/packages/datadog-instrumentations/src/fastify.js +17 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/next.js +17 -18
- package/packages/datadog-instrumentations/src/openai.js +13 -114
- package/packages/datadog-instrumentations/src/sequelize.js +4 -14
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/tracing.js +6 -38
- package/packages/datadog-plugin-azure-functions/src/index.js +57 -28
- package/packages/datadog-plugin-azure-service-bus/src/index.js +15 -0
- package/packages/datadog-plugin-azure-service-bus/src/producer.js +36 -0
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +24 -23
- package/packages/datadog-plugin-google-cloud-vertexai/src/tracing.js +3 -155
- package/packages/datadog-plugin-langchain/src/handlers/default.js +0 -18
- package/packages/datadog-plugin-langchain/src/handlers/embedding.js +0 -48
- package/packages/datadog-plugin-langchain/src/handlers/language_models.js +18 -0
- package/packages/datadog-plugin-langchain/src/tracing.js +5 -17
- package/packages/datadog-plugin-openai/src/stream-helpers.js +114 -0
- package/packages/datadog-plugin-openai/src/tracing.js +38 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/cookie-analyzer.js +8 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +2 -2
- package/packages/dd-trace/src/appsec/iast/analyzers/missing-header-analyzer.js +11 -10
- package/packages/dd-trace/src/appsec/iast/analyzers/set-cookies-header-interceptor.js +25 -18
- package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +13 -5
- package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +5 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/xcontenttype-header-missing-analyzer.js +2 -2
- package/packages/dd-trace/src/appsec/iast/iast-plugin.js +4 -0
- package/packages/dd-trace/src/appsec/iast/index.js +25 -7
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +79 -21
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +1 -3
- package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +0 -4
- package/packages/dd-trace/src/appsec/reporter.js +3 -15
- package/packages/dd-trace/src/appsec/waf/index.js +20 -1
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +2 -1
- package/packages/dd-trace/src/config.js +0 -16
- package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +4 -8
- package/packages/dd-trace/src/datastreams/schemas/schema_sampler.js +2 -4
- package/packages/dd-trace/src/debugger/config.js +16 -0
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/config.js +2 -6
- package/packages/dd-trace/src/debugger/devtools_client/index.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/log.js +19 -0
- package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/send.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/state.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -1
- package/packages/dd-trace/src/debugger/index.js +13 -3
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/util/ci.js +23 -7
- package/packages/dd-trace/src/plugins/util/git.js +53 -18
- package/packages/dd-trace/src/plugins/util/tags.js +8 -6
- package/packages/dd-trace/src/profiling/profilers/events.js +3 -3
- package/packages/dd-trace/src/profiling/profilers/space.js +4 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +5 -4
- package/packages/dd-trace/src/remote_config/capabilities.js +2 -1
- package/packages/dd-trace/src/remote_config/index.js +2 -0
- package/packages/dd-trace/src/remote_config/scheduler.js +2 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +4 -0
- package/packages/dd-trace/src/supported-configurations.json +1 -0
- package/packages/datadog-plugin-langchain/src/handlers/chain.js +0 -50
- package/packages/datadog-plugin-langchain/src/handlers/language_models/chat_model.js +0 -101
- package/packages/datadog-plugin-langchain/src/handlers/language_models/index.js +0 -48
- package/packages/datadog-plugin-langchain/src/handlers/language_models/llm.js +0 -58
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const TracingPlugin = require('../../dd-trace/src/plugins/tracing')
|
|
4
|
-
const { storage } = require('../../datadog-core')
|
|
5
4
|
const serverless = require('../../dd-trace/src/plugins/util/serverless')
|
|
6
5
|
const web = require('../../dd-trace/src/plugins/util/web')
|
|
7
6
|
|
|
@@ -11,7 +10,9 @@ const triggerMap = {
|
|
|
11
10
|
get: 'Http',
|
|
12
11
|
patch: 'Http',
|
|
13
12
|
post: 'Http',
|
|
14
|
-
put: 'Http'
|
|
13
|
+
put: 'Http',
|
|
14
|
+
serviceBusQueue: 'ServiceBus',
|
|
15
|
+
serviceBusTopic: 'ServiceBus',
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
class AzureFunctionsPlugin extends TracingPlugin {
|
|
@@ -22,24 +23,16 @@ class AzureFunctionsPlugin extends TracingPlugin {
|
|
|
22
23
|
static get prefix () { return 'tracing:datadog:azure:functions:invoke' }
|
|
23
24
|
|
|
24
25
|
bindStart (ctx) {
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
// httpRequest.headers is a map
|
|
28
|
-
const childOf = this._tracer.extract('http_headers', Object.fromEntries(httpRequest.headers))
|
|
26
|
+
const childOf = extractTraceContext(this._tracer, ctx)
|
|
27
|
+
const meta = getMetaForTrigger(ctx)
|
|
29
28
|
const span = this.startSpan(this.operationName(), {
|
|
30
29
|
childOf,
|
|
31
30
|
service: this.serviceName(),
|
|
32
31
|
type: 'serverless',
|
|
33
|
-
meta
|
|
34
|
-
|
|
35
|
-
'aas.function.trigger': mapTriggerTag(methodName)
|
|
36
|
-
}
|
|
37
|
-
}, false)
|
|
32
|
+
meta,
|
|
33
|
+
}, ctx)
|
|
38
34
|
|
|
39
35
|
ctx.span = span
|
|
40
|
-
ctx.parentStore = store
|
|
41
|
-
ctx.currentStore = { ...store, span }
|
|
42
|
-
|
|
43
36
|
return ctx.currentStore
|
|
44
37
|
}
|
|
45
38
|
|
|
@@ -49,21 +42,26 @@ class AzureFunctionsPlugin extends TracingPlugin {
|
|
|
49
42
|
}
|
|
50
43
|
|
|
51
44
|
asyncEnd (ctx) {
|
|
52
|
-
const { httpRequest, result = {} } = ctx
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
45
|
+
const { httpRequest, methodName, result = {} } = ctx
|
|
46
|
+
if (triggerMap[methodName] === 'Http') {
|
|
47
|
+
// If the method is an HTTP trigger, we need to patch the request and finish the span
|
|
48
|
+
const path = (new URL(httpRequest.url)).pathname
|
|
49
|
+
const req = {
|
|
50
|
+
method: httpRequest.method,
|
|
51
|
+
headers: Object.fromEntries(httpRequest.headers),
|
|
52
|
+
url: path
|
|
53
|
+
}
|
|
54
|
+
const context = web.patch(req)
|
|
55
|
+
context.config = this.config
|
|
56
|
+
context.paths = [path]
|
|
57
|
+
context.res = { statusCode: result.status }
|
|
58
|
+
context.span = ctx.currentStore.span
|
|
65
59
|
|
|
66
|
-
|
|
60
|
+
serverless.finishSpan(context)
|
|
61
|
+
// Fallback for other trigger types
|
|
62
|
+
} else {
|
|
63
|
+
super.finish()
|
|
64
|
+
}
|
|
67
65
|
}
|
|
68
66
|
|
|
69
67
|
configure (config) {
|
|
@@ -71,8 +69,39 @@ class AzureFunctionsPlugin extends TracingPlugin {
|
|
|
71
69
|
}
|
|
72
70
|
}
|
|
73
71
|
|
|
72
|
+
function getMetaForTrigger ({ functionName, methodName, invocationContext }) {
|
|
73
|
+
let meta = {
|
|
74
|
+
'aas.function.name': functionName,
|
|
75
|
+
'aas.function.trigger': mapTriggerTag(methodName)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (triggerMap[methodName] === 'ServiceBus') {
|
|
79
|
+
const triggerEntity = invocationContext.options.trigger.queueName || invocationContext.options.trigger.topicName
|
|
80
|
+
meta = {
|
|
81
|
+
...meta,
|
|
82
|
+
'messaging.message_id': invocationContext.triggerMetadata.messageId,
|
|
83
|
+
'messaging.operation': 'receive',
|
|
84
|
+
'messaging.system': 'servicebus',
|
|
85
|
+
'messaging.destination.name': triggerEntity,
|
|
86
|
+
'resource.name': `ServiceBus ${functionName}`,
|
|
87
|
+
'span.kind': 'consumer'
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return meta
|
|
92
|
+
}
|
|
93
|
+
|
|
74
94
|
function mapTriggerTag (methodName) {
|
|
75
95
|
return triggerMap[methodName] || 'Unknown'
|
|
76
96
|
}
|
|
77
97
|
|
|
98
|
+
function extractTraceContext (tracer, ctx) {
|
|
99
|
+
switch (String(triggerMap[ctx.methodName])) {
|
|
100
|
+
case 'Http':
|
|
101
|
+
return tracer.extract('http_headers', Object.fromEntries(ctx.httpRequest.headers))
|
|
102
|
+
case 'ServiceBus':
|
|
103
|
+
return tracer.extract('text_map', ctx.invocationContext.triggerMetadata.applicationProperties)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
78
107
|
module.exports = AzureFunctionsPlugin
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const ProducerPlugin = require('./producer')
|
|
4
|
+
const CompositePlugin = require('../../dd-trace/src/plugins/composite')
|
|
5
|
+
|
|
6
|
+
class AzureServiceBusPlugin extends CompositePlugin {
|
|
7
|
+
static get id () { return 'azure-service-bus' }
|
|
8
|
+
static get plugins () {
|
|
9
|
+
return {
|
|
10
|
+
producer: ProducerPlugin
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = AzureServiceBusPlugin
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const ProducerPlugin = require('../../dd-trace/src/plugins/producer')
|
|
4
|
+
|
|
5
|
+
class AzureServiceBusProducerPlugin extends ProducerPlugin {
|
|
6
|
+
static get id () { return 'azure-service-bus' }
|
|
7
|
+
static get operation () { return 'send' }
|
|
8
|
+
|
|
9
|
+
bindStart (ctx) {
|
|
10
|
+
const { sender, msg } = ctx
|
|
11
|
+
const qualifiedSenderNamespace = sender._sender.audience.replace('sb://', '')
|
|
12
|
+
const span = this.startSpan({
|
|
13
|
+
resource: sender.entityPath,
|
|
14
|
+
type: 'messaging',
|
|
15
|
+
meta: {
|
|
16
|
+
component: 'azure-service-bus',
|
|
17
|
+
'messaging.destination.name': sender.entityPath,
|
|
18
|
+
'messaging.operation': 'send',
|
|
19
|
+
'messaging.system': 'servicebus',
|
|
20
|
+
'network.destination.name': qualifiedSenderNamespace,
|
|
21
|
+
}
|
|
22
|
+
}, ctx)
|
|
23
|
+
|
|
24
|
+
// This is the correct key for injecting trace context into Azure Service Bus messages
|
|
25
|
+
// It may not be present in the message properties, so we ensure it exists
|
|
26
|
+
if (!msg.applicationProperties) {
|
|
27
|
+
msg.applicationProperties = {}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
this.tracer.inject(span, 'text_map', msg.applicationProperties)
|
|
31
|
+
|
|
32
|
+
return ctx.currentStore
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = AzureServiceBusProducerPlugin
|
|
@@ -223,10 +223,31 @@ function getSuiteStatus (suiteStats) {
|
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
class CypressPlugin {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
226
|
+
_isInit = false
|
|
227
|
+
testEnvironmentMetadata = getTestEnvironmentMetadata(TEST_FRAMEWORK_NAME)
|
|
228
|
+
|
|
229
|
+
finishedTestsByFile = {}
|
|
230
|
+
testStatuses = {}
|
|
231
|
+
|
|
232
|
+
isTestsSkipped = false
|
|
233
|
+
isSuitesSkippingEnabled = false
|
|
234
|
+
isCodeCoverageEnabled = false
|
|
235
|
+
isFlakyTestRetriesEnabled = false
|
|
236
|
+
isEarlyFlakeDetectionEnabled = false
|
|
237
|
+
isKnownTestsEnabled = false
|
|
238
|
+
earlyFlakeDetectionNumRetries = 0
|
|
239
|
+
testsToSkip = []
|
|
240
|
+
skippedTests = []
|
|
241
|
+
hasForcedToRunSuites = false
|
|
242
|
+
hasUnskippableSuites = false
|
|
243
|
+
unskippableSuites = []
|
|
244
|
+
knownTests = []
|
|
245
|
+
isTestManagementTestsEnabled = false
|
|
246
|
+
testManagementAttemptToFixRetries = 0
|
|
247
|
+
isImpactedTestsEnabled = false
|
|
248
|
+
modifiedTests = []
|
|
229
249
|
|
|
250
|
+
constructor () {
|
|
230
251
|
const {
|
|
231
252
|
[GIT_REPOSITORY_URL]: repositoryUrl,
|
|
232
253
|
[GIT_COMMIT_SHA]: sha,
|
|
@@ -265,26 +286,6 @@ class CypressPlugin {
|
|
|
265
286
|
commitHeadSha,
|
|
266
287
|
commitHeadMessage
|
|
267
288
|
}
|
|
268
|
-
this.finishedTestsByFile = {}
|
|
269
|
-
this.testStatuses = {}
|
|
270
|
-
|
|
271
|
-
this.isTestsSkipped = false
|
|
272
|
-
this.isSuitesSkippingEnabled = false
|
|
273
|
-
this.isCodeCoverageEnabled = false
|
|
274
|
-
this.isFlakyTestRetriesEnabled = false
|
|
275
|
-
this.isEarlyFlakeDetectionEnabled = false
|
|
276
|
-
this.isKnownTestsEnabled = false
|
|
277
|
-
this.earlyFlakeDetectionNumRetries = 0
|
|
278
|
-
this.testsToSkip = []
|
|
279
|
-
this.skippedTests = []
|
|
280
|
-
this.hasForcedToRunSuites = false
|
|
281
|
-
this.hasUnskippableSuites = false
|
|
282
|
-
this.unskippableSuites = []
|
|
283
|
-
this.knownTests = []
|
|
284
|
-
this.isTestManagementTestsEnabled = false
|
|
285
|
-
this.testManagementAttemptToFixRetries = 0
|
|
286
|
-
this.isImpactedTestsEnabled = false
|
|
287
|
-
this.modifiedTests = []
|
|
288
289
|
}
|
|
289
290
|
|
|
290
291
|
// Init function returns a promise that resolves with the Cypress configuration
|
|
@@ -7,7 +7,6 @@ const makeUtilities = require('../../dd-trace/src/plugins/util/llm')
|
|
|
7
7
|
|
|
8
8
|
const {
|
|
9
9
|
extractModel,
|
|
10
|
-
extractSystemInstructions
|
|
11
10
|
} = require('./utils')
|
|
12
11
|
|
|
13
12
|
class GoogleCloudVertexAITracingPlugin extends TracingPlugin {
|
|
@@ -23,20 +22,18 @@ class GoogleCloudVertexAITracingPlugin extends TracingPlugin {
|
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
bindStart (ctx) {
|
|
26
|
-
const { instance,
|
|
25
|
+
const { instance, resource } = ctx
|
|
27
26
|
|
|
28
27
|
const span = this.startSpan('vertexai.request', {
|
|
29
28
|
service: this.config.service,
|
|
30
29
|
resource,
|
|
31
30
|
kind: 'client',
|
|
32
31
|
meta: {
|
|
33
|
-
[MEASURED]: 1
|
|
32
|
+
[MEASURED]: 1,
|
|
33
|
+
'vertexai.request.model': extractModel(instance)
|
|
34
34
|
}
|
|
35
35
|
}, false)
|
|
36
36
|
|
|
37
|
-
const tags = this.tagRequest(request, instance, stream, span)
|
|
38
|
-
span.addTags(tags)
|
|
39
|
-
|
|
40
37
|
const store = storage('legacy').getStore() || {}
|
|
41
38
|
ctx.currentStore = { ...store, span }
|
|
42
39
|
|
|
@@ -47,157 +44,8 @@ class GoogleCloudVertexAITracingPlugin extends TracingPlugin {
|
|
|
47
44
|
const span = ctx.currentStore?.span
|
|
48
45
|
if (!span) return
|
|
49
46
|
|
|
50
|
-
const { result } = ctx
|
|
51
|
-
|
|
52
|
-
const response = result?.response
|
|
53
|
-
if (response) {
|
|
54
|
-
const tags = this.tagResponse(response, span)
|
|
55
|
-
span.addTags(tags)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
47
|
span.finish()
|
|
59
48
|
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Generate the request tags.
|
|
63
|
-
*
|
|
64
|
-
* @param {Object} request
|
|
65
|
-
* @param {Object} instance
|
|
66
|
-
* @param {boolean} stream
|
|
67
|
-
* @param {Span} span
|
|
68
|
-
* @returns {Object}
|
|
69
|
-
*/
|
|
70
|
-
tagRequest (request, instance, stream, span) {
|
|
71
|
-
const model = extractModel(instance)
|
|
72
|
-
const tags = {
|
|
73
|
-
'vertexai.request.model': model
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const history = instance.historyInternal
|
|
77
|
-
|
|
78
|
-
let contents = typeof request === 'string' || Array.isArray(request) ? request : request.contents
|
|
79
|
-
if (history) {
|
|
80
|
-
contents = [...history, ...(Array.isArray(contents) ? contents : [contents])]
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const generationConfig = instance.generationConfig || {}
|
|
84
|
-
for (const key of Object.keys(generationConfig)) {
|
|
85
|
-
const transformedKey = key.replaceAll(/([a-z0-9])([A-Z])/g, '$1_$2').toLowerCase()
|
|
86
|
-
tags[`vertexai.request.generation_config.${transformedKey}`] = JSON.stringify(generationConfig[key])
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (stream) {
|
|
90
|
-
tags['vertexai.request.stream'] = true
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (!this.isPromptCompletionSampled(span)) return tags
|
|
94
|
-
|
|
95
|
-
const systemInstructions = extractSystemInstructions(instance)
|
|
96
|
-
|
|
97
|
-
for (const [idx, systemInstruction] of systemInstructions.entries()) {
|
|
98
|
-
tags[`vertexai.request.system_instruction.${idx}.text`] = systemInstruction
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (typeof contents === 'string') {
|
|
102
|
-
tags['vertexai.request.contents.0.text'] = contents
|
|
103
|
-
return tags
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
for (const [contentIdx, content] of contents.entries()) {
|
|
107
|
-
this.tagRequestContent(tags, content, contentIdx)
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return tags
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
tagRequestPart (part, tags, partIdx, contentIdx) {
|
|
114
|
-
tags[`vertexai.request.contents.${contentIdx}.parts.${partIdx}.text`] = this.normalize(part.text)
|
|
115
|
-
|
|
116
|
-
const functionCall = part.functionCall
|
|
117
|
-
const functionResponse = part.functionResponse
|
|
118
|
-
|
|
119
|
-
if (functionCall) {
|
|
120
|
-
tags[`vertexai.request.contents.${contentIdx}.parts.${partIdx}.function_call.name`] = functionCall.name
|
|
121
|
-
tags[`vertexai.request.contents.${contentIdx}.parts.${partIdx}.function_call.args`] =
|
|
122
|
-
this.normalize(JSON.stringify(functionCall.args))
|
|
123
|
-
}
|
|
124
|
-
if (functionResponse) {
|
|
125
|
-
tags[`vertexai.request.contents.${contentIdx}.parts.${partIdx}.function_response.name`] =
|
|
126
|
-
functionResponse.name
|
|
127
|
-
tags[`vertexai.request.contents.${contentIdx}.parts.${partIdx}.function_response.response`] =
|
|
128
|
-
this.normalize(JSON.stringify(functionResponse.response))
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
tagRequestContent (tags, content, contentIdx) {
|
|
133
|
-
if (typeof content === 'string') {
|
|
134
|
-
tags[`vertexai.request.contents.${contentIdx}.text`] = this.normalize(content)
|
|
135
|
-
return
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (content.text || content.functionCall || content.functionResponse) {
|
|
139
|
-
this.tagRequestPart(content, tags, 0, contentIdx)
|
|
140
|
-
return
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const { role, parts } = content
|
|
144
|
-
if (role) {
|
|
145
|
-
tags[`vertexai.request.contents.${contentIdx}.role`] = role
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
for (const [partIdx, part] of parts.entries()) {
|
|
149
|
-
this.tagRequestPart(part, tags, partIdx, contentIdx)
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Generate the response tags.
|
|
155
|
-
*
|
|
156
|
-
* @param {Object} response
|
|
157
|
-
* @param {Span} span
|
|
158
|
-
* @returns {Object}
|
|
159
|
-
*/
|
|
160
|
-
tagResponse (response, span) {
|
|
161
|
-
const tags = {}
|
|
162
|
-
const isSampled = this.isPromptCompletionSampled(span)
|
|
163
|
-
|
|
164
|
-
const candidates = response.candidates
|
|
165
|
-
for (const [candidateIdx, candidate] of candidates.entries()) {
|
|
166
|
-
const finishReason = candidate.finishReason
|
|
167
|
-
if (finishReason) {
|
|
168
|
-
tags[`vertexai.response.candidates.${candidateIdx}.finish_reason`] = finishReason
|
|
169
|
-
}
|
|
170
|
-
const candidateContent = candidate.content
|
|
171
|
-
const role = candidateContent.role
|
|
172
|
-
tags[`vertexai.response.candidates.${candidateIdx}.content.role`] = role
|
|
173
|
-
|
|
174
|
-
if (!isSampled) continue
|
|
175
|
-
|
|
176
|
-
const parts = candidateContent.parts
|
|
177
|
-
for (const [partIdx, part] of parts.entries()) {
|
|
178
|
-
const text = part.text
|
|
179
|
-
tags[`vertexai.response.candidates.${candidateIdx}.content.parts.${partIdx}.text`] =
|
|
180
|
-
this.normalize(String(text))
|
|
181
|
-
|
|
182
|
-
const functionCall = part.functionCall
|
|
183
|
-
if (!functionCall) continue
|
|
184
|
-
|
|
185
|
-
tags[`vertexai.response.candidates.${candidateIdx}.content.parts.${partIdx}.function_call.name`] =
|
|
186
|
-
functionCall.name
|
|
187
|
-
tags[`vertexai.response.candidates.${candidateIdx}.content.parts.${partIdx}.function_call.args`] =
|
|
188
|
-
this.normalize(JSON.stringify(functionCall.args))
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const tokenCounts = response.usageMetadata
|
|
193
|
-
if (tokenCounts) {
|
|
194
|
-
tags['vertexai.response.usage.prompt_tokens'] = tokenCounts.promptTokenCount
|
|
195
|
-
tags['vertexai.response.usage.completion_tokens'] = tokenCounts.candidatesTokenCount
|
|
196
|
-
tags['vertexai.response.usage.total_tokens'] = tokenCounts.totalTokenCount
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
return tags
|
|
200
|
-
}
|
|
201
49
|
}
|
|
202
50
|
|
|
203
51
|
module.exports = GoogleCloudVertexAITracingPlugin
|
|
@@ -1,24 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const makeUtilities = require('../../../dd-trace/src/plugins/util/llm')
|
|
4
|
-
|
|
5
3
|
class LangChainHandler {
|
|
6
|
-
constructor (tracerConfig) {
|
|
7
|
-
const utilities = makeUtilities('langchain', tracerConfig)
|
|
8
|
-
|
|
9
|
-
this.normalize = utilities.normalize
|
|
10
|
-
this.isPromptCompletionSampled = utilities.isPromptCompletionSampled
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// no-op for default handler
|
|
14
|
-
getSpanStartTags (ctx) {}
|
|
15
|
-
|
|
16
|
-
// no-op for default handler
|
|
17
|
-
getSpanEndTags (ctx) {}
|
|
18
|
-
|
|
19
|
-
// no-op for default handler
|
|
20
|
-
extractApiKey (instance) {}
|
|
21
|
-
|
|
22
4
|
// no-op for default handler
|
|
23
5
|
extractProvider (instance) {}
|
|
24
6
|
|
|
@@ -3,54 +3,6 @@
|
|
|
3
3
|
const LangChainHandler = require('./default')
|
|
4
4
|
|
|
5
5
|
class LangChainEmbeddingHandler extends LangChainHandler {
|
|
6
|
-
getSpanStartTags (ctx, provider, span) {
|
|
7
|
-
const tags = {}
|
|
8
|
-
|
|
9
|
-
const inputTexts = ctx.args?.[0]
|
|
10
|
-
|
|
11
|
-
const sampled = this.isPromptCompletionSampled(span)
|
|
12
|
-
if (typeof inputTexts === 'string') {
|
|
13
|
-
// embed query
|
|
14
|
-
if (sampled) {
|
|
15
|
-
tags['langchain.request.inputs.0.text'] = this.normalize(inputTexts)
|
|
16
|
-
}
|
|
17
|
-
tags['langchain.request.input_counts'] = 1
|
|
18
|
-
} else {
|
|
19
|
-
// embed documents
|
|
20
|
-
if (sampled) {
|
|
21
|
-
for (const idx in inputTexts) {
|
|
22
|
-
const inputText = inputTexts[idx]
|
|
23
|
-
tags[`langchain.request.inputs.${idx}.text`] = this.normalize(inputText)
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
tags['langchain.request.input_counts'] = inputTexts.length
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return tags
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
getSpanEndTags (ctx) {
|
|
33
|
-
const tags = {}
|
|
34
|
-
|
|
35
|
-
const { result } = ctx
|
|
36
|
-
if (!Array.isArray(result)) return
|
|
37
|
-
|
|
38
|
-
tags['langchain.response.outputs.embedding_length'] = (
|
|
39
|
-
Array.isArray(result[0]) ? result[0] : result
|
|
40
|
-
).length
|
|
41
|
-
|
|
42
|
-
return tags
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
extractApiKey (instance) {
|
|
46
|
-
const apiKey =
|
|
47
|
-
instance.clientConfig?.apiKey ||
|
|
48
|
-
instance.apiKey ||
|
|
49
|
-
instance.client?.apiKey
|
|
50
|
-
if (!apiKey || apiKey.length < 4) return ''
|
|
51
|
-
return `...${apiKey.slice(-4)}`
|
|
52
|
-
}
|
|
53
|
-
|
|
54
6
|
extractProvider (instance) {
|
|
55
7
|
return instance.constructor.name.split('Embeddings')[0].toLowerCase()
|
|
56
8
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const LangChainHandler = require('./default')
|
|
4
|
+
|
|
5
|
+
class LangChainLanguageModelHandler extends LangChainHandler {
|
|
6
|
+
extractProvider (instance) {
|
|
7
|
+
return typeof instance._llmType === 'function' && instance._llmType().split('-')[0]
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
extractModel (instance) {
|
|
11
|
+
for (const attr of ['model', 'modelName', 'modelId', 'modelKey', 'repoId']) {
|
|
12
|
+
const modelName = instance[attr]
|
|
13
|
+
if (modelName) return modelName
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = LangChainLanguageModelHandler
|
|
@@ -4,15 +4,12 @@ const { MEASURED } = require('../../../ext/tags')
|
|
|
4
4
|
const { storage } = require('../../datadog-core')
|
|
5
5
|
const TracingPlugin = require('../../dd-trace/src/plugins/tracing')
|
|
6
6
|
|
|
7
|
-
const API_KEY = 'langchain.request.api_key'
|
|
8
7
|
const MODEL = 'langchain.request.model'
|
|
9
8
|
const PROVIDER = 'langchain.request.provider'
|
|
10
9
|
const TYPE = 'langchain.request.type'
|
|
11
10
|
|
|
12
11
|
const LangChainHandler = require('./handlers/default')
|
|
13
|
-
const
|
|
14
|
-
const LangChainLLMHandler = require('./handlers/language_models/llm')
|
|
15
|
-
const LangChainChainHandler = require('./handlers/chain')
|
|
12
|
+
const LangChainLanguageModelHandler = require('./handlers/language_models')
|
|
16
13
|
const LangChainEmbeddingHandler = require('./handlers/embedding')
|
|
17
14
|
|
|
18
15
|
class BaseLangChainTracingPlugin extends TracingPlugin {
|
|
@@ -24,9 +21,9 @@ class BaseLangChainTracingPlugin extends TracingPlugin {
|
|
|
24
21
|
super(...arguments)
|
|
25
22
|
|
|
26
23
|
this.handlers = {
|
|
27
|
-
chain: new
|
|
28
|
-
chat_model: new
|
|
29
|
-
llm: new
|
|
24
|
+
chain: new LangChainHandler(this._tracerConfig),
|
|
25
|
+
chat_model: new LangChainLanguageModelHandler(this._tracerConfig),
|
|
26
|
+
llm: new LangChainLanguageModelHandler(this._tracerConfig),
|
|
30
27
|
embedding: new LangChainEmbeddingHandler(this._tracerConfig),
|
|
31
28
|
default: new LangChainHandler(this._tracerConfig)
|
|
32
29
|
}
|
|
@@ -50,7 +47,6 @@ class BaseLangChainTracingPlugin extends TracingPlugin {
|
|
|
50
47
|
const handler = this.handlers[type] || this.handlers.default
|
|
51
48
|
|
|
52
49
|
const instance = ctx.instance
|
|
53
|
-
const apiKey = handler.extractApiKey(instance)
|
|
54
50
|
const provider = handler.extractProvider(instance)
|
|
55
51
|
const model = handler.extractModel(instance)
|
|
56
52
|
|
|
@@ -63,9 +59,8 @@ class BaseLangChainTracingPlugin extends TracingPlugin {
|
|
|
63
59
|
}
|
|
64
60
|
}, false)
|
|
65
61
|
|
|
66
|
-
const tags =
|
|
62
|
+
const tags = {}
|
|
67
63
|
|
|
68
|
-
if (apiKey) tags[API_KEY] = apiKey
|
|
69
64
|
if (provider) tags[PROVIDER] = provider
|
|
70
65
|
if (model) tags[MODEL] = model
|
|
71
66
|
if (type) tags[TYPE] = type
|
|
@@ -81,13 +76,6 @@ class BaseLangChainTracingPlugin extends TracingPlugin {
|
|
|
81
76
|
asyncEnd (ctx) {
|
|
82
77
|
const span = ctx.currentStore.span
|
|
83
78
|
|
|
84
|
-
const { type } = ctx
|
|
85
|
-
|
|
86
|
-
const handler = this.handlers[type] || this.handlers.default
|
|
87
|
-
const tags = handler.getSpanEndTags(ctx, span) || {}
|
|
88
|
-
|
|
89
|
-
span.addTags(tags)
|
|
90
|
-
|
|
91
79
|
span.finish()
|
|
92
80
|
}
|
|
93
81
|
|