dd-trace 5.132.0 → 5.133.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 (204) hide show
  1. package/ci/diagnose.js +9 -14
  2. package/ci/init.js +1 -2
  3. package/ci/test-optimization-validation/ci-discovery.js +9 -4
  4. package/ci/test-optimization-validation/executable.js +1 -5
  5. package/ci/test-optimization-validation/framework-adapters/cypress.js +1 -4
  6. package/ci/test-optimization-validation/framework-adapters/vitest.js +1 -1
  7. package/ci/test-optimization-validation/generated-files.js +6 -4
  8. package/ci/test-optimization-validation/generated-test-contract.js +7 -3
  9. package/ci/test-optimization-validation/manifest-scaffold.js +7 -2
  10. package/ci/test-optimization-validation/manifest-schema.js +6 -7
  11. package/ci/test-optimization-validation/report-writer.js +6 -2
  12. package/ci/test-optimization-validation/runner-command.js +2 -4
  13. package/ci/test-optimization-validation/runner-contract.js +1 -2
  14. package/ci/test-optimization-validation/scenarios/auto-test-retries.js +1 -15
  15. package/ci/vitest-no-worker-init-setup.mjs +10 -7
  16. package/index.d.ts +25 -14
  17. package/initialize.mjs +1 -3
  18. package/openfeature/rules-based.d.ts +11 -0
  19. package/openfeature/rules-based.js +7 -0
  20. package/package.json +12 -10
  21. package/packages/datadog-core/src/storage.js +16 -0
  22. package/packages/datadog-esbuild/index.js +18 -12
  23. package/packages/datadog-instrumentations/src/aerospike.js +3 -3
  24. package/packages/datadog-instrumentations/src/ai.js +3 -3
  25. package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +0 -6
  26. package/packages/datadog-instrumentations/src/aws-sdk.js +21 -13
  27. package/packages/datadog-instrumentations/src/cassandra-driver.js +25 -20
  28. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +9 -23
  29. package/packages/datadog-instrumentations/src/cucumber.js +48 -33
  30. package/packages/datadog-instrumentations/src/elasticsearch.js +1 -5
  31. package/packages/datadog-instrumentations/src/electron.js +6 -6
  32. package/packages/datadog-instrumentations/src/fastify.js +13 -4
  33. package/packages/datadog-instrumentations/src/fs.js +2 -2
  34. package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +9 -5
  35. package/packages/datadog-instrumentations/src/grpc/client.js +2 -0
  36. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +2 -2
  37. package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +1 -2
  38. package/packages/datadog-instrumentations/src/helpers/instrument.js +1 -1
  39. package/packages/datadog-instrumentations/src/helpers/register.js +36 -1
  40. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -0
  41. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentation-registry.js +87 -6
  42. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +5 -6
  43. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/postgres.js +48 -31
  44. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +7 -7
  45. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms/postgres.js +42 -4
  46. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +7 -10
  47. package/packages/datadog-instrumentations/src/ioredis.js +7 -5
  48. package/packages/datadog-instrumentations/src/iovalkey.js +7 -5
  49. package/packages/datadog-instrumentations/src/jest.js +3 -10
  50. package/packages/datadog-instrumentations/src/knex.js +9 -5
  51. package/packages/datadog-instrumentations/src/mariadb-bundle.js +3 -3
  52. package/packages/datadog-instrumentations/src/mocha/common.js +20 -8
  53. package/packages/datadog-instrumentations/src/mocha/main.js +140 -43
  54. package/packages/datadog-instrumentations/src/mocha/utils.js +3 -2
  55. package/packages/datadog-instrumentations/src/mocha/worker.js +1 -2
  56. package/packages/datadog-instrumentations/src/mongodb-core.js +3 -3
  57. package/packages/datadog-instrumentations/src/mongoose.js +3 -3
  58. package/packages/datadog-instrumentations/src/nova-sonic/audio.js +173 -0
  59. package/packages/datadog-instrumentations/src/nova-sonic/index.js +209 -0
  60. package/packages/datadog-instrumentations/src/nova-sonic/session.js +483 -0
  61. package/packages/datadog-instrumentations/src/openai-realtime/index.js +18 -6
  62. package/packages/datadog-instrumentations/src/openai-realtime/session.js +1 -1
  63. package/packages/datadog-instrumentations/src/openai.js +7 -0
  64. package/packages/datadog-instrumentations/src/pino.js +5 -6
  65. package/packages/datadog-instrumentations/src/redis.js +27 -13
  66. package/packages/datadog-instrumentations/src/router.js +30 -0
  67. package/packages/datadog-instrumentations/src/rum-browser-scripts.js +2 -6
  68. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +7 -5
  69. package/packages/datadog-instrumentations/src/vitest-main.js +9 -15
  70. package/packages/datadog-instrumentations/src/vitest-util.js +2 -2
  71. package/packages/datadog-instrumentations/src/vitest-worker.js +1 -2
  72. package/packages/datadog-instrumentations/src/vitest.js +2 -2
  73. package/packages/datadog-instrumentations/src/webdriverio.js +5 -8
  74. package/packages/datadog-instrumentations/src/ws.js +1 -4
  75. package/packages/datadog-plugin-amqplib/src/client.js +1 -2
  76. package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -5
  77. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +1 -2
  78. package/packages/datadog-plugin-aws-sdk/src/base.js +5 -8
  79. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/index.js +18 -0
  80. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/sonic.js +45 -0
  81. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +9 -3
  82. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +2 -5
  83. package/packages/datadog-plugin-azure-durable-functions/src/index.js +5 -4
  84. package/packages/datadog-plugin-azure-functions/src/index.js +5 -3
  85. package/packages/datadog-plugin-claude-agent-sdk/src/index.js +0 -2
  86. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +1 -5
  87. package/packages/datadog-plugin-cypress/src/index.js +6 -6
  88. package/packages/datadog-plugin-cypress/src/source-map-utils.js +1 -2
  89. package/packages/datadog-plugin-cypress/src/support.js +1 -4
  90. package/packages/datadog-plugin-express/src/code_origin.js +1 -2
  91. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -2
  92. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +1 -2
  93. package/packages/datadog-plugin-graphql/src/execute.js +5 -5
  94. package/packages/datadog-plugin-grpc/src/util.js +1 -2
  95. package/packages/datadog-plugin-kafkajs/src/producer.js +1 -2
  96. package/packages/datadog-plugin-mocha/src/index.js +7 -10
  97. package/packages/datadog-plugin-mocha/src/webdriverio-video.js +8 -4
  98. package/packages/datadog-plugin-mongodb-core/src/query.js +8 -5
  99. package/packages/datadog-plugin-next/src/index.js +1 -2
  100. package/packages/datadog-plugin-openai/src/tracing.js +5 -2
  101. package/packages/datadog-plugin-openai-agents/src/integration.js +6 -2
  102. package/packages/datadog-plugin-openai-agents/src/processor.js +2 -4
  103. package/packages/datadog-plugin-postgres/src/index.js +24 -0
  104. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -5
  105. package/packages/datadog-plugin-rhea/src/consumer.js +1 -4
  106. package/packages/datadog-plugin-vitest/src/index.js +5 -2
  107. package/packages/datadog-shimmer/src/shimmer.js +5 -4
  108. package/packages/datadog-webpack/index.js +55 -43
  109. package/packages/datadog-webpack/src/loader.js +56 -8
  110. package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -4
  111. package/packages/dd-trace/src/aiguard/messages/utils.js +1 -2
  112. package/packages/dd-trace/src/aiguard/redaction.js +8 -2
  113. package/packages/dd-trace/src/appsec/api_security/normalized-route.js +3 -2
  114. package/packages/dd-trace/src/appsec/api_security/sampler.js +2 -6
  115. package/packages/dd-trace/src/appsec/downstream_requests.js +1 -9
  116. package/packages/dd-trace/src/appsec/handlers/http-request.js +3 -9
  117. package/packages/dd-trace/src/appsec/handlers/http-response.js +7 -2
  118. package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +4 -3
  119. package/packages/dd-trace/src/appsec/iast/analyzers/set-cookies-header-interceptor.js +1 -4
  120. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +7 -1
  121. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +1 -3
  122. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-esm.mjs +1 -3
  123. package/packages/dd-trace/src/appsec/iast/telemetry/iast-metric.js +1 -6
  124. package/packages/dd-trace/src/appsec/lambda.js +1 -3
  125. package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +37 -27
  126. package/packages/dd-trace/src/appsec/rasp/lfi.js +9 -5
  127. package/packages/dd-trace/src/appsec/reporter.js +21 -1
  128. package/packages/dd-trace/src/appsec/stack_trace.js +1 -2
  129. package/packages/dd-trace/src/ci-visibility/dynamic-atr-retries.js +3 -6
  130. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +1 -1
  131. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +13 -7
  132. package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +1 -2
  133. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +2 -8
  134. package/packages/dd-trace/src/ci-visibility/resolve-runfile-path.js +54 -0
  135. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +5 -8
  136. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +2 -53
  137. package/packages/dd-trace/src/config/config-types.d.ts +2 -2
  138. package/packages/dd-trace/src/config/defaults.js +5 -5
  139. package/packages/dd-trace/src/config/generated-config-types.d.ts +8 -0
  140. package/packages/dd-trace/src/config/helper.js +34 -3
  141. package/packages/dd-trace/src/config/index.js +10 -9
  142. package/packages/dd-trace/src/config/normalize-service.js +1 -1
  143. package/packages/dd-trace/src/config/parsers.js +2 -2
  144. package/packages/dd-trace/src/config/supported-configurations.json +35 -0
  145. package/packages/dd-trace/src/datastreams/processor.js +3 -2
  146. package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +1 -4
  147. package/packages/dd-trace/src/debugger/devtools_client/send.js +1 -0
  148. package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -0
  149. package/packages/dd-trace/src/debugger/guardrail-metrics.js +2 -2
  150. package/packages/dd-trace/src/dogstatsd.js +1 -1
  151. package/packages/dd-trace/src/encode/tags-processors.js +2 -2
  152. package/packages/dd-trace/src/exporters/common/request.js +1 -5
  153. package/packages/dd-trace/src/id.js +14 -1
  154. package/packages/dd-trace/src/lambda/handler.js +179 -19
  155. package/packages/dd-trace/src/lambda/index.js +15 -1
  156. package/packages/dd-trace/src/lambda/runtime/patch.js +34 -7
  157. package/packages/dd-trace/src/llmobs/audio-utils.js +3 -2
  158. package/packages/dd-trace/src/llmobs/constants/tags.js +12 -0
  159. package/packages/dd-trace/src/llmobs/experiments/experiment.js +1 -1
  160. package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +15 -0
  161. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +1 -2
  162. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chat_model.js +1 -3
  163. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
  164. package/packages/dd-trace/src/llmobs/plugins/nova-sonic.js +92 -0
  165. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +56 -2
  166. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +38 -0
  167. package/packages/dd-trace/src/llmobs/prompts/manager.js +5 -7
  168. package/packages/dd-trace/src/llmobs/prompts/prompt.js +1 -1
  169. package/packages/dd-trace/src/llmobs/span_processor.js +1 -2
  170. package/packages/dd-trace/src/llmobs/tagger.js +2 -2
  171. package/packages/dd-trace/src/llmobs/util.js +1 -2
  172. package/packages/dd-trace/src/llmobs/writers/base.js +4 -5
  173. package/packages/dd-trace/src/log/log.js +1 -5
  174. package/packages/dd-trace/src/openfeature/evaluation-telemetry-opt-out-hook.js +51 -0
  175. package/packages/dd-trace/src/openfeature/flagging_provider.js +48 -21
  176. package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +1 -1
  177. package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +1 -1
  178. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -2
  179. package/packages/dd-trace/src/opentelemetry/otlp/common.proto +1 -1
  180. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +6 -23
  181. package/packages/dd-trace/src/opentracing/propagation/text_map.js +53 -13
  182. package/packages/dd-trace/src/otel-sampling.js +4 -5
  183. package/packages/dd-trace/src/otel-thread-ctx.js +79 -13
  184. package/packages/dd-trace/src/plugins/database.js +7 -16
  185. package/packages/dd-trace/src/plugins/util/git.js +9 -25
  186. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +1 -5
  187. package/packages/dd-trace/src/plugins/util/test.js +48 -4
  188. package/packages/dd-trace/src/priority_sampler.js +1 -2
  189. package/packages/dd-trace/src/profiling/profiler.js +4 -6
  190. package/packages/dd-trace/src/proxy.js +1 -2
  191. package/packages/dd-trace/src/ritm.js +4 -2
  192. package/packages/dd-trace/src/serverless.js +5 -0
  193. package/packages/dd-trace/src/span_processor.js +13 -17
  194. package/packages/dd-trace/src/telemetry/endpoints.js +1 -3
  195. package/packages/dd-trace/src/util.js +29 -0
  196. package/packages/dd-trace/src/web-tags-cache.js +6 -2
  197. package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
  198. package/vendor/dist/@datadog/openfeature-node-server/rules-based/LICENSE +201 -0
  199. package/vendor/dist/@datadog/openfeature-node-server/rules-based/index.d.ts +925 -0
  200. package/vendor/dist/@datadog/openfeature-node-server/rules-based/index.js +1 -0
  201. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  202. package/vendor/dist/protobufjs/index.js +1 -1
  203. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  204. package/vendor/dist/shell-quote/index.js +1 -1
@@ -129,6 +129,7 @@ function statusPayload (probeId, probeVersion, status) {
129
129
  return {
130
130
  ddsource,
131
131
  service,
132
+ timestamp: Date.now(),
132
133
  debugger: {
133
134
  diagnostics: { probeId, runtimeId, probeVersion, status },
134
135
  },
@@ -9,10 +9,10 @@
9
9
  * atomics, and the main thread periodically drains the counters into instrumentation telemetry metrics.
10
10
  *
11
11
  * The metric names, tags and reason codes are defined by the "Debugger Observability for GA" specification and are
12
- * shared with the other tracers. All metrics are counts in the `live_debugger` telemetry namespace.
12
+ * shared with the other tracers. All metrics are counts in the `debugger` telemetry namespace.
13
13
  */
14
14
 
15
- const TELEMETRY_NAMESPACE = 'live_debugger'
15
+ const TELEMETRY_NAMESPACE = 'debugger'
16
16
 
17
17
  const EVENT_TYPE = Object.freeze({
18
18
  SNAPSHOT: 0,
@@ -533,7 +533,7 @@ class DogStatsDClient {
533
533
  for (const [key, value] of Object.entries(config.tags)) {
534
534
  // Skip runtime-id unless enabled as cardinality may be too high
535
535
  if (typeof value === 'string' && (key !== 'runtime-id' || config.runtimeMetricsRuntimeId)) {
536
- // https://docs.datadoghq.com/tagging/#defining-tags
536
+ // https://docs.datadoghq.com/getting_started/tagging/#define-tags
537
537
  const valueStripped = value.replaceAll(/[^a-z0-9_:./-]/ig, '_')
538
538
 
539
539
  tags.push(`${key}:${valueStripped}`)
@@ -2,7 +2,7 @@
2
2
 
3
3
  // From agent truncators: https://github.com/DataDog/datadog-agent/blob/main/pkg/trace/agent/truncator.go
4
4
 
5
- // Values from: https://github.com/DataDog/datadog-agent/blob/main/pkg/trace/traceutil/truncate.go#L22-L27
5
+ // Values from: https://github.com/DataDog/datadog-agent/blob/main/pkg/trace/traceutil/normalize/truncate.go
6
6
  // MAX_RESOURCE_NAME_LENGTH the maximum length a span resource can have
7
7
  const MAX_RESOURCE_NAME_LENGTH = 5000
8
8
  // MAX_META_KEY_LENGTH the maximum length of metadata key
@@ -15,7 +15,7 @@ const MAX_META_VALUE_LENGTH_TEST_OPTIMIZATION = 5000
15
15
  const MAX_METRIC_KEY_LENGTH = MAX_META_KEY_LENGTH
16
16
 
17
17
  // From agent normalizer:
18
- // https://github.com/DataDog/datadog-agent/blob/main/pkg/trace/traceutil/normalize.go
18
+ // https://github.com/DataDog/datadog-agent/blob/main/pkg/trace/traceutil/normalize/normalize.go
19
19
  // DEFAULT_SPAN_NAME is the default name we assign a span if it's missing and we have no reasonable fallback
20
20
  const DEFAULT_SPAN_NAME = 'unnamed_operation'
21
21
  // DEFAULT_SERVICE_NAME is the default name we assign a service if it's missing and we have no reasonable fallback
@@ -86,11 +86,7 @@ function request (data, options, callback) {
86
86
  const timeout = options.timeout || 2000
87
87
  const isSecure = options.protocol === 'https:'
88
88
  const client = isSecure ? https : http
89
- let dataArray = data
90
-
91
- if (!Array.isArray(data)) {
92
- dataArray = [data]
93
- }
89
+ const dataArray = Array.isArray(data) ? data : [data]
94
90
  const contentLength = byteLength(dataArray)
95
91
  options.headers['Content-Length'] = contentLength
96
92
 
@@ -6,6 +6,8 @@ const UINT_MAX = 4_294_967_296
6
6
 
7
7
  const data = new Uint8Array(8 * 8192)
8
8
  const zeroId = new Uint8Array(8)
9
+ // Reused for hex encoding: toString('hex') copies out, so the bytes need not outlive the call.
10
+ const hexScratch = Buffer.alloc(16)
9
11
 
10
12
  let batch = 0
11
13
 
@@ -35,7 +37,7 @@ class Identifier {
35
37
  */
36
38
  toString (radix = 16) {
37
39
  if (radix === 16) {
38
- this.#stringHex ??= Buffer.from(this.#buffer).toString('hex')
40
+ this.#stringHex ??= toHexString(this.#buffer)
39
41
  return this.#stringHex
40
42
  }
41
43
  if (radix === 10) {
@@ -190,6 +192,17 @@ function toNumberString (buffer, radix) {
190
192
  return str
191
193
  }
192
194
 
195
+ // Same string as Buffer.from(buffer).toString('hex'), without allocating a Buffer per call.
196
+ /**
197
+ * @param {number[] | Uint8Array} buffer
198
+ */
199
+ function toHexString (buffer) {
200
+ const length = buffer.length
201
+ if (length > hexScratch.length) return Buffer.from(buffer).toString('hex')
202
+ for (let i = 0; i < length; i++) hexScratch[i] = buffer[i]
203
+ return hexScratch.toString('hex', 0, length)
204
+ }
205
+
193
206
  // Simple pseudo-random 64-bit ID generator.
194
207
  /**
195
208
  * @returns {number[] | Uint8Array}
@@ -1,20 +1,24 @@
1
1
  'use strict'
2
2
 
3
- const log = require('../log')
3
+ const { types: { isProxy } } = require('node:util')
4
+
4
5
  const { channel } = require('../../../datadog-instrumentations/src/helpers/instrument')
5
6
  const { ERROR_MESSAGE, ERROR_TYPE } = require('../constants')
7
+ const log = require('../log')
6
8
  const { ImpendingTimeout } = require('./runtime/errors')
7
9
  const { extractContext } = require('./context')
8
10
 
11
+ const HANDLER_STREAMING = Symbol.for('aws.lambda.runtime.handler.streaming')
12
+ const HANDLER_HIGHWATERMARK = Symbol.for('aws.lambda.runtime.handler.streaming.highWaterMark')
13
+ const BEFORE_EXIT = Symbol.for('aws.lambda.beforeExit')
9
14
  const timeoutChannel = channel('apm:aws:lambda:timeout')
15
+ const monitoredContexts = new WeakSet()
10
16
  // Always crash the flushes when a message is received
11
17
  // from this channel.
12
18
  timeoutChannel.subscribe(_ => {
13
19
  crashFlush()
14
20
  })
15
21
 
16
- let __lambdaTimeout
17
-
18
22
  /**
19
23
  * Publishes to the `apm:aws:lambda:timeout` channel when
20
24
  * the AWS Lambda run time is about to end.
@@ -23,11 +27,15 @@ let __lambdaTimeout
23
27
  */
24
28
  function checkTimeout (context) {
25
29
  const remainingTimeInMillis = context.getRemainingTimeInMillis()
26
- const apmFlushDeadline = global._ddtrace._tracer._config.DD_APM_FLUSH_DEADLINE_MILLISECONDS
30
+ const apmFlushDeadline = global._ddtrace?._tracer?._config?.DD_APM_FLUSH_DEADLINE_MILLISECONDS
31
+ if (!Number.isFinite(remainingTimeInMillis) || !Number.isFinite(apmFlushDeadline)) return
27
32
 
28
- __lambdaTimeout = setTimeout(() => {
33
+ const timeout = setTimeout(() => {
29
34
  timeoutChannel.publish()
30
- }, remainingTimeInMillis - apmFlushDeadline)
35
+ }, Math.max(0, remainingTimeInMillis - apmFlushDeadline))
36
+ // eslint-disable-next-line eslint-rules/eslint-timer-unref -- AWS Lambda uses Node.js timers.
37
+ timeout.unref()
38
+ return timeout
31
39
  }
32
40
 
33
41
  /**
@@ -58,26 +66,178 @@ function crashFlush () {
58
66
  }
59
67
 
60
68
  /**
61
- * Patches your AWS Lambda handler function to add some tracing support.
69
+ * Monitors an AWS Lambda handler for impending timeouts without creating a span.
62
70
  *
63
71
  * @param {Function} lambdaHandler a Lambda handler function.
72
+ * @param {boolean} normalized whether datadog-lambda-js owns completion selection.
64
73
  */
65
- exports.datadog = function datadog (lambdaHandler) {
66
- return (...args) => {
74
+ exports.datadog = function datadog (lambdaHandler, normalized = false) {
75
+ function monitored (...args) {
67
76
  const context = extractContext(args)
77
+ if (!context || monitoredContexts.has(context)) return Reflect.apply(lambdaHandler, this, args)
68
78
 
69
- if (context) {
70
- checkTimeout(context)
79
+ const timeout = checkTimeout(context)
80
+ if (timeout === undefined) return Reflect.apply(lambdaHandler, this, args)
81
+
82
+ let completed = false
83
+ let callbackPending = false
84
+ /** @type {Array<[object, string | symbol, Function, Function]>} */
85
+ const completionMethods = []
86
+ const clear = () => {
87
+ if (completed) return
88
+ completed = true
89
+ clearTimeout(timeout)
90
+ for (const [target, name, original, wrapped] of completionMethods) {
91
+ try {
92
+ if (target[name] === wrapped) Reflect.set(target, name, original)
93
+ } catch (error) {
94
+ log.error('Error restoring Lambda completion %s: %s', name, error)
95
+ }
96
+ }
97
+ }
98
+ const clearOnHandlerCompletion = normalized
99
+ ? clear
100
+ : () => {
101
+ // Once a native callback selects event-loop drain, later handler signals are ignored by RIC.
102
+ if (!callbackPending) clear()
103
+ }
104
+
105
+ const contextIndex = args.indexOf(context)
106
+ const callbackObservable = contextIndex !== 2 && typeof args[2] === 'function'
107
+ if (!normalized) {
108
+ for (const name of ['done', 'succeed', 'fail']) {
109
+ wrapCompletion(context, name, clearOnHandlerCompletion, completionMethods)
110
+ }
71
111
  }
112
+ // Legacy Lambda runtimes complete idle invocations through this callback instead of the handler's callback.
113
+ wrapCompletion(globalThis, BEFORE_EXIT, clear, completionMethods)
72
114
 
73
- const result = lambdaHandler.apply(this, args)
74
- if (result && typeof result.then === 'function') {
75
- return result.then((res) => {
76
- clearTimeout(__lambdaTimeout)
77
- return res
78
- })
115
+ if (callbackObservable) {
116
+ const callback = args[2]
117
+ const beforeExitObservable = completionMethods.at(-1)?.[1] === BEFORE_EXIT
118
+ let callbackInvoked = false
119
+ args[2] = function (...callbackArgs) {
120
+ if (completed || callbackInvoked) return Reflect.apply(callback, this, callbackArgs)
121
+ callbackInvoked = true
122
+ let beforeExit
123
+ try {
124
+ if (beforeExitObservable && callbackArgs[0] == null && context.callbackWaitsForEmptyEventLoop) {
125
+ beforeExit = getDataProperty(globalThis, BEFORE_EXIT)
126
+ }
127
+ } catch (error) {
128
+ log.error('Error reading Lambda callback completion: %s', error)
129
+ }
130
+ if (typeof beforeExit !== 'function') clear()
131
+ try {
132
+ const result = Reflect.apply(callback, this, callbackArgs)
133
+ if (!completed) {
134
+ // A successful legacy callback installs a new listener while waiting for event-loop drain.
135
+ const replacement = getDataProperty(globalThis, BEFORE_EXIT)
136
+ if (typeof replacement !== 'function' || replacement === beforeExit ||
137
+ !wrapCompletion(globalThis, BEFORE_EXIT, clear, completionMethods)) clear()
138
+ else callbackPending = true
139
+ }
140
+ return result
141
+ } catch (error) {
142
+ clear()
143
+ throw error
144
+ }
145
+ }
79
146
  }
80
- clearTimeout(__lambdaTimeout)
81
- return result
147
+
148
+ try {
149
+ // The outer completion adapter owns this context during synchronous dispatch to nested wrappers.
150
+ monitoredContexts.add(context)
151
+ const result = Reflect.apply(lambdaHandler, this, args)
152
+ if (result && typeof result.then === 'function') {
153
+ return result.then(value => {
154
+ clearOnHandlerCompletion()
155
+ return value
156
+ }, error => {
157
+ clearOnHandlerCompletion()
158
+ throw error
159
+ })
160
+ }
161
+ // Legacy RIC supplies a callback; newer runtimes complete from the return value.
162
+ if (!callbackObservable) clear()
163
+ return result
164
+ } catch (error) {
165
+ clearOnHandlerCompletion()
166
+ throw error
167
+ } finally {
168
+ monitoredContexts.delete(context)
169
+ }
170
+ }
171
+
172
+ if (normalized) return monitored
173
+
174
+ // datadog-lambda-js uses arity and the runtime's streaming metadata to choose how to invoke the handler.
175
+ Object.defineProperty(monitored, 'length', { value: getDataProperty(lambdaHandler, 'length') ?? 0 })
176
+ for (const name of [HANDLER_STREAMING, HANDLER_HIGHWATERMARK, '_ddWrapped']) {
177
+ const value = getDataProperty(lambdaHandler, name)
178
+ if (value !== undefined) monitored[name] = value
179
+ }
180
+ if (!isProxy(lambdaHandler)) {
181
+ for (const name of Reflect.ownKeys(lambdaHandler)) {
182
+ if (name === 'arguments' || name === 'caller') continue
183
+ const descriptor = Object.getOwnPropertyDescriptor(lambdaHandler, name)
184
+ if (!Object.hasOwn(descriptor, 'value')) continue
185
+ const existing = Object.getOwnPropertyDescriptor(monitored, name)
186
+ if (existing?.configurable === false) {
187
+ // Intrinsic function slots such as prototype cannot change their descriptor shape.
188
+ if (existing.writable) {
189
+ Object.defineProperty(monitored, name, { value: descriptor.value, writable: descriptor.writable })
190
+ }
191
+ } else {
192
+ Object.defineProperty(monitored, name, descriptor)
193
+ }
194
+ }
195
+ }
196
+ return monitored
197
+ }
198
+
199
+ /**
200
+ * @param {object} target
201
+ * @param {string | symbol} name
202
+ * @param {() => void} clear
203
+ * @param {Array<[object, string | symbol, Function, Function]>} completionMethods
204
+ */
205
+ function wrapCompletion (target, name, clear, completionMethods) {
206
+ try {
207
+ const original = target[name]
208
+ if (typeof original !== 'function') return true
209
+ const wrapped = onCompletion(original, clear)
210
+ if (!Reflect.set(target, name, wrapped) || target[name] !== wrapped) return false
211
+ completionMethods.push([target, name, original, wrapped])
212
+ return true
213
+ } catch (error) {
214
+ log.error('Error wrapping Lambda completion %s: %s', name, error)
215
+ return false
216
+ }
217
+ }
218
+
219
+ /**
220
+ * @param {Function} callback
221
+ * @param {() => void} clear
222
+ */
223
+ function onCompletion (callback, clear) {
224
+ return function (...args) {
225
+ clear()
226
+ return Reflect.apply(callback, this, args)
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Reads metadata without executing application getters or Proxy traps.
232
+ *
233
+ * @param {unknown} target
234
+ * @param {string | symbol} name
235
+ */
236
+ function getDataProperty (target, name) {
237
+ if (target === null || (typeof target !== 'object' && typeof target !== 'function')) return
238
+ while (target !== null && !isProxy(target)) {
239
+ const descriptor = Object.getOwnPropertyDescriptor(target, name)
240
+ if (descriptor) return descriptor.value
241
+ target = Object.getPrototypeOf(target)
82
242
  }
83
243
  }
@@ -41,7 +41,6 @@ if (!getValueFromEnvSources('DD_TRACE_DISABLED_INSTRUMENTATIONS')?.split(',').in
41
41
 
42
42
  return moduleExports
43
43
  })
44
- return
45
44
  }
46
45
 
47
46
  const moduleToPatch = 'datadog-lambda-js'
@@ -62,4 +61,19 @@ if (!getValueFromEnvSources('DD_TRACE_DISABLED_INSTRUMENTATIONS')?.split(',').in
62
61
 
63
62
  return moduleExports
64
63
  })
64
+
65
+ // The published datadog-lambda-js ESM entrypoint imports this helper before initializing the tracer.
66
+ // Revisit cached copies through the registered hook without loading datadog-lambda-js early.
67
+ const helperSuffixes = [
68
+ path.join('datadog-lambda-js', 'dist', 'utils', 'handler.js'),
69
+ path.join('datadog-lambda-js', 'utils', 'handler.js'),
70
+ ]
71
+ for (const filename of Object.keys(require.cache)) {
72
+ if (!helperSuffixes.some(suffix => filename.endsWith(path.sep + suffix))) continue
73
+ try {
74
+ require(filename)
75
+ } catch (error) {
76
+ log.error('Error executing cached lambda hook for datadog-lambda-js: %s', error)
77
+ }
78
+ }
65
79
  }
@@ -1,6 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const path = require('path')
4
+ const { types: { isModuleNamespaceObject } } = require('node:util')
4
5
 
5
6
  const { datadog } = require('../handler')
6
7
  const { addHook } = require('../../../../datadog-instrumentations/src/helpers/instrument')
@@ -23,22 +24,48 @@ function patchDatadogLambdaHandler (datadogHandler) {
23
24
  return userHandler => datadogHandler(datadog(userHandler))
24
25
  }
25
26
 
27
+ /**
28
+ * Wraps datadog-lambda-js's promisifiedHandler factory, which wraps the customer's handler.
29
+ * The timeout monitor surrounds that adapter to observe the completion Promise selected by datadog-lambda-js.
30
+ * Legacy RIC completes returned Promises through context.succeed without waiting for event-loop drain.
31
+ *
32
+ * @param {object} moduleExports
33
+ */
34
+ function patchPromisifiedHandlerModule (moduleExports) {
35
+ shimmer.wrap(moduleExports, 'promisifiedHandler', promisify => {
36
+ return handler => datadog(promisify(handler), true)
37
+ })
38
+ return moduleExports
39
+ }
40
+
26
41
  /** @param {string} handlerPath */
27
42
  function patchLambdaModule (handlerPath) {
28
43
  return lambdaModule => {
29
- shimmer.wrap(lambdaModule, handlerPath, patchLambdaHandler)
30
- return lambdaModule
44
+ const descriptor = Object.getOwnPropertyDescriptor(lambdaModule, handlerPath)
45
+ if (typeof descriptor?.value === 'function') {
46
+ if (!descriptor.configurable && !descriptor.writable) return lambdaModule
47
+ // The monitor copies Lambda metadata without invoking application accessors or Proxy traps.
48
+ // Generic shimmer wrapping would copy the original properties back onto that safe wrapper.
49
+ descriptor.value = datadog(descriptor.value)
50
+ // Native ESM namespaces reject replacement even when their export reports writable: true.
51
+ const exports = isModuleNamespaceObject(lambdaModule)
52
+ ? Object.create(lambdaModule, Object.getOwnPropertyDescriptors(lambdaModule))
53
+ : lambdaModule
54
+ Object.defineProperty(exports, handlerPath, descriptor)
55
+ return exports
56
+ }
57
+ return shimmer.wrap(lambdaModule, handlerPath, datadog)
31
58
  }
32
59
  }
33
60
 
34
- /** @param {Function} lambdaHandler */
35
- function patchLambdaHandler (lambdaHandler) {
36
- return datadog(lambdaHandler)
37
- }
38
-
39
61
  const lambdaTaskRoot = getEnvironmentVariable('LAMBDA_TASK_ROOT')
40
62
  const originalLambdaHandler = getValueFromEnvSources('DD_LAMBDA_HANDLER')
41
63
 
64
+ // Observe the completion Promise selected by datadog-lambda-js inside the invocation span's async scope.
65
+ // The npm package keeps dist/; Lambda layer builds place its contents directly in the package root.
66
+ addHook({ name: 'datadog-lambda-js', file: 'dist/utils/handler.js' }, patchPromisifiedHandlerModule)
67
+ addHook({ name: 'datadog-lambda-js', file: 'utils/handler.js' }, patchPromisifiedHandlerModule)
68
+
42
69
  if (originalLambdaHandler === undefined) {
43
70
  addHook({ name: 'datadog-lambda-js' }, patchDatadogLambdaModule)
44
71
  } else {
@@ -106,8 +106,9 @@ function fitsInlineAudioBudget (byteLength, maxBytes = LLMOBS_AUDIO_INLINE_MAX_B
106
106
  * @returns {AudioPart | undefined}
107
107
  */
108
108
  function formatAudioPartWithGuard (audioBytes, mimeType, maxBytes = LLMOBS_AUDIO_INLINE_MAX_BYTES) {
109
- if (!audioBytes?.length || !isRenderableAudioMime(mimeType)) return
110
- if (!fitsInlineAudioBudget(audioBytes.length, maxBytes)) return
109
+ if (!audioBytes?.length || !isRenderableAudioMime(mimeType) || !fitsInlineAudioBudget(audioBytes.length, maxBytes)) {
110
+ return
111
+ }
111
112
 
112
113
  return formatAudioPart(audioBytes, mimeType)
113
114
  }
@@ -7,7 +7,11 @@ const CACHE_READ_INPUT_TOKENS_METRIC_KEY = 'cache_read_input_tokens'
7
7
  const CACHE_WRITE_INPUT_TOKENS_METRIC_KEY = 'cache_write_input_tokens'
8
8
  const CACHE_WRITE_5M_INPUT_TOKENS_METRIC_KEY = 'ephemeral_5m_input_tokens'
9
9
  const CACHE_WRITE_1H_INPUT_TOKENS_METRIC_KEY = 'ephemeral_1h_input_tokens'
10
+ const CACHE_WRITE_30M_INPUT_TOKENS_METRIC_KEY = 'ephemeral_30m_input_tokens'
11
+ const CACHE_WRITE_12H_INPUT_TOKENS_METRIC_KEY = 'ephemeral_12h_input_tokens'
10
12
  const REASONING_OUTPUT_TOKENS_METRIC_KEY = 'reasoning_output_tokens'
13
+ const WEB_SEARCH_COUNT_METRIC_KEY = 'web_search_count'
14
+ const STORAGE_SEARCH_COUNT_METRIC_KEY = 'storage_search_count'
11
15
 
12
16
  module.exports = {
13
17
  SPAN_KINDS: ['llm', 'agent', 'workflow', 'task', 'tool', 'embedding', 'retrieval', 'experiment'],
@@ -72,7 +76,11 @@ module.exports = {
72
76
  CACHE_WRITE_INPUT_TOKENS_METRIC_KEY,
73
77
  CACHE_WRITE_5M_INPUT_TOKENS_METRIC_KEY,
74
78
  CACHE_WRITE_1H_INPUT_TOKENS_METRIC_KEY,
79
+ CACHE_WRITE_30M_INPUT_TOKENS_METRIC_KEY,
80
+ CACHE_WRITE_12H_INPUT_TOKENS_METRIC_KEY,
75
81
  REASONING_OUTPUT_TOKENS_METRIC_KEY,
82
+ WEB_SEARCH_COUNT_METRIC_KEY,
83
+ STORAGE_SEARCH_COUNT_METRIC_KEY,
76
84
 
77
85
  // integrations build metric objects with these camelCase spellings. Null prototype: a metric
78
86
  // named after an `Object.prototype` member must not resolve to an inherited property.
@@ -84,7 +92,11 @@ module.exports = {
84
92
  cacheWriteTokens: CACHE_WRITE_INPUT_TOKENS_METRIC_KEY,
85
93
  cacheWrite5mTokens: CACHE_WRITE_5M_INPUT_TOKENS_METRIC_KEY,
86
94
  cacheWrite1hTokens: CACHE_WRITE_1H_INPUT_TOKENS_METRIC_KEY,
95
+ cacheWrite30mTokens: CACHE_WRITE_30M_INPUT_TOKENS_METRIC_KEY,
96
+ cacheWrite12hTokens: CACHE_WRITE_12H_INPUT_TOKENS_METRIC_KEY,
87
97
  reasoningOutputTokens: REASONING_OUTPUT_TOKENS_METRIC_KEY,
98
+ webSearchCount: WEB_SEARCH_COUNT_METRIC_KEY,
99
+ storageSearchCount: STORAGE_SEARCH_COUNT_METRIC_KEY,
88
100
  }),
89
101
 
90
102
  DROPPED_IO_COLLECTION_ERROR: 'dropped_io',
@@ -253,7 +253,7 @@ class Experiment {
253
253
  this.#external = options.external === true
254
254
  if (!this.#external) {
255
255
  if (!options.dataset) throw new Error('Experiment dataset is required')
256
- if (typeof options.task !== 'function') throw new Error('Experiment task is required')
256
+ if (typeof options.task !== 'function') throw new TypeError('Experiment task is required')
257
257
  }
258
258
 
259
259
  this.#client = client
@@ -239,11 +239,19 @@ function mergeChunkUsage (usage, chunk) {
239
239
  const mergedUsage = usage ?? { input_tokens: 0, output_tokens: 0 }
240
240
  mergedUsage.output_tokens = chunk.usage.output_tokens
241
241
 
242
+ // `message_delta` counts are cumulative; input grows past the `message_start` count when server
243
+ // tools (e.g. web search) feed results back into the model
244
+ const inputTokens = chunk.usage.input_tokens
245
+ if (inputTokens != null) mergedUsage.input_tokens = inputTokens
246
+
242
247
  const cacheCreationTokens = chunk.usage.cache_creation_input_tokens
243
248
  const cacheReadTokens = chunk.usage.cache_read_input_tokens
244
249
  if (cacheCreationTokens) mergedUsage.cache_creation_input_tokens = cacheCreationTokens
245
250
  if (cacheReadTokens) mergedUsage.cache_read_input_tokens = cacheReadTokens
246
251
 
252
+ const serverToolUse = chunk.usage.server_tool_use
253
+ if (serverToolUse) mergedUsage.server_tool_use = serverToolUse
254
+
247
255
  return mergedUsage
248
256
  }
249
257
 
@@ -280,6 +288,13 @@ function extractUsage (result) {
280
288
  metrics.cacheWrite1hTokens = 0
281
289
  }
282
290
 
291
+ // Read the usage counter, not server_tool_use blocks: dynamic filtering nests searches under code
292
+ // execution, and `response_inclusion: "excluded"` drops the blocks while the searches are still billed.
293
+ // Failed searches aren't billed and are already excluded. web_fetch_requests is skipped because web
294
+ // fetch has no per-call price.
295
+ const webSearchRequests = usage.server_tool_use?.web_search_requests
296
+ if (webSearchRequests > 0) metrics.webSearchCount = webSearchRequests
297
+
283
298
  return metrics
284
299
  }
285
300
 
@@ -84,8 +84,7 @@ function determineRole (candidate, parts = []) {
84
84
  * @param {string} role
85
85
  */
86
86
  function normalizeRole (role) {
87
- if (role === ROLES.MODEL) return ROLES.ASSISTANT
88
- if (role === ROLES.ASSISTANT) return ROLES.ASSISTANT
87
+ if (role === ROLES.MODEL || role === ROLES.ASSISTANT) return ROLES.ASSISTANT
89
88
  if (role === ROLES.USER) return ROLES.USER
90
89
  if (role === ROLES.REASONING) return ROLES.REASONING
91
90
  return ROLES.USER // default
@@ -62,11 +62,9 @@ class LangChainLLMObsChatModelHandler extends LangChainLLMObsHandler {
62
62
  */
63
63
  getTokenUsage (results) {
64
64
  const tokens = this.checkTokenUsageChatOrLLMResult(results)
65
- if (tokens.totalTokens > 0) return tokens
66
-
67
65
  // providers that report usage on each generated message instead of on `llmOutput`; counts are
68
66
  // summed per run so a run split across generations is totalled once
69
- if (!results.generations) return tokens
67
+ if (tokens.totalTokens > 0 || !results.generations) return tokens
70
68
 
71
69
  const tokensPerRunId = {}
72
70
  for (const messageSet of results.generations) {
@@ -76,7 +76,7 @@ class NextStreamLLMObsPlugin extends LLMObsPlugin {
76
76
  const { streamInputs: inputs, chunks } = streamData
77
77
  const input = inputs == null ? undefined : formatIO(inputs)
78
78
  const lastChunk = chunks.length > 0 ? chunks.at(-1) : undefined
79
- const output = !hasError && lastChunk != null ? formatIO(lastChunk) : undefined
79
+ const output = hasError || lastChunk == null ? undefined : formatIO(lastChunk)
80
80
 
81
81
  this._tagger.tagTextIO(span, input, output)
82
82
 
@@ -0,0 +1,92 @@
1
+ 'use strict'
2
+
3
+ const { pcm16ToWav } = require('../audio-codec')
4
+ const { fitsInlineAudioBudget, formatAudioPartWithGuard } = require('../audio-utils')
5
+ const { AUDIO_FALLBACK, LLMOBS_AUDIO_INLINE_MAX_BYTES } = require('../constants/audio')
6
+ const { storage } = require('../storage')
7
+ const LLMObsPlugin = require('./base')
8
+
9
+ class SonicLLMObsPlugin extends LLMObsPlugin {
10
+ static id = 'bedrockruntime_sonic_llmobs'
11
+ static integration = 'bedrock'
12
+ static system = 'aws.bedrock'
13
+ static prefix = 'tracing:apm:aws:bedrockruntime:sonic:span'
14
+ static emitsGenAiApmTags = false
15
+
16
+ constructor (...args) {
17
+ super(...args)
18
+ this.addSub('dd-trace:aws:bedrockruntime:sonic:capture-context', context => {
19
+ if (!this._llmobsEnabled) return
20
+ context.enabled = true
21
+ // Capture once at invocation, even when absent or already finished. Scoped replay restores
22
+ // the consumer's context on every exit, including exceptions, without repairing IDs later.
23
+ const store = storage.getStore()
24
+ const previous = context.runInContext ?? (fn => fn())
25
+ context.runInContext = fn => previous(() => storage.run(store, fn))
26
+ })
27
+ }
28
+
29
+ /** @param {object} ctx */
30
+ getLLMObsSpanRegisterOptions (ctx) {
31
+ const options = {
32
+ kind: ctx.kind,
33
+ name: ctx.name,
34
+ sessionId: ctx.descriptor.sessionId,
35
+ }
36
+ if (ctx.kind === 'llm') {
37
+ options.modelName = ctx.model
38
+ options.modelProvider = 'amazon'
39
+ }
40
+ return options
41
+ }
42
+
43
+ /** @param {object} ctx */
44
+ start (ctx) {
45
+ super.start(ctx)
46
+ // Replay already has the complete descriptor. Annotate before any end subscriber can finish
47
+ // the span, including when disabling/re-enabling LLMObs changes subscriber registration order.
48
+ if (this._llmobsEnabledFor(ctx)) this.setLLMObsTags(ctx)
49
+ }
50
+
51
+ /** @param {object} ctx */
52
+ setLLMObsTags (ctx) {
53
+ const span = ctx.currentStore?.span
54
+ if (!span) return
55
+ const { turn, history, metadata } = ctx.descriptor
56
+ const text = turn.finalText || turn.speculativeText
57
+ if (ctx.kind !== 'llm') {
58
+ if (ctx.name === 'nova sonic audio turn') this._tagger.tagTextIO(span, turn.userText, text)
59
+ return
60
+ }
61
+
62
+ const input = { role: 'user', content: turn.userText || (turn.inputAudioObserved ? AUDIO_FALLBACK : '') }
63
+ const output = { role: 'assistant', content: text || (turn.outputAudioObserved ? AUDIO_FALLBACK : '') }
64
+ if (turn.tools.length) output.toolCalls = turn.tools
65
+ const inputs = [...history, input, ...turn.toolResults]
66
+ const outputs = [output]
67
+ // Reserve the serialized text/tools and metadata before spending one budget across both WAVs.
68
+ let budget = Math.max(0, LLMOBS_AUDIO_INLINE_MAX_BYTES -
69
+ Buffer.byteLength(JSON.stringify([inputs, outputs, metadata])) - 256)
70
+ for (const [message, pcm, rate] of [
71
+ [input, turn.inputPcm, turn.inputRate],
72
+ [output, turn.output.pcm(), turn.output.rate],
73
+ ]) {
74
+ if (!pcm?.length || !rate) continue
75
+ const part = fitsInlineAudioBudget(pcm.length + 44, budget)
76
+ ? formatAudioPartWithGuard(pcm16ToWav(pcm, rate), 'audio/wav', budget)
77
+ : undefined
78
+ if (part) {
79
+ message.audioParts = [part]
80
+ budget -= part.content.length
81
+ } else if (message === output) {
82
+ metadata.output_audio_omitted_reason = 'payload_limit'
83
+ }
84
+ }
85
+ if (turn.output.omittedReason) metadata.output_audio_omitted_reason = turn.output.omittedReason
86
+ this._tagger.tagLLMIO(span, inputs, outputs)
87
+ this._tagger.tagMetadata(span, metadata)
88
+ this._tagger.tagMetrics(span, turn.metrics)
89
+ }
90
+ }
91
+
92
+ module.exports = SonicLLMObsPlugin