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.
Files changed (68) hide show
  1. package/index.d.ts +6 -0
  2. package/package.json +7 -7
  3. package/packages/datadog-code-origin/index.js +3 -0
  4. package/packages/datadog-instrumentations/src/apollo-server.js +14 -3
  5. package/packages/datadog-instrumentations/src/azure-functions.js +5 -0
  6. package/packages/datadog-instrumentations/src/azure-service-bus.js +38 -0
  7. package/packages/datadog-instrumentations/src/fastify.js +17 -0
  8. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  9. package/packages/datadog-instrumentations/src/next.js +17 -18
  10. package/packages/datadog-instrumentations/src/openai.js +13 -114
  11. package/packages/datadog-instrumentations/src/sequelize.js +4 -14
  12. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/tracing.js +6 -38
  13. package/packages/datadog-plugin-azure-functions/src/index.js +57 -28
  14. package/packages/datadog-plugin-azure-service-bus/src/index.js +15 -0
  15. package/packages/datadog-plugin-azure-service-bus/src/producer.js +36 -0
  16. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +24 -23
  17. package/packages/datadog-plugin-google-cloud-vertexai/src/tracing.js +3 -155
  18. package/packages/datadog-plugin-langchain/src/handlers/default.js +0 -18
  19. package/packages/datadog-plugin-langchain/src/handlers/embedding.js +0 -48
  20. package/packages/datadog-plugin-langchain/src/handlers/language_models.js +18 -0
  21. package/packages/datadog-plugin-langchain/src/tracing.js +5 -17
  22. package/packages/datadog-plugin-openai/src/stream-helpers.js +114 -0
  23. package/packages/datadog-plugin-openai/src/tracing.js +38 -0
  24. package/packages/dd-trace/src/appsec/iast/analyzers/cookie-analyzer.js +8 -1
  25. package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +2 -2
  26. package/packages/dd-trace/src/appsec/iast/analyzers/missing-header-analyzer.js +11 -10
  27. package/packages/dd-trace/src/appsec/iast/analyzers/set-cookies-header-interceptor.js +25 -18
  28. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +13 -5
  29. package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +5 -1
  30. package/packages/dd-trace/src/appsec/iast/analyzers/xcontenttype-header-missing-analyzer.js +2 -2
  31. package/packages/dd-trace/src/appsec/iast/iast-plugin.js +4 -0
  32. package/packages/dd-trace/src/appsec/iast/index.js +25 -7
  33. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +79 -21
  34. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +1 -3
  35. package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +0 -4
  36. package/packages/dd-trace/src/appsec/reporter.js +3 -15
  37. package/packages/dd-trace/src/appsec/waf/index.js +20 -1
  38. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +2 -1
  39. package/packages/dd-trace/src/config.js +0 -16
  40. package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +4 -8
  41. package/packages/dd-trace/src/datastreams/schemas/schema_sampler.js +2 -4
  42. package/packages/dd-trace/src/debugger/config.js +16 -0
  43. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +1 -1
  44. package/packages/dd-trace/src/debugger/devtools_client/config.js +2 -6
  45. package/packages/dd-trace/src/debugger/devtools_client/index.js +1 -1
  46. package/packages/dd-trace/src/debugger/devtools_client/log.js +19 -0
  47. package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +1 -1
  48. package/packages/dd-trace/src/debugger/devtools_client/send.js +1 -1
  49. package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +1 -1
  50. package/packages/dd-trace/src/debugger/devtools_client/state.js +1 -1
  51. package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -1
  52. package/packages/dd-trace/src/debugger/index.js +13 -3
  53. package/packages/dd-trace/src/plugins/index.js +1 -0
  54. package/packages/dd-trace/src/plugins/util/ci.js +23 -7
  55. package/packages/dd-trace/src/plugins/util/git.js +53 -18
  56. package/packages/dd-trace/src/plugins/util/tags.js +8 -6
  57. package/packages/dd-trace/src/profiling/profilers/events.js +3 -3
  58. package/packages/dd-trace/src/profiling/profilers/space.js +4 -3
  59. package/packages/dd-trace/src/profiling/profilers/wall.js +5 -4
  60. package/packages/dd-trace/src/remote_config/capabilities.js +2 -1
  61. package/packages/dd-trace/src/remote_config/index.js +2 -0
  62. package/packages/dd-trace/src/remote_config/scheduler.js +2 -1
  63. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +4 -0
  64. package/packages/dd-trace/src/supported-configurations.json +1 -0
  65. package/packages/datadog-plugin-langchain/src/handlers/chain.js +0 -50
  66. package/packages/datadog-plugin-langchain/src/handlers/language_models/chat_model.js +0 -101
  67. package/packages/datadog-plugin-langchain/src/handlers/language_models/index.js +0 -48
  68. 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 { functionName, methodName, httpRequest } = ctx
26
- const store = storage('legacy').getStore()
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
- 'aas.function.name': functionName,
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
- const path = (new URL(httpRequest.url)).pathname
54
- const req = {
55
- method: httpRequest.method,
56
- headers: Object.fromEntries(httpRequest.headers),
57
- url: path
58
- }
59
-
60
- const context = web.patch(req)
61
- context.config = this.config
62
- context.paths = [path]
63
- context.res = { statusCode: result.status }
64
- context.span = ctx.currentStore.span
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
- serverless.finishSpan(context)
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
- constructor () {
227
- this._isInit = false
228
- this.testEnvironmentMetadata = getTestEnvironmentMetadata(TEST_FRAMEWORK_NAME)
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, request, resource, stream } = ctx
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 LangChainChatModelHandler = require('./handlers/language_models/chat_model')
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 LangChainChainHandler(this._tracerConfig),
28
- chat_model: new LangChainChatModelHandler(this._tracerConfig),
29
- llm: new LangChainLLMHandler(this._tracerConfig),
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 = handler.getSpanStartTags(ctx, provider, span) || []
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