dd-trace 5.97.0 → 5.99.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 (175) hide show
  1. package/LICENSE-3rdparty.csv +0 -1
  2. package/ext/tags.js +1 -0
  3. package/index.d.ts +35 -3
  4. package/package.json +48 -46
  5. package/packages/datadog-instrumentations/src/crypto.js +45 -0
  6. package/packages/datadog-instrumentations/src/cucumber.js +65 -3
  7. package/packages/datadog-instrumentations/src/cypress-config.js +153 -53
  8. package/packages/datadog-instrumentations/src/dns.js +24 -56
  9. package/packages/datadog-instrumentations/src/graphql.js +1 -1
  10. package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +74 -0
  11. package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +4 -1
  12. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  13. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +10 -3
  14. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  15. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/modelcontextprotocol-sdk.js +59 -0
  16. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +11 -2
  17. package/packages/datadog-instrumentations/src/jest.js +104 -12
  18. package/packages/datadog-instrumentations/src/mocha/utils.js +8 -0
  19. package/packages/datadog-instrumentations/src/modelcontextprotocol-sdk.js +7 -0
  20. package/packages/datadog-instrumentations/src/pino.js +4 -28
  21. package/packages/datadog-instrumentations/src/playwright-browser-scripts.js +27 -0
  22. package/packages/datadog-instrumentations/src/playwright.js +5 -17
  23. package/packages/datadog-instrumentations/src/redis.js +12 -6
  24. package/packages/datadog-instrumentations/src/stripe.js +38 -24
  25. package/packages/datadog-instrumentations/src/vitest.js +32 -4
  26. package/packages/datadog-instrumentations/src/zlib.js +29 -0
  27. package/packages/datadog-plugin-aws-sdk/src/base.js +2 -3
  28. package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +1 -0
  29. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +1 -0
  30. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -0
  31. package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +1 -0
  32. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +1 -0
  33. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -0
  34. package/packages/datadog-plugin-azure-event-hubs/src/producer.js +8 -15
  35. package/packages/datadog-plugin-azure-service-bus/src/producer.js +4 -9
  36. package/packages/datadog-plugin-cucumber/src/index.js +8 -2
  37. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +114 -6
  38. package/packages/datadog-plugin-cypress/src/index.js +59 -2
  39. package/packages/datadog-plugin-cypress/src/source-map-utils.js +48 -1
  40. package/packages/datadog-plugin-fs/src/index.js +1 -1
  41. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -1
  42. package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +2 -7
  43. package/packages/datadog-plugin-http/src/client.js +1 -1
  44. package/packages/datadog-plugin-http/src/server.js +21 -13
  45. package/packages/datadog-plugin-http2/src/client.js +1 -1
  46. package/packages/datadog-plugin-http2/src/server.js +10 -2
  47. package/packages/datadog-plugin-jest/src/index.js +2 -2
  48. package/packages/datadog-plugin-mocha/src/index.js +1 -2
  49. package/packages/datadog-plugin-modelcontextprotocol-sdk/src/index.js +24 -0
  50. package/packages/datadog-plugin-modelcontextprotocol-sdk/src/tracing.js +55 -0
  51. package/packages/datadog-plugin-mongodb-core/src/index.js +4 -9
  52. package/packages/datadog-plugin-mysql/src/index.js +1 -1
  53. package/packages/datadog-plugin-next/src/index.js +8 -2
  54. package/packages/datadog-plugin-pg/src/index.js +1 -1
  55. package/packages/datadog-plugin-playwright/src/index.js +2 -3
  56. package/packages/datadog-plugin-tedious/src/index.js +1 -1
  57. package/packages/datadog-plugin-vitest/src/index.js +14 -6
  58. package/packages/datadog-plugin-ws/src/close.js +3 -1
  59. package/packages/datadog-plugin-ws/src/producer.js +2 -0
  60. package/packages/datadog-plugin-ws/src/receiver.js +2 -1
  61. package/packages/dd-trace/src/aiguard/channels.js +8 -0
  62. package/packages/dd-trace/src/aiguard/index.js +7 -3
  63. package/packages/dd-trace/src/aiguard/sdk.js +66 -22
  64. package/packages/dd-trace/src/aiguard/tags.js +1 -0
  65. package/packages/dd-trace/src/appsec/blocked_templates.js +4 -3
  66. package/packages/dd-trace/src/appsec/blocking.js +62 -34
  67. package/packages/dd-trace/src/appsec/graphql.js +6 -6
  68. package/packages/dd-trace/src/appsec/index.js +9 -11
  69. package/packages/dd-trace/src/appsec/rasp/command_injection.js +4 -5
  70. package/packages/dd-trace/src/appsec/rasp/lfi.js +8 -4
  71. package/packages/dd-trace/src/appsec/rasp/sql_injection.js +5 -10
  72. package/packages/dd-trace/src/appsec/rasp/ssrf.js +5 -6
  73. package/packages/dd-trace/src/appsec/recommended.json +2438 -13
  74. package/packages/dd-trace/src/appsec/reporter.js +6 -5
  75. package/packages/dd-trace/src/appsec/sdk/set_user.js +1 -1
  76. package/packages/dd-trace/src/appsec/sdk/track_event.js +5 -5
  77. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +6 -10
  78. package/packages/dd-trace/src/appsec/sdk/utils.js +4 -2
  79. package/packages/dd-trace/src/appsec/store.js +50 -0
  80. package/packages/dd-trace/src/appsec/waf/index.js +3 -5
  81. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +2 -2
  82. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +2 -2
  83. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +2 -2
  84. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +2 -2
  85. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +3 -4
  86. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -2
  87. package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +4 -5
  88. package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +3 -4
  89. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +6 -6
  90. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +2 -2
  91. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +2 -2
  92. package/packages/dd-trace/src/config/config-types.d.ts +0 -4
  93. package/packages/dd-trace/src/config/defaults.js +10 -11
  94. package/packages/dd-trace/src/config/generated-config-types.d.ts +14 -8
  95. package/packages/dd-trace/src/config/index.js +49 -32
  96. package/packages/dd-trace/src/config/parsers.js +26 -9
  97. package/packages/dd-trace/src/config/supported-configurations.json +86 -33
  98. package/packages/dd-trace/src/constants.js +1 -0
  99. package/packages/dd-trace/src/debugger/config.js +2 -0
  100. package/packages/dd-trace/src/debugger/devtools_client/send.js +25 -5
  101. package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +5 -2
  102. package/packages/dd-trace/src/encode/0.4.js +11 -11
  103. package/packages/dd-trace/src/encode/span-stats.js +4 -1
  104. package/packages/dd-trace/src/exporters/agent/index.js +0 -1
  105. package/packages/dd-trace/src/exporters/agent/writer.js +1 -2
  106. package/packages/dd-trace/src/exporters/agentless/writer.js +3 -3
  107. package/packages/dd-trace/src/exporters/common/util.js +2 -2
  108. package/packages/dd-trace/src/id.js +2 -0
  109. package/packages/dd-trace/src/index.js +2 -5
  110. package/packages/dd-trace/src/lambda/handler.js +1 -3
  111. package/packages/dd-trace/src/llmobs/plugins/{anthropic.js → anthropic/index.js} +5 -63
  112. package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +106 -0
  113. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chain.js +3 -2
  114. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chat_model.js +3 -2
  115. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/embedding.js +2 -1
  116. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +0 -49
  117. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/vectorstore.js +2 -1
  118. package/packages/dd-trace/src/llmobs/plugins/langchain/messages.js +76 -0
  119. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -26
  120. package/packages/dd-trace/src/llmobs/plugins/modelcontextprotocol-sdk/index.js +68 -0
  121. package/packages/dd-trace/src/llmobs/plugins/modelcontextprotocol-sdk/utils.js +57 -0
  122. package/packages/dd-trace/src/llmobs/sdk.js +2 -2
  123. package/packages/dd-trace/src/log/index.js +0 -10
  124. package/packages/dd-trace/src/openfeature/eval-metrics-hook.js +103 -0
  125. package/packages/dd-trace/src/openfeature/flagging_provider.js +3 -0
  126. package/packages/dd-trace/src/openfeature/remote_config.js +6 -1
  127. package/packages/dd-trace/src/opentelemetry/context_manager.js +6 -4
  128. package/packages/dd-trace/src/opentelemetry/logs/index.js +1 -1
  129. package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +3 -2
  130. package/packages/dd-trace/src/opentelemetry/metrics/index.js +1 -1
  131. package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +3 -2
  132. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +19 -51
  133. package/packages/dd-trace/src/opentelemetry/otlp/protobuf_loader.js +14 -2
  134. package/packages/dd-trace/src/opentelemetry/otlp/trace.proto +358 -0
  135. package/packages/dd-trace/src/opentelemetry/otlp/trace_service.proto +78 -0
  136. package/packages/dd-trace/src/opentelemetry/trace/index.js +70 -0
  137. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +74 -0
  138. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +342 -0
  139. package/packages/dd-trace/src/opentelemetry/tracer.js +9 -11
  140. package/packages/dd-trace/src/opentracing/propagation/text_map.js +17 -10
  141. package/packages/dd-trace/src/opentracing/span.js +1 -1
  142. package/packages/dd-trace/src/opentracing/tracer.js +17 -5
  143. package/packages/dd-trace/src/plugins/index.js +1 -0
  144. package/packages/dd-trace/src/plugins/log_plugin.js +3 -0
  145. package/packages/dd-trace/src/plugins/plugin.js +6 -11
  146. package/packages/dd-trace/src/plugins/storage.js +2 -2
  147. package/packages/dd-trace/src/plugins/tracing.js +22 -5
  148. package/packages/dd-trace/src/plugins/util/test.js +128 -5
  149. package/packages/dd-trace/src/plugins/util/url.js +2 -1
  150. package/packages/dd-trace/src/plugins/util/web.js +6 -88
  151. package/packages/dd-trace/src/profiling/profiler.js +34 -77
  152. package/packages/dd-trace/src/profiling/profilers/event_plugins/crypto.js +32 -0
  153. package/packages/dd-trace/src/profiling/profilers/event_plugins/zlib.js +19 -0
  154. package/packages/dd-trace/src/profiling/profilers/events.js +35 -0
  155. package/packages/dd-trace/src/proxy.js +3 -4
  156. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +17 -13
  157. package/packages/dd-trace/src/service-naming/index.js +1 -1
  158. package/packages/dd-trace/src/service-naming/schemas/definition.js +4 -1
  159. package/packages/dd-trace/src/service-naming/schemas/util.js +15 -1
  160. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +24 -1
  161. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +60 -0
  162. package/packages/dd-trace/src/service-naming/schemas/v0/web.js +21 -1
  163. package/packages/dd-trace/src/service-naming/schemas/v0/websocket.js +5 -0
  164. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +17 -0
  165. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +15 -1
  166. package/packages/dd-trace/src/service-naming/schemas/v1/websocket.js +6 -0
  167. package/packages/dd-trace/src/span_processor.js +1 -2
  168. package/packages/dd-trace/src/span_stats.js +5 -1
  169. package/packages/dd-trace/src/tagger.js +2 -2
  170. package/packages/dd-trace/src/telemetry/send-data.js +5 -7
  171. package/vendor/dist/@apm-js-collab/code-transformer/index.js +28 -6
  172. package/vendor/dist/protobufjs/index.js +1 -1
  173. package/packages/dd-trace/src/log/utils.js +0 -16
  174. package/vendor/dist/ignore/LICENSE +0 -21
  175. package/vendor/dist/ignore/index.js +0 -1
@@ -31,7 +31,7 @@ class SpanStatsEncoder extends AgentEncoder {
31
31
  }
32
32
 
33
33
  _encodeStat (bytes, stat) {
34
- this._encodeMapPrefix(bytes, 14)
34
+ this._encodeMapPrefix(bytes, 15)
35
35
 
36
36
  this._encodeString(bytes, 'Service')
37
37
  const service = stat.Service || DEFAULT_SERVICE_NAME
@@ -76,6 +76,9 @@ class SpanStatsEncoder extends AgentEncoder {
76
76
 
77
77
  this._encodeString(bytes, 'HTTPEndpoint')
78
78
  this._encodeString(bytes, stat.HTTPEndpoint)
79
+
80
+ this._encodeString(bytes, 'srv_src')
81
+ this._encodeString(bytes, stat.srv_src || '')
79
82
  }
80
83
 
81
84
  _encodeBucket (bytes, bucket) {
@@ -24,7 +24,6 @@ class AgentExporter {
24
24
  lookup,
25
25
  protocolVersion,
26
26
  headers,
27
- config,
28
27
  })
29
28
 
30
29
  globalThis[Symbol.for('dd-trace')].beforeExitHandlers.add(this.flush.bind(this))
@@ -20,14 +20,13 @@ class AgentWriter extends BaseWriter {
20
20
  ...args[0],
21
21
  beforeFirstFlush: () => firstFlushChannel.publish(),
22
22
  })
23
- const { prioritySampler, lookup, protocolVersion, headers, config = {} } = args[0]
23
+ const { prioritySampler, lookup, protocolVersion, headers } = args[0]
24
24
  const AgentEncoder = getEncoder(protocolVersion)
25
25
 
26
26
  this._prioritySampler = prioritySampler
27
27
  this._lookup = lookup
28
28
  this._protocolVersion = protocolVersion
29
29
  this._headers = headers
30
- this._config = config
31
30
  this._encoder = new AgentEncoder(this)
32
31
  }
33
32
 
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { getValueFromEnvSources } = require('../../config/helper')
3
+ const getConfig = require('../../config')
4
4
  const log = require('../../log')
5
5
  const request = require('../common/request')
6
6
  const tracerVersion = require('../../../../../package.json').version
@@ -39,7 +39,7 @@ class AgentlessWriter extends BaseWriter {
39
39
  }
40
40
  }
41
41
 
42
- if (!getValueFromEnvSources('DD_API_KEY')) {
42
+ if (!getConfig().apiKey) {
43
43
  this.#apiKeyMissing = true
44
44
  log.error('DD_API_KEY is required for agentless trace intake. Set DD_API_KEY. Traces will not be sent.')
45
45
  }
@@ -108,7 +108,7 @@ class AgentlessWriter extends BaseWriter {
108
108
  return
109
109
  }
110
110
 
111
- const apiKey = getValueFromEnvSources('DD_API_KEY')
111
+ const apiKey = getConfig().apiKey
112
112
  if (!apiKey) {
113
113
  if (!this.#apiKeyMissing) {
114
114
  this.#apiKeyMissing = true
@@ -1,12 +1,12 @@
1
1
  'use strict'
2
2
 
3
- const { getValueFromEnvSources } = require('../../config/helper')
3
+ const getConfig = require('../../config')
4
4
 
5
5
  function safeJSONStringify (value) {
6
6
  return JSON.stringify(
7
7
  value,
8
8
  (key, value) => key === 'dd-api-key' ? undefined : value,
9
- getValueFromEnvSources('DD_TRACE_BEAUTIFUL_LOGS') ? 2 : undefined
9
+ getConfig().DD_TRACE_BEAUTIFUL_LOGS ? 2 : undefined
10
10
  )
11
11
  }
12
12
 
@@ -245,3 +245,5 @@ function writeUInt32BE (buffer, value, offset) {
245
245
  module.exports = function createIdentifier (value, radix) {
246
246
  return new Identifier(value ?? '', radix)
247
247
  }
248
+
249
+ module.exports.Identifier = Identifier
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const { getValueFromEnvSources } = require('./config/helper')
4
- const { isFalse, isTrue } = require('./util')
4
+ const { isFalse } = require('./util')
5
5
 
6
6
  // Global `jest` is only present in Jest workers.
7
7
  const inJestWorker = typeof jest !== 'undefined'
@@ -9,10 +9,7 @@ const inJestWorker = typeof jest !== 'undefined'
9
9
  const ddTraceDisabled = getValueFromEnvSources('DD_TRACE_ENABLED')
10
10
  ? isFalse(getValueFromEnvSources('DD_TRACE_ENABLED'))
11
11
  : String(getValueFromEnvSources('OTEL_TRACES_EXPORTER')).toLowerCase() === 'none'
12
- const shouldUseProxyWhenTracingDisabled =
13
- isTrue(getValueFromEnvSources('DD_DYNAMIC_INSTRUMENTATION_ENABLED')) ||
14
- isTrue(getValueFromEnvSources('DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED'))
15
12
 
16
- module.exports = (ddTraceDisabled && !shouldUseProxyWhenTracingDisabled) || inJestWorker
13
+ module.exports = ddTraceDisabled || inJestWorker
17
14
  ? require('./noop/proxy')
18
15
  : require('./proxy')
@@ -3,7 +3,6 @@
3
3
  const log = require('../log')
4
4
  const { channel } = require('../../../datadog-instrumentations/src/helpers/instrument')
5
5
  const { ERROR_MESSAGE, ERROR_TYPE } = require('../constants')
6
- const { getValueFromEnvSources } = require('../config/helper')
7
6
  const { ImpendingTimeout } = require('./runtime/errors')
8
7
  const { extractContext } = require('./context')
9
8
 
@@ -27,8 +26,7 @@ let __lambdaTimeout
27
26
  function checkTimeout (context) {
28
27
  const remainingTimeInMillis = context.getRemainingTimeInMillis()
29
28
 
30
- let apmFlushDeadline = Number.parseInt(getValueFromEnvSources('DD_APM_FLUSH_DEADLINE_MILLISECONDS')) || 100
31
- apmFlushDeadline = apmFlushDeadline < 0 ? 100 : apmFlushDeadline
29
+ const apmFlushDeadline = tracer._config.DD_APM_FLUSH_DEADLINE_MILLISECONDS
32
30
 
33
31
  __lambdaTimeout = setTimeout(() => {
34
32
  timeoutChannel.publish()
@@ -1,7 +1,8 @@
1
1
  'use strict'
2
2
 
3
- const { UNKNOWN_MODEL_PROVIDER } = require('../constants/tags')
4
- const LLMObsPlugin = require('./base')
3
+ const { UNKNOWN_MODEL_PROVIDER } = require('../../constants/tags')
4
+ const LLMObsPlugin = require('../base')
5
+ const { appendMessage } = require('./util')
5
6
 
6
7
  const ALLOWED_METADATA_KEYS = new Set([
7
8
  'max_tokens',
@@ -144,51 +145,11 @@ class AnthropicLLMObsPlugin extends LLMObsPlugin {
144
145
  const inputMessages = []
145
146
 
146
147
  if (system) {
147
- messages.unshift({ content: system, role: 'system' })
148
+ appendMessage(inputMessages, { role: 'system', content: system })
148
149
  }
149
150
 
150
151
  for (const message of messages) {
151
- const { content, role } = message
152
-
153
- if (typeof content === 'string') {
154
- inputMessages.push({ content, role })
155
- continue
156
- }
157
-
158
- for (const block of content) {
159
- if (block.type === 'text') {
160
- inputMessages.push({ content: block.text, role })
161
- } else if (block.type === 'image') {
162
- inputMessages.push({ content: '([IMAGE DETECTED])', role })
163
- } else if (block.type === 'tool_use') {
164
- const { text, name, id, type } = block
165
- let input = block.input
166
- if (typeof input === 'string') {
167
- input = JSON.parse(input)
168
- }
169
-
170
- const toolCall = {
171
- name,
172
- arguments: input,
173
- toolId: id,
174
- type,
175
- }
176
-
177
- inputMessages.push({ content: text ?? '', role, toolCalls: [toolCall] })
178
- } else if (block.type === 'tool_result') {
179
- const { content } = block
180
- const formattedContent = this.#formatAnthropicToolResultContent(content)
181
- const toolResult = {
182
- result: formattedContent,
183
- toolId: block.tool_use_id,
184
- type: 'tool_result',
185
- }
186
-
187
- inputMessages.push({ content: '', role, toolResults: [toolResult] })
188
- } else {
189
- inputMessages.push({ content: JSON.stringify(block), role })
190
- }
191
- }
152
+ appendMessage(inputMessages, message)
192
153
  }
193
154
 
194
155
  this._tagger.tagLLMIO(span, inputMessages)
@@ -273,25 +234,6 @@ class AnthropicLLMObsPlugin extends LLMObsPlugin {
273
234
 
274
235
  this._tagger.tagMetrics(span, metrics)
275
236
  }
276
-
277
- // maybe can make into a util file
278
- #formatAnthropicToolResultContent (content) {
279
- if (typeof content === 'string') {
280
- return content
281
- } else if (Array.isArray(content)) {
282
- const formattedContent = []
283
- for (const toolResultBlock of content) {
284
- if (toolResultBlock.text) {
285
- formattedContent.push(toolResultBlock.text)
286
- } else if (toolResultBlock.type === 'image') {
287
- formattedContent.push('([IMAGE DETECTED])')
288
- }
289
- }
290
-
291
- return formattedContent.join(',')
292
- }
293
- return JSON.stringify(content)
294
- }
295
237
  }
296
238
 
297
239
  module.exports = AnthropicLLMObsPlugin
@@ -0,0 +1,106 @@
1
+ 'use strict'
2
+
3
+ /**
4
+ * @typedef {{type: 'text', text: string}} TextBlock
5
+ * @typedef {{type: 'image'}} ImageBlock
6
+ * @typedef {{
7
+ * type: 'tool_use', text: string, name: string, id: string, input: string | Record<string, unknown>
8
+ * }} ToolUseBlock
9
+ * @typedef {{
10
+ * type: 'tool_result',
11
+ * tool_use_id: string,
12
+ * content: string | Array<{type: string, text?: string}>
13
+ * }} ToolResultBlock
14
+ *
15
+ * @typedef {{
16
+ * content: string,
17
+ * role: string,
18
+ * toolCalls?: Array<{
19
+ * name: string,
20
+ * arguments: string | Record<string, unknown>,
21
+ * toolId: string,
22
+ * type: string
23
+ * }>,
24
+ * toolResults?: Array<{
25
+ * result: string,
26
+ * toolId: string,
27
+ * type: 'tool_result'
28
+ * }>
29
+ * }} AnthropicLlmObsMessage
30
+ */
31
+
32
+ /**
33
+ * Formats tool result into LLM Observability compatible contents
34
+ * @param {ToolResultBlock['content']} content
35
+ */
36
+ function formatAnthropicToolResultContent (content) {
37
+ if (typeof content === 'string') {
38
+ return content
39
+ } else if (Array.isArray(content)) {
40
+ const formattedContent = []
41
+ for (const toolResultBlock of content) {
42
+ if (toolResultBlock.text) {
43
+ formattedContent.push(toolResultBlock.text)
44
+ } else if (toolResultBlock.type === 'image') {
45
+ formattedContent.push('([IMAGE DETECTED])')
46
+ }
47
+ }
48
+
49
+ return formattedContent.join(',')
50
+ }
51
+ return JSON.stringify(content)
52
+ }
53
+
54
+ /**
55
+ * Normalizes and formats a message into LLM Observability compatible contents.
56
+ * Can be spread into a list of other messages.
57
+ *
58
+ * @param {AnthropicLlmObsMessage[]} messages
59
+ * @param {{ role: string, content: string | Array<TextBlock | ImageBlock | ToolUseBlock | ToolResultBlock> }} message
60
+ * @returns {void}
61
+ */
62
+ function appendMessage (messages, { role, content }) {
63
+ if (typeof content === 'string') {
64
+ messages.push({ content, role })
65
+ return
66
+ }
67
+
68
+ for (const block of content) {
69
+ if (block.type === 'text') {
70
+ messages.push({ content: block.text, role })
71
+ } else if (block.type === 'image') {
72
+ messages.push({ content: '([IMAGE DETECTED])', role })
73
+ } else if (block.type === 'tool_use') {
74
+ const { text, name, id, type } = block
75
+ let input = block.input
76
+ if (typeof input === 'string') {
77
+ input = JSON.parse(input)
78
+ }
79
+
80
+ const toolCall = {
81
+ name,
82
+ arguments: input,
83
+ toolId: id,
84
+ type,
85
+ }
86
+
87
+ messages.push({ content: text ?? '', role, toolCalls: [toolCall] })
88
+ } else if (block.type === 'tool_result') {
89
+ const { content } = block
90
+ const formattedContent = formatAnthropicToolResultContent(content)
91
+ const toolResult = {
92
+ result: formattedContent,
93
+ toolId: block.tool_use_id,
94
+ type: 'tool_result',
95
+ }
96
+
97
+ messages.push({ content: '', role, toolResults: [toolResult] })
98
+ } else {
99
+ messages.push({ content: JSON.stringify(block), role })
100
+ }
101
+ }
102
+ }
103
+
104
+ module.exports = {
105
+ appendMessage,
106
+ }
@@ -1,16 +1,17 @@
1
1
  'use strict'
2
2
 
3
3
  const { spanHasError } = require('../../../util')
4
+ const { formatIO } = require('../messages')
4
5
  const LangChainLLMObsHandler = require('.')
5
6
 
6
7
  class LangChainLLMObsChainHandler extends LangChainLLMObsHandler {
7
8
  setMetaTags ({ span, inputs, results }) {
8
9
  let input
9
10
  if (inputs) {
10
- input = this.formatIO(inputs)
11
+ input = formatIO(inputs)
11
12
  }
12
13
 
13
- const output = !results || spanHasError(span) ? '' : this.formatIO(results)
14
+ const output = !results || spanHasError(span) ? '' : formatIO(results)
14
15
 
15
16
  // chain spans will always be workflows
16
17
  this._tagger.tagTextIO(span, input, output)
@@ -2,6 +2,7 @@
2
2
 
3
3
  const LLMObsTagger = require('../../../tagger')
4
4
  const { spanHasError } = require('../../../util')
5
+ const { getRole } = require('../messages')
5
6
  const LangChainLLMObsHandler = require('.')
6
7
 
7
8
  const LLM = 'llm'
@@ -22,7 +23,7 @@ class LangChainLLMObsChatModelHandler extends LangChainLLMObsHandler {
22
23
  for (const messageSet of inputs) {
23
24
  for (const message of messageSet) {
24
25
  const content = message.content || ''
25
- const role = this.getRole(message)
26
+ const role = getRole(message)
26
27
  inputMessages.push({ content, role })
27
28
  }
28
29
  }
@@ -54,7 +55,7 @@ class LangChainLLMObsChatModelHandler extends LangChainLLMObsHandler {
54
55
  for (const messageSet of results.generations) {
55
56
  for (const chatCompletion of messageSet) {
56
57
  const chatCompletionMessage = chatCompletion.message
57
- const role = this.getRole(chatCompletionMessage)
58
+ const role = getRole(chatCompletionMessage)
58
59
  const content = chatCompletionMessage.text || ''
59
60
  const toolCalls = this.extractToolCalls(chatCompletionMessage)
60
61
  outputMessages.push({ content, role, toolCalls })
@@ -2,6 +2,7 @@
2
2
 
3
3
  const LLMObsTagger = require('../../../tagger')
4
4
  const { spanHasError } = require('../../../util')
5
+ const { formatIO } = require('../messages')
5
6
  const LangChainLLMObsHandler = require('.')
6
7
 
7
8
  class LangChainLLMObsEmbeddingHandler extends LangChainLLMObsHandler {
@@ -10,7 +11,7 @@ class LangChainLLMObsEmbeddingHandler extends LangChainLLMObsHandler {
10
11
  let embeddingInput, embeddingOutput
11
12
 
12
13
  if (isWorkflow) {
13
- embeddingInput = this.formatIO(inputs)
14
+ embeddingInput = formatIO(inputs)
14
15
  } else {
15
16
  const input = Array.isArray(inputs) ? inputs : [inputs]
16
17
  embeddingInput = input.map(doc => ({ text: doc }))
@@ -1,11 +1,5 @@
1
1
  'use strict'
2
2
 
3
- const ROLE_MAPPINGS = {
4
- human: 'user',
5
- ai: 'assistant',
6
- system: 'system',
7
- }
8
-
9
3
  class LangChainLLMObsHandler {
10
4
  constructor (tagger) {
11
5
  /** @type {import('../../../tagger')} */
@@ -18,38 +12,6 @@ class LangChainLLMObsHandler {
18
12
 
19
13
  setMetaTags () {}
20
14
 
21
- formatIO (messages) {
22
- if (messages.constructor.name === 'Object') { // plain JSON
23
- const formatted = {}
24
- for (const [key, value] of Object.entries(messages)) {
25
- formatted[key] = this.formatIO(value)
26
- }
27
-
28
- return formatted
29
- } else if (Array.isArray(messages)) {
30
- return messages.map(message => this.formatIO(message))
31
- } // either a BaseMesage type or a string
32
- return this.getContentFromMessage(messages)
33
- }
34
-
35
- getContentFromMessage (message) {
36
- if (typeof message === 'string') {
37
- return message
38
- }
39
- try {
40
- const messageContent = {
41
- content: message.content || '',
42
- }
43
-
44
- const role = this.getRole(message)
45
- if (role) messageContent.role = role
46
-
47
- return messageContent
48
- } catch {
49
- return JSON.stringify(message)
50
- }
51
- }
52
-
53
15
  checkTokenUsageChatOrLLMResult (results) {
54
16
  const llmOutput = results.llmOutput
55
17
  const tokens = {
@@ -90,17 +52,6 @@ class LangChainLLMObsHandler {
90
52
  runId: runIdBase,
91
53
  }
92
54
  }
93
-
94
- getRole (message) {
95
- if (message.role) return ROLE_MAPPINGS[message.role] || message.role
96
-
97
- const type = (
98
- (typeof message.getType === 'function' && message.getType()) ||
99
- (typeof message._getType === 'function' && message._getType())
100
- )
101
-
102
- return ROLE_MAPPINGS[type] || type
103
- }
104
55
  }
105
56
 
106
57
  module.exports = LangChainLLMObsHandler
@@ -1,11 +1,12 @@
1
1
  'use strict'
2
2
 
3
3
  const { spanHasError } = require('../../../util')
4
+ const { formatIO } = require('../messages')
4
5
  const LangChainLLMObsHandler = require('.')
5
6
 
6
7
  class LangChainLLMObsVectorStoreHandler extends LangChainLLMObsHandler {
7
8
  setMetaTags ({ span, inputs, results }) {
8
- const input = this.formatIO(inputs)
9
+ const input = formatIO(inputs)
9
10
  if (spanHasError(span)) {
10
11
  this._tagger.tagRetrievalIO(span, input)
11
12
  return
@@ -0,0 +1,76 @@
1
+ 'use strict'
2
+
3
+ const ROLE_MAPPINGS = {
4
+ human: 'user',
5
+ ai: 'assistant',
6
+ system: 'system',
7
+ }
8
+
9
+ function getRole (message) {
10
+ if (message.role) return ROLE_MAPPINGS[message.role] || message.role
11
+
12
+ const type = (
13
+ (typeof message.getType === 'function' && message.getType()) ||
14
+ (typeof message._getType === 'function' && message._getType())
15
+ )
16
+
17
+ return ROLE_MAPPINGS[type] || type
18
+ }
19
+
20
+ function getContentFromMessage (message) {
21
+ if (typeof message === 'string') {
22
+ return message
23
+ }
24
+ try {
25
+ const messageContent = {
26
+ content: message.content || '',
27
+ }
28
+
29
+ const role = getRole(message)
30
+ if (role) messageContent.role = role
31
+
32
+ return messageContent
33
+ } catch {
34
+ return JSON.stringify(message)
35
+ }
36
+ }
37
+
38
+ function isBaseMessage (data) {
39
+ return typeof data._getType === 'function' || typeof data.getType === 'function'
40
+ }
41
+
42
+ function formatIO (data) {
43
+ if (data == null) return ''
44
+
45
+ if (typeof data === 'string' || typeof data === 'number' || typeof data === 'boolean') {
46
+ return data
47
+ }
48
+
49
+ if (data.constructor?.name === 'Object') {
50
+ const formatted = {}
51
+ for (const [key, value] of Object.entries(data)) {
52
+ formatted[key] = formatIO(value)
53
+ }
54
+ return formatted
55
+ }
56
+
57
+ if (Array.isArray(data)) {
58
+ return data.map(item => formatIO(item))
59
+ }
60
+
61
+ // Only duck-typed BaseMessage instances collapse to { content, role }.
62
+ // Other class instances (e.g. LangChain Document) preserve their shape via JSON.stringify,
63
+ // otherwise they'd reduce to { content: '' } and lose data.
64
+ if (isBaseMessage(data)) return getContentFromMessage(data)
65
+
66
+ try {
67
+ return JSON.stringify(data)
68
+ } catch {
69
+ return String(data)
70
+ }
71
+ }
72
+
73
+ module.exports = {
74
+ getRole,
75
+ formatIO,
76
+ }
@@ -1,36 +1,11 @@
1
1
  'use strict'
2
2
 
3
3
  const LLMObsPlugin = require('../base')
4
+ const { formatIO } = require('../langchain/messages')
4
5
  const { spanHasError } = require('../../util')
5
6
 
6
7
  const streamDataMap = new WeakMap()
7
8
 
8
- function formatIO (data) {
9
- if (data == null) return ''
10
-
11
- if (typeof data === 'string' || typeof data === 'number' || typeof data === 'boolean') {
12
- return data
13
- }
14
-
15
- if (data.constructor?.name === 'Object') {
16
- const formatted = {}
17
- for (const [key, value] of Object.entries(data)) {
18
- formatted[key] = formatIO(value)
19
- }
20
- return formatted
21
- }
22
-
23
- if (Array.isArray(data)) {
24
- return data.map(item => formatIO(item))
25
- }
26
-
27
- try {
28
- return JSON.stringify(data)
29
- } catch {
30
- return String(data)
31
- }
32
- }
33
-
34
9
  class PregelStreamLLMObsPlugin extends LLMObsPlugin {
35
10
  static id = 'llmobs_langgraph_pregel_stream'
36
11
  static integration = 'langgraph'
@@ -0,0 +1,68 @@
1
+ 'use strict'
2
+
3
+ const LLMObsPlugin = require('../base')
4
+ const { formatInput, formatOutput } = require('./utils')
5
+
6
+ class McpToolCallLLMObsPlugin extends LLMObsPlugin {
7
+ static id = 'llmobs_mcp_tool_call'
8
+ static integration = 'modelcontextprotocol-sdk'
9
+ static prefix = 'tracing:orchestrion:@modelcontextprotocol/sdk:Client_callTool'
10
+
11
+ getLLMObsSpanRegisterOptions (ctx) {
12
+ const params = ctx.arguments?.[0]
13
+ const toolName = params?.name || 'unknown_tool'
14
+
15
+ return {
16
+ kind: 'tool',
17
+ name: `MCP Client Tool Call: ${toolName}`,
18
+ }
19
+ }
20
+
21
+ setLLMObsTags (ctx) {
22
+ const span = ctx.currentStore?.span
23
+ if (!span) return
24
+
25
+ const params = ctx.arguments?.[0]
26
+ const toolName = params?.name
27
+ const toolArguments = params?.arguments
28
+
29
+ const spanTags = { mcp_tool_kind: 'client' }
30
+
31
+ const serverVersion = ctx.self?.getServerVersion?.()
32
+ if (serverVersion) {
33
+ if (serverVersion.name) spanTags.mcp_server_name = serverVersion.name
34
+ if (serverVersion.version) spanTags.mcp_server_version = serverVersion.version
35
+ if (serverVersion.title) spanTags.mcp_server_title = serverVersion.title
36
+ }
37
+
38
+ this._tagger.tagSpanTags(span, spanTags)
39
+
40
+ const hasError = ctx.error || ctx.result?.isError
41
+ const input = formatInput(toolName, toolArguments)
42
+ const output = hasError ? undefined : formatOutput(ctx.result)
43
+
44
+ this._tagger.tagTextIO(span, input, output)
45
+ }
46
+ }
47
+
48
+ class McpListToolsLLMObsPlugin extends LLMObsPlugin {
49
+ static id = 'llmobs_mcp_list_tools'
50
+ static integration = 'modelcontextprotocol-sdk'
51
+ static prefix = 'tracing:orchestrion:@modelcontextprotocol/sdk:Client_listTools'
52
+
53
+ getLLMObsSpanRegisterOptions () {
54
+ return {
55
+ kind: 'task',
56
+ name: 'MCP Client List Tools',
57
+ }
58
+ }
59
+
60
+ setLLMObsTags (ctx) {
61
+ const span = ctx.currentStore?.span
62
+ if (!span || ctx.error) return
63
+
64
+ this._tagger.tagTextIO(span, null, JSON.stringify(ctx.result))
65
+ }
66
+ }
67
+
68
+ module.exports = [McpToolCallLLMObsPlugin, McpListToolsLLMObsPlugin]