dd-trace 6.12.0 → 6.14.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 (251) hide show
  1. package/LICENSE-3rdparty.csv +3 -1
  2. package/ci/init.js +8 -3
  3. package/ci/test-optimization-validation/ci-package-scripts.js +2 -2
  4. package/ci/test-optimization-validation/ci-remediation.js +6 -6
  5. package/ci/test-optimization-validation/command-blocker.js +7 -5
  6. package/ci/test-optimization-validation/framework-adapters/cucumber.js +1 -1
  7. package/ci/test-optimization-validation/framework-adapters/cypress.js +9 -11
  8. package/ci/test-optimization-validation/framework-adapters/vitest.js +3 -3
  9. package/ci/test-optimization-validation/package-check.js +1 -1
  10. package/ci/test-optimization-validation/report-writer.js +7 -5
  11. package/ci/test-optimization-validation/scenarios/ci-wiring.js +5 -4
  12. package/ci/vitest-no-worker-init-runner-setup.mjs +8 -0
  13. package/ci/vitest-no-worker-init-setup.mjs +13 -13
  14. package/ci/vitest-no-worker-init-v5-setup.mjs +4 -0
  15. package/index.d.ts +127 -26
  16. package/package.json +22 -19
  17. package/packages/datadog-esbuild/index.js +3 -1
  18. package/packages/datadog-instrumentations/src/bunyan.js +4 -2
  19. package/packages/datadog-instrumentations/src/cypress-config.js +19 -17
  20. package/packages/datadog-instrumentations/src/fs.js +24 -20
  21. package/packages/datadog-instrumentations/src/google-cloud-vertexai.js +2 -2
  22. package/packages/datadog-instrumentations/src/graphql.js +16 -16
  23. package/packages/datadog-instrumentations/src/helpers/bunyan.js +12 -3
  24. package/packages/datadog-instrumentations/src/helpers/graphql-jit-runtime.js +504 -0
  25. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  26. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +266 -0
  27. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +1 -2
  28. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +95 -30
  29. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +0 -25
  30. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +83 -0
  31. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +85 -1
  32. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  33. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mercurius.js +30 -12
  34. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +13 -0
  35. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +279 -0
  36. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +12 -1
  37. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +562 -27
  38. package/packages/datadog-instrumentations/src/jest.js +165 -38
  39. package/packages/datadog-instrumentations/src/knex.js +76 -0
  40. package/packages/datadog-instrumentations/src/mariadb-bundle.js +1597 -0
  41. package/packages/datadog-instrumentations/src/mariadb.js +13 -0
  42. package/packages/datadog-instrumentations/src/mocha/common.js +59 -2
  43. package/packages/datadog-instrumentations/src/mocha/main.js +25 -15
  44. package/packages/datadog-instrumentations/src/mocha/utils.js +2 -1
  45. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +98 -0
  46. package/packages/datadog-instrumentations/src/mocha/worker.js +12 -8
  47. package/packages/datadog-instrumentations/src/net.js +14 -17
  48. package/packages/datadog-instrumentations/src/next.js +212 -62
  49. package/packages/datadog-instrumentations/src/openai.js +42 -40
  50. package/packages/datadog-instrumentations/src/pg.js +15 -0
  51. package/packages/datadog-instrumentations/src/pino.js +15 -4
  52. package/packages/datadog-instrumentations/src/playwright.js +137 -27
  53. package/packages/datadog-instrumentations/src/rum-browser-scripts.js +40 -0
  54. package/packages/datadog-instrumentations/src/sequelize.js +94 -2
  55. package/packages/datadog-instrumentations/src/undici.js +53 -4
  56. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +44 -7
  57. package/packages/datadog-instrumentations/src/vitest-main.js +92 -9
  58. package/packages/datadog-instrumentations/src/vitest-worker.js +82 -14
  59. package/packages/datadog-instrumentations/src/webdriverio.js +986 -1
  60. package/packages/datadog-instrumentations/src/winston.js +6 -3
  61. package/packages/datadog-plugin-amqp10/src/consumer.js +3 -1
  62. package/packages/datadog-plugin-amqp10/src/producer.js +3 -1
  63. package/packages/datadog-plugin-amqp10/src/util.js +2 -1
  64. package/packages/datadog-plugin-avsc/src/schema_iterator.js +6 -1
  65. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +16 -5
  66. package/packages/datadog-plugin-cassandra-driver/src/index.js +8 -3
  67. package/packages/datadog-plugin-cucumber/src/index.js +0 -2
  68. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +262 -48
  69. package/packages/datadog-plugin-cypress/src/support.js +1 -0
  70. package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +4 -1
  71. package/packages/datadog-plugin-graphql/src/execute.js +998 -196
  72. package/packages/datadog-plugin-graphql/src/index.js +27 -7
  73. package/packages/datadog-plugin-graphql/src/jit.js +136 -0
  74. package/packages/datadog-plugin-graphql/src/request.js +18 -27
  75. package/packages/datadog-plugin-graphql/src/resolve-error.js +108 -0
  76. package/packages/datadog-plugin-graphql/src/tools/signature.js +1 -2
  77. package/packages/datadog-plugin-graphql/src/tools/transforms.js +45 -3
  78. package/packages/datadog-plugin-graphql/src/utils.js +208 -120
  79. package/packages/datadog-plugin-graphql/src/validate.js +10 -23
  80. package/packages/datadog-plugin-http/src/client.js +2 -15
  81. package/packages/datadog-plugin-http2/src/client.js +2 -15
  82. package/packages/datadog-plugin-jest/src/index.js +0 -6
  83. package/packages/datadog-plugin-langchain/src/tracing.js +7 -0
  84. package/packages/datadog-plugin-mocha/src/index.js +403 -75
  85. package/packages/datadog-plugin-mongodb-core/src/query.js +6 -0
  86. package/packages/datadog-plugin-mysql/src/index.js +41 -15
  87. package/packages/datadog-plugin-net/src/tcp.js +3 -1
  88. package/packages/datadog-plugin-next/src/index.js +63 -29
  89. package/packages/datadog-plugin-openai-agents/src/integration.js +15 -11
  90. package/packages/datadog-plugin-playwright/src/index.js +176 -28
  91. package/packages/datadog-plugin-undici/src/index.js +29 -56
  92. package/packages/datadog-plugin-vitest/src/index.js +6 -12
  93. package/packages/dd-trace/src/aiguard/evaluation.js +96 -21
  94. package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -3
  95. package/packages/dd-trace/src/aiguard/messages/anthropic.js +29 -10
  96. package/packages/dd-trace/src/aiguard/messages/openai.js +8 -1
  97. package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +20 -6
  98. package/packages/dd-trace/src/aiguard/noop.js +9 -1
  99. package/packages/dd-trace/src/aiguard/redaction.js +183 -0
  100. package/packages/dd-trace/src/aiguard/sdk.js +7 -2
  101. package/packages/dd-trace/src/aiguard/tags.js +2 -0
  102. package/packages/dd-trace/src/appsec/api_security/index.js +23 -6
  103. package/packages/dd-trace/src/appsec/api_security/sampler.js +99 -40
  104. package/packages/dd-trace/src/appsec/graphql.js +6 -2
  105. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +3 -3
  106. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +2 -1
  107. package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +8 -2
  108. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +11 -36
  109. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +1 -1
  110. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +10 -9
  111. package/packages/dd-trace/src/appsec/lambda.js +44 -26
  112. package/packages/dd-trace/src/appsec/rule_manager.js +2 -0
  113. package/packages/dd-trace/src/ci-visibility/encode/json-encoder.js +32 -2
  114. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +8 -8
  115. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +6 -4
  116. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +20 -4
  117. package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +7 -150
  118. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +7 -6
  119. package/packages/dd-trace/src/ci-visibility/exporters/agents.js +54 -0
  120. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/writer.js +1 -2
  121. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +397 -234
  122. package/packages/dd-trace/src/ci-visibility/exporters/limits.js +9 -0
  123. package/packages/dd-trace/src/ci-visibility/exporters/request.js +323 -31
  124. package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +42 -0
  125. package/packages/dd-trace/src/ci-visibility/final-flush.js +21 -0
  126. package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +57 -18
  127. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +0 -3
  128. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +164 -71
  129. package/packages/dd-trace/src/ci-visibility/requests/video-request.js +176 -0
  130. package/packages/dd-trace/src/ci-visibility/telemetry.js +10 -7
  131. package/packages/dd-trace/src/ci-visibility/test-video.js +61 -0
  132. package/packages/dd-trace/src/config/generated-config-types.d.ts +10 -0
  133. package/packages/dd-trace/src/config/index.js +75 -14
  134. package/packages/dd-trace/src/config/parsers.js +11 -1
  135. package/packages/dd-trace/src/config/stable.js +3 -3
  136. package/packages/dd-trace/src/config/supported-configurations.json +47 -2
  137. package/packages/dd-trace/src/crashtracking/crashtracker.js +82 -18
  138. package/packages/dd-trace/src/debugger/config.js +12 -1
  139. package/packages/dd-trace/src/debugger/constants.js +1 -0
  140. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +2 -1
  141. package/packages/dd-trace/src/debugger/devtools_client/request-options.js +33 -0
  142. package/packages/dd-trace/src/debugger/devtools_client/send.js +7 -39
  143. package/packages/dd-trace/src/debugger/devtools_client/status.js +11 -6
  144. package/packages/dd-trace/src/debugger/devtools_client/tags.js +41 -0
  145. package/packages/dd-trace/src/debugger/index.js +23 -3
  146. package/packages/dd-trace/src/dogstatsd.js +532 -71
  147. package/packages/dd-trace/src/encode/0.4-cross-payload.js +170 -0
  148. package/packages/dd-trace/src/encode/0.4.js +5 -5
  149. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +9 -8
  150. package/packages/dd-trace/src/evp_proxy/direct.js +27 -23
  151. package/packages/dd-trace/src/exporters/agent/index.js +24 -3
  152. package/packages/dd-trace/src/exporters/agent/writer.js +28 -11
  153. package/packages/dd-trace/src/exporters/agentless/index.js +7 -9
  154. package/packages/dd-trace/src/exporters/agentless/writer.js +100 -118
  155. package/packages/dd-trace/src/exporters/common/agents.js +28 -6
  156. package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -2
  157. package/packages/dd-trace/src/exporters/common/final-flush-request-tracker.js +191 -0
  158. package/packages/dd-trace/src/exporters/common/request.js +31 -11
  159. package/packages/dd-trace/src/exporters/common/url.js +40 -1
  160. package/packages/dd-trace/src/exporters/common/writer.js +31 -7
  161. package/packages/dd-trace/src/exporters/span-stats/index.js +23 -4
  162. package/packages/dd-trace/src/external-logger/src/index.js +4 -3
  163. package/packages/dd-trace/src/flush.js +105 -0
  164. package/packages/dd-trace/src/id.js +14 -0
  165. package/packages/dd-trace/src/index.js +5 -2
  166. package/packages/dd-trace/src/llmobs/experiments/client.js +4 -0
  167. package/packages/dd-trace/src/llmobs/experiments/dataset.js +52 -2
  168. package/packages/dd-trace/src/llmobs/experiments/experiment.js +420 -105
  169. package/packages/dd-trace/src/llmobs/experiments/index.js +54 -42
  170. package/packages/dd-trace/src/llmobs/experiments/noop.js +39 -8
  171. package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
  172. package/packages/dd-trace/src/llmobs/experiments/util.js +21 -0
  173. package/packages/dd-trace/src/llmobs/index.js +111 -48
  174. package/packages/dd-trace/src/llmobs/noop.js +2 -1
  175. package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +22 -71
  176. package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +23 -13
  177. package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +8 -4
  178. package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +6 -3
  179. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +7 -0
  180. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +13 -6
  181. package/packages/dd-trace/src/llmobs/plugins/openai-agents/utils.js +5 -7
  182. package/packages/dd-trace/src/llmobs/util.js +0 -33
  183. package/packages/dd-trace/src/llmobs/writers/base.js +106 -53
  184. package/packages/dd-trace/src/log/index.js +7 -1
  185. package/packages/dd-trace/src/log-submission/log-submission-plugin.js +249 -0
  186. package/packages/dd-trace/src/noop/span.js +5 -0
  187. package/packages/dd-trace/src/openfeature/writers/base.js +14 -12
  188. package/packages/dd-trace/src/openfeature/writers/exposures.js +8 -1
  189. package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
  190. package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +45 -3
  191. package/packages/dd-trace/src/opentelemetry/logs/index.js +9 -25
  192. package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +7 -4
  193. package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +14 -4
  194. package/packages/dd-trace/src/opentelemetry/metrics/index.js +18 -23
  195. package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +8 -0
  196. package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +18 -6
  197. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +14 -2
  198. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +25 -5
  199. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +61 -25
  200. package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +19 -4
  201. package/packages/dd-trace/src/opentelemetry/resource-attributes.js +55 -0
  202. package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
  203. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +10 -0
  204. package/packages/dd-trace/src/opentelemetry/tracer.js +26 -2
  205. package/packages/dd-trace/src/opentracing/propagation/text_map.js +494 -351
  206. package/packages/dd-trace/src/opentracing/span.js +28 -0
  207. package/packages/dd-trace/src/otel-thread-ctx.js +23 -5
  208. package/packages/dd-trace/src/payload-tagging/index.js +2 -1
  209. package/packages/dd-trace/src/plugin_manager.js +2 -0
  210. package/packages/dd-trace/src/plugins/ci_plugin.js +1 -6
  211. package/packages/dd-trace/src/plugins/tracing.js +12 -10
  212. package/packages/dd-trace/src/plugins/util/ci.js +3 -1
  213. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +16 -15
  214. package/packages/dd-trace/src/plugins/util/status-validator.js +98 -0
  215. package/packages/dd-trace/src/plugins/util/test.js +157 -41
  216. package/packages/dd-trace/src/plugins/util/url.js +2 -2
  217. package/packages/dd-trace/src/plugins/util/web.js +6 -74
  218. package/packages/dd-trace/src/profiling/config.js +1 -0
  219. package/packages/dd-trace/src/profiling/oom.js +11 -5
  220. package/packages/dd-trace/src/profiling/profiler.js +30 -13
  221. package/packages/dd-trace/src/profiling/profilers/events.js +3 -3
  222. package/packages/dd-trace/src/profiling/profilers/space.js +38 -11
  223. package/packages/dd-trace/src/profiling/profilers/wall.js +14 -14
  224. package/packages/dd-trace/src/profiling/webspan-utils.js +7 -4
  225. package/packages/dd-trace/src/proxy.js +108 -10
  226. package/packages/dd-trace/src/remote_config/index.js +319 -101
  227. package/packages/dd-trace/src/remote_config/scheduler.js +37 -12
  228. package/packages/dd-trace/src/runtime_metrics/client.js +37 -8
  229. package/packages/dd-trace/src/runtime_metrics/index.js +5 -0
  230. package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +10 -1
  231. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +22 -7
  232. package/packages/dd-trace/src/sampling_rule.js +36 -9
  233. package/packages/dd-trace/src/serverless/telemetry-delivery-tracker.js +51 -0
  234. package/packages/dd-trace/src/serverless/vercel.js +155 -0
  235. package/packages/dd-trace/src/serverless.js +45 -21
  236. package/packages/dd-trace/src/service-naming/schemas/util.js +1 -1
  237. package/packages/dd-trace/src/span_stats.js +64 -31
  238. package/packages/dd-trace/src/startup-log.js +3 -2
  239. package/packages/dd-trace/src/telemetry/agentless-url.js +28 -0
  240. package/packages/dd-trace/src/telemetry/send-data.js +18 -16
  241. package/packages/dd-trace/src/telemetry/session-propagation.js +3 -2
  242. package/packages/dd-trace/src/tracer.js +26 -5
  243. package/packages/dd-trace/src/tracer_metadata.js +11 -3
  244. package/packages/dd-trace/src/web-tags-cache.js +67 -13
  245. package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
  246. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  247. package/vendor/dist/protobufjs/index.js +1 -1
  248. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  249. package/packages/datadog-instrumentations/src/playwright-browser-scripts.js +0 -27
  250. package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +0 -53
  251. package/packages/dd-trace/src/encode/agentless-json.js +0 -209
@@ -4,7 +4,6 @@ const { channel } = require('dc-polyfill')
4
4
  const BaseLLMObsPlugin = require('../base')
5
5
  const { getModelProvider } = require('../../../../../datadog-plugin-ai/src/utils')
6
6
 
7
- const toolCreationCh = channel('tracing:orchestrion:ai:tool:start')
8
7
  const setAttributesCh = channel('dd-trace:vercel-ai:span:setAttributes')
9
8
 
10
9
  const { MODEL_NAME, MODEL_PROVIDER, NAME } = require('../../constants/tags')
@@ -21,16 +20,10 @@ const {
21
20
  getTelemetryMetadata,
22
21
  } = require('./util')
23
22
 
24
- /**
25
- * @typedef {Record<string, unknown> & { description?: string, id?: string }} AvailableToolArgs
26
- */
27
-
28
23
  /**
29
24
  * @typedef {string | number | boolean | null | undefined | string[] | number[] | boolean[]} TagValue
30
25
  * @typedef {Record<string, TagValue>} SpanTags
31
26
  *
32
- * @typedef {{ name?: string, description?: string }} ToolForModel
33
- *
34
27
  * @typedef {{ type: 'text' | 'reasoning' | 'redacted-reasoning', text?: string, data?: string }} TextPart
35
28
  * @typedef {{ type: 'tool-call', toolName: string, toolCallId: string, args?: unknown, input?: unknown }} ToolCallPart
36
29
  * @typedef {(
@@ -76,58 +69,14 @@ class DdTelemetryPlugin extends BaseLLMObsPlugin {
76
69
  static integration = 'ai'
77
70
  static prefix = 'tracing:dd-trace:vercel-ai'
78
71
 
79
- /**
80
- * The available tools within the runtime scope of this integration.
81
- * This essentially acts as a global registry for all tools made through the Vercel AI SDK.
82
- * @type {Set<AvailableToolArgs>}
83
- */
84
- #availableTools
85
-
86
- /**
87
- * A mapping of tool call IDs to tool names.
88
- * This is used to map the tool call ID to the tool name for the output message.
89
- * @type {Record<string, string>}
90
- */
91
- #toolCallIdsToName
92
-
93
72
  constructor (...args) {
94
73
  super(...args)
95
74
 
96
- this.#toolCallIdsToName = {}
97
- this.#availableTools = new Set()
98
- toolCreationCh.subscribe(ctx => {
99
- const toolArgs = ctx.arguments
100
- const tool = toolArgs[0] ?? {}
101
- this.#availableTools.add(tool)
102
- })
103
-
104
75
  setAttributesCh.subscribe(({ ctx, attributes }) => {
105
76
  Object.assign(ctx.attributes, attributes)
106
77
  })
107
78
  }
108
79
 
109
- /**
110
- * Does a best-effort attempt to find the right tool name for the given tool description.
111
- * This is because the Vercel AI SDK does not tag tools by name properly, but
112
- * rather by the index they were passed in. Tool names appear nowhere in the span tags.
113
- *
114
- * We use the tool description as the next best identifier for a tool.
115
- *
116
- * @param {string} toolName
117
- * @param {string | undefined} toolDescription
118
- * @returns {string | undefined}
119
- */
120
- findToolName (toolName, toolDescription) {
121
- if (Number.isNaN(Number.parseInt(toolName, 10))) return toolName
122
-
123
- for (const availableTool of this.#availableTools) {
124
- const description = availableTool.description
125
- if (description === toolDescription && availableTool.id) {
126
- return availableTool.id
127
- }
128
- }
129
- }
130
-
131
80
  /**
132
81
  * @override
133
82
  */
@@ -232,7 +181,13 @@ class DdTelemetryPlugin extends BaseLLMObsPlugin {
232
181
  const lastUserPrompt =
233
182
  promptInfo.prompt ??
234
183
  promptInfo.messages.reverse().find(message => message.role === 'user')?.content
235
- const prompt = Array.isArray(lastUserPrompt) ? lastUserPrompt.map(part => part.text ?? '').join('') : lastUserPrompt
184
+ let prompt = lastUserPrompt
185
+ if (Array.isArray(lastUserPrompt)) {
186
+ prompt = ''
187
+ for (const part of lastUserPrompt) {
188
+ if (typeof part.text === 'string') prompt += part.text
189
+ }
190
+ }
236
191
 
237
192
  const output = tags['ai.response.object']
238
193
 
@@ -248,7 +203,13 @@ class DdTelemetryPlugin extends BaseLLMObsPlugin {
248
203
  const lastUserPrompt =
249
204
  promptInfo.prompt ??
250
205
  promptInfo.messages.reverse().find(message => message.role === 'user')?.content
251
- const prompt = Array.isArray(lastUserPrompt) ? lastUserPrompt.map(part => part.text ?? '').join('') : lastUserPrompt
206
+ let prompt = lastUserPrompt
207
+ if (Array.isArray(lastUserPrompt)) {
208
+ prompt = ''
209
+ for (const part of lastUserPrompt) {
210
+ if (typeof part.text === 'string') prompt += part.text
211
+ }
212
+ }
252
213
 
253
214
  const output = tags['ai.response.text']
254
215
 
@@ -263,16 +224,14 @@ class DdTelemetryPlugin extends BaseLLMObsPlugin {
263
224
  * @param {SpanTags} tags
264
225
  */
265
226
  setLLMOperationTags (span, tags) {
266
- const toolsForModel = tags['ai.prompt.tools']?.map(getJsonStringValue)
267
-
268
227
  const inputMessages = getJsonStringValue(tags['ai.prompt.messages'], [])
269
228
  const parsedInputMessages = []
270
229
  for (const message of inputMessages) {
271
- const formattedMessages = this.formatMessage(message, toolsForModel)
230
+ const formattedMessages = this.formatMessage(message)
272
231
  parsedInputMessages.push(...formattedMessages)
273
232
  }
274
233
 
275
- const outputMessage = this.formatOutputMessage(tags, toolsForModel)
234
+ const outputMessage = this.formatOutputMessage(tags)
276
235
 
277
236
  this._tagger.tagLLMIO(span, parsedInputMessages, outputMessage)
278
237
 
@@ -284,8 +243,8 @@ class DdTelemetryPlugin extends BaseLLMObsPlugin {
284
243
  }
285
244
 
286
245
  setToolTags (span, tags) {
287
- const toolCallId = tags['ai.toolCall.id']
288
- const name = getToolNameFromTags(tags) ?? this.#toolCallIdsToName[toolCallId]
246
+ const name = getToolNameFromTags(tags)
247
+
289
248
  if (name) this._tagger._setTag(span, NAME, name)
290
249
 
291
250
  const input = tags['ai.toolCall.args']
@@ -294,7 +253,7 @@ class DdTelemetryPlugin extends BaseLLMObsPlugin {
294
253
  this._tagger.tagTextIO(span, input, output)
295
254
  }
296
255
 
297
- formatOutputMessage (tags, toolsForModel) {
256
+ formatOutputMessage (tags) {
298
257
  const outputMessageText = tags['ai.response.text'] ?? tags['ai.response.object']
299
258
  const outputMessageToolCalls = getJsonStringValue(tags['ai.response.toolCalls'], [])
300
259
 
@@ -302,13 +261,9 @@ class DdTelemetryPlugin extends BaseLLMObsPlugin {
302
261
  for (const toolCall of outputMessageToolCalls) {
303
262
  const toolArgs = toolCall.args ?? toolCall.input
304
263
  const toolCallArgs = typeof toolArgs === 'string' ? getJsonStringValue(toolArgs, {}) : toolArgs
305
- const toolDescription = toolsForModel?.find(tool => toolCall.toolName === tool.name)?.description
306
- const name = this.findToolName(toolCall.toolName, toolDescription)
307
- this.#toolCallIdsToName[toolCall.toolCallId] = name
308
-
309
264
  formattedToolCalls.push({
310
265
  arguments: toolCallArgs,
311
- name,
266
+ name: toolCall.toolName,
312
267
  toolId: toolCall.toolCallId,
313
268
  type: toolCall.toolCallType ?? 'function',
314
269
  })
@@ -328,11 +283,10 @@ class DdTelemetryPlugin extends BaseLLMObsPlugin {
328
283
  * need to split into multiple messages.
329
284
  *
330
285
  * @param {AiSdkMessage} message
331
- * @param {ToolForModel[] | null | undefined} toolsForModel
332
286
  * @returns {Array<{role: string, content: string, toolId?: string,
333
287
  * toolCalls?: Array<{arguments: string, name: string, toolId: string, type: string}>}>}
334
288
  */
335
- formatMessage (message, toolsForModel) {
289
+ formatMessage (message) {
336
290
  const { role, content } = message
337
291
 
338
292
  if (role === 'system') {
@@ -357,12 +311,9 @@ class DdTelemetryPlugin extends BaseLLMObsPlugin {
357
311
  if (['text', 'reasoning', 'redacted-reasoning'].includes(type)) {
358
312
  finalContent += part.text ?? part.data
359
313
  } else if (type === 'tool-call') {
360
- const toolDescription = toolsForModel?.find(tool => part.toolName === tool.name)?.description
361
- const name = this.findToolName(part.toolName, toolDescription)
362
-
363
314
  toolCalls.push({
364
315
  arguments: part.args ?? part.input,
365
- name,
316
+ name: part.toolName,
366
317
  toolId: part.toolCallId,
367
318
  type: 'function',
368
319
  })
@@ -38,11 +38,15 @@ function formatLanguageModelInputMessages (instructions, messages) {
38
38
  const inputMessages = []
39
39
 
40
40
  if (instructions) {
41
- const systemPrompt = typeof instructions === 'string'
42
- ? instructions
43
- : Array.isArray(instructions)
44
- ? instructions.map(instruction => instruction.content).join('')
45
- : instructions.content
41
+ let systemPrompt = instructions
42
+ if (typeof instructions !== 'string') {
43
+ if (Array.isArray(instructions)) {
44
+ systemPrompt = ''
45
+ for (const instruction of instructions) systemPrompt += instruction.content
46
+ } else {
47
+ systemPrompt = instructions.content
48
+ }
49
+ }
46
50
 
47
51
  inputMessages.push({ role: 'system', content: systemPrompt })
48
52
  }
@@ -53,13 +57,13 @@ function formatLanguageModelInputMessages (instructions, messages) {
53
57
  if (role === 'system') {
54
58
  inputMessages.push({ role, content })
55
59
  } else if (role === 'user') {
56
- const userMessageContent =
57
- typeof content === 'string'
58
- ? content
59
- : content
60
- .filter(part => part.type === 'text')
61
- .map(part => part.text)
62
- .join('')
60
+ let userMessageContent = content
61
+ if (typeof content !== 'string') {
62
+ userMessageContent = ''
63
+ for (const part of content) {
64
+ if (part.type === 'text') userMessageContent += part.text
65
+ }
66
+ }
63
67
 
64
68
  inputMessages.push({ role, content: userMessageContent })
65
69
  } else if (role === 'assistant') {
@@ -292,7 +296,13 @@ class VercelAiTelemetryPlugin extends BaseLLMObsPlugin {
292
296
  const { event, result } = ctx
293
297
 
294
298
  const lastUserPrompt = event.messages.findLast(message => message.role === 'user')?.content
295
- const input = Array.isArray(lastUserPrompt) ? lastUserPrompt.map(part => part.text ?? '').join('') : lastUserPrompt
299
+ let input = lastUserPrompt
300
+ if (Array.isArray(lastUserPrompt)) {
301
+ input = ''
302
+ for (const part of lastUserPrompt) {
303
+ if (part.type === 'text') input += part.text
304
+ }
305
+ }
296
306
 
297
307
  const output =
298
308
  ctx.isStream
@@ -38,16 +38,20 @@ function formatAnthropicToolResultContent (content) {
38
38
  if (typeof content === 'string') {
39
39
  return content
40
40
  } else if (Array.isArray(content)) {
41
- const formattedContent = []
41
+ let formattedContent = ''
42
42
  for (const toolResultBlock of content) {
43
+ let part
43
44
  if (toolResultBlock.text) {
44
- formattedContent.push(toolResultBlock.text)
45
+ part = toolResultBlock.text
45
46
  } else if (toolResultBlock.type === 'image') {
46
- formattedContent.push('([IMAGE DETECTED])')
47
+ part = '([IMAGE DETECTED])'
47
48
  }
49
+ if (part === undefined) continue
50
+ if (formattedContent) formattedContent += ','
51
+ formattedContent += part
48
52
  }
49
53
 
50
- return formattedContent.join(',')
54
+ return formattedContent
51
55
  }
52
56
  return JSON.stringify(content)
53
57
  }
@@ -18,12 +18,15 @@ function getToolOutputText (raw) {
18
18
  if (raw == null) return
19
19
 
20
20
  if (Array.isArray(raw)) {
21
- const output = []
21
+ let output = ''
22
+ let separator = ''
22
23
  for (const block of raw) {
23
24
  const text = getToolOutputText(block)
24
- if (text) output.push(text)
25
+ if (!text) continue
26
+ output += separator + text
27
+ separator = '\n'
25
28
  }
26
- return output.join('\n') || undefined
29
+ return output
27
30
  }
28
31
 
29
32
  if (raw.type === 'tool_result') return getToolOutputText(raw.content)
@@ -46,6 +46,13 @@ class BaseLangChainLLMObsPlugin extends LLMObsPlugin {
46
46
  }
47
47
  }
48
48
 
49
+ error (ctx) {
50
+ // NodeInterrupt or GraphInterrupt "errors" for control flow, do not mark as real errors
51
+ if (ctx.error?.is_bubble_up) return
52
+
53
+ super.error(ctx)
54
+ }
55
+
49
56
  getLLMObsSpanRegisterOptions (ctx) {
50
57
  const span = ctx.currentStore?.span
51
58
  const spanContext = span?.context()
@@ -134,15 +134,17 @@ function hasMultimodalInputs (variables) {
134
134
  * @returns {{ content: string, audioParts: Array<{ mimeType: string, content: string }> }}
135
135
  */
136
136
  function extractContentParts (parts) {
137
- const extracted = []
137
+ let content = ''
138
+ let hasContent = false
138
139
  const audioParts = []
139
140
 
140
141
  for (const part of parts) {
141
142
  const partType = part?.type ?? ''
143
+ let extracted
142
144
  if (partType === 'text') {
143
- extracted.push(part.text ?? '')
145
+ extracted = part.text ?? ''
144
146
  } else if (partType === 'image_url') {
145
- extracted.push(IMAGE_FALLBACK)
147
+ extracted = IMAGE_FALLBACK
146
148
  } else if (partType === 'input_audio') {
147
149
  const inputAudio = part.input_audio ?? {}
148
150
  const data = inputAudio.data
@@ -151,14 +153,19 @@ function extractContentParts (parts) {
151
153
  // is needed. Only fall back to "[audio]" when there's no audio to capture.
152
154
  audioParts.push(formatAudioPart(data, audioMimeTypeFromFormat(inputAudio.format, AUDIO_MIME_TYPES)))
153
155
  } else {
154
- extracted.push(AUDIO_FALLBACK)
156
+ extracted = AUDIO_FALLBACK
155
157
  }
156
158
  } else {
157
- extracted.push(`[${partType}]`)
159
+ extracted = `[${partType}]`
158
160
  }
161
+
162
+ if (extracted === undefined) continue
163
+ if (hasContent) content += '\n'
164
+ content += extracted
165
+ hasContent = true
159
166
  }
160
167
 
161
- return { content: extracted.join('\n'), audioParts }
168
+ return { content, audioParts }
162
169
  }
163
170
 
164
171
  /**
@@ -11,23 +11,23 @@ const { safeJsonParse } = require('../../util')
11
11
  * @returns {{ content: string, audioParts: Array<{ mimeType: string, content: string }> }}
12
12
  */
13
13
  function extractMessageContent (parts) {
14
- const contentParts = []
14
+ let content = ''
15
15
  const audioParts = []
16
16
 
17
17
  for (const part of parts) {
18
18
  if (!part) continue
19
19
  const text = extractTextFromContentItem(part)
20
20
  if (text) {
21
- contentParts.push(text)
21
+ content += text
22
22
  continue
23
23
  }
24
24
 
25
25
  const extracted = extractContentParts([part])
26
- if (extracted.content) contentParts.push(extracted.content)
26
+ if (extracted.content) content += extracted.content
27
27
  if (extracted.audioParts.length > 0) audioParts.push(...extracted.audioParts)
28
28
  }
29
29
 
30
- return { content: contentParts.join(''), audioParts }
30
+ return { content, audioParts }
31
31
  }
32
32
 
33
33
  /**
@@ -167,13 +167,11 @@ function extractOutputMessages (result) {
167
167
  if (item.type === 'message') {
168
168
  let content = ''
169
169
  if (Array.isArray(item.content)) {
170
- const textParts = []
171
170
  for (const contentItem of item.content) {
172
171
  if (contentItem?.type === 'output_text' && contentItem.text) {
173
- textParts.push(contentItem.text)
172
+ content += contentItem.text
174
173
  }
175
174
  }
176
- content = textParts.join('')
177
175
  } else if (typeof item.content === 'string') {
178
176
  content = item.content
179
177
  }
@@ -337,37 +337,6 @@ function resolveAgentAttribution (tags, span) {
337
337
  return { name: tags[PARENT_AGENT_NAME], spanId: tags[PARENT_AGENT_SPAN_ID] }
338
338
  }
339
339
 
340
- /**
341
- * Removes all `key=value` entries for the given key from a comma-separated tagset string.
342
- *
343
- * @param {string} tags - Existing tagset string (may be empty).
344
- * @param {string} key
345
- * @returns {string}
346
- */
347
- function stripTagsetEntry (tags, key) {
348
- if (!tags.includes(key)) return tags
349
- return tags.split(',').filter(entry => !entry.startsWith(`${key}=`)).join(',')
350
- }
351
-
352
- /**
353
- * Appends `key=value` to the tagset string with a comma separator, but only when `value` is
354
- * truthy, passes the optional `safeguard` predicate, and fits within `maxTagSetLength`. Returns
355
- * the original `tags` unchanged when the value is absent, unsafe, or would overflow the budget.
356
- *
357
- * @param {string} tags - Existing tagset string (may be empty).
358
- * @param {string} key
359
- * @param {string | undefined} value
360
- * @param {((v: string) => boolean) | null} [safeguard]
361
- * @param {number} [maxTagSetLength]
362
- * @returns {string}
363
- */
364
- function appendOptionalPropagatedTag (tags, key, value, safeguard, maxTagSetLength) {
365
- if (!value || (safeguard && !safeguard(value))) return tags
366
- const entry = `${tags ? ',' : ''}${key}=${value}`
367
- if (maxTagSetLength != null && tags.length + entry.length > maxTagSetLength) return tags
368
- return `${tags}${entry}`
369
- }
370
-
371
340
  function spanHasError (span) {
372
341
  const spanContext = span.context()
373
342
  return !!(spanContext.getTag('error') || spanContext.getTag('error.type'))
@@ -518,7 +487,6 @@ function formatAudioPart (data, mimeType) {
518
487
 
519
488
  module.exports = {
520
489
  agentNameWireSafe,
521
- appendOptionalPropagatedTag,
522
490
  audioMimeTypeFromFormat,
523
491
  encodeUnicode,
524
492
  findGenAIAncestorSpanId,
@@ -527,7 +495,6 @@ module.exports = {
527
495
  normalizeLlmObsTraceId,
528
496
  formatAudioPart,
529
497
  resolveAgentAttribution,
530
- stripTagsetEntry,
531
498
  validateCostTags,
532
499
  validateKind,
533
500
  getFunctionArguments,