dd-trace 6.15.0 → 6.17.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 (378) hide show
  1. package/README.md +15 -0
  2. package/ci/diagnose.js +0 -23
  3. package/ci/test-optimization-validation/approval-artifacts.js +0 -3
  4. package/ci/test-optimization-validation/approval.js +0 -6
  5. package/ci/test-optimization-validation/artifact-id.js +0 -1
  6. package/ci/test-optimization-validation/bounded-json.js +0 -1
  7. package/ci/test-optimization-validation/cli.js +0 -12
  8. package/ci/test-optimization-validation/command-output-policy.js +0 -1
  9. package/ci/test-optimization-validation/command-runner.js +0 -3
  10. package/ci/test-optimization-validation/environment.js +0 -4
  11. package/ci/test-optimization-validation/executable-approval.js +0 -1
  12. package/ci/test-optimization-validation/executable.js +0 -5
  13. package/ci/test-optimization-validation/framework-adapters/cucumber.js +4 -8
  14. package/ci/test-optimization-validation/framework-adapters/cypress.js +0 -3
  15. package/ci/test-optimization-validation/framework-adapters/playwright.js +0 -6
  16. package/ci/test-optimization-validation/framework-adapters/vitest.js +0 -4
  17. package/ci/test-optimization-validation/generated-test-contract.js +0 -5
  18. package/ci/test-optimization-validation/generated-verifier.js +0 -1
  19. package/ci/test-optimization-validation/literal-environment.js +0 -1
  20. package/ci/test-optimization-validation/manifest-scaffold.js +0 -17
  21. package/ci/test-optimization-validation/manifest-schema.js +0 -21
  22. package/ci/test-optimization-validation/offline-fixtures.js +0 -4
  23. package/ci/test-optimization-validation/package-check.js +4 -1
  24. package/ci/test-optimization-validation/plan-writer.js +0 -11
  25. package/ci/test-optimization-validation/preflight-runner.js +0 -1
  26. package/ci/test-optimization-validation/redaction.js +0 -4
  27. package/ci/test-optimization-validation/report-writer.js +0 -16
  28. package/ci/test-optimization-validation/runner-command.js +0 -3
  29. package/ci/test-optimization-validation/runner-contract.js +0 -4
  30. package/ci/test-optimization-validation/safe-files.js +0 -1
  31. package/ci/test-optimization-validation/scenarios/basic-reporting.js +0 -1
  32. package/ci/test-optimization-validation/scenarios/ci-wiring.js +1 -2
  33. package/ci/test-optimization-validation/source-text.js +0 -3
  34. package/ci/vitest-no-worker-init-setup.mjs +41 -16
  35. package/index.d.ts +169 -0
  36. package/next.d.ts +27 -0
  37. package/next.js +3 -0
  38. package/package.json +28 -23
  39. package/packages/datadog-esbuild/index.js +14 -1
  40. package/packages/datadog-esbuild/src/resolver.js +284 -0
  41. package/packages/datadog-esbuild/src/utils.js +162 -120
  42. package/packages/datadog-instrumentations/src/aerospike.js +28 -6
  43. package/packages/datadog-instrumentations/src/ai.js +39 -97
  44. package/packages/datadog-instrumentations/src/anthropic.js +76 -134
  45. package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +1 -3
  46. package/packages/datadog-instrumentations/src/aws-sdk.js +0 -2
  47. package/packages/datadog-instrumentations/src/azure-cosmos.js +1 -1
  48. package/packages/datadog-instrumentations/src/azure-durable-functions.js +23 -9
  49. package/packages/datadog-instrumentations/src/azure-functions.js +48 -0
  50. package/packages/datadog-instrumentations/src/bullmq.js +1 -1
  51. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +1 -3
  52. package/packages/datadog-instrumentations/src/console.js +97 -0
  53. package/packages/datadog-instrumentations/src/cucumber.js +72 -12
  54. package/packages/datadog-instrumentations/src/cypress-config.js +0 -23
  55. package/packages/datadog-instrumentations/src/dd-trace-api.js +2 -2
  56. package/packages/datadog-instrumentations/src/graphql.js +1 -1
  57. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +39 -14
  58. package/packages/datadog-instrumentations/src/helpers/channel.js +0 -2
  59. package/packages/datadog-instrumentations/src/helpers/graphql-jit-runtime.js +0 -5
  60. package/packages/datadog-instrumentations/src/helpers/hooks.js +8 -0
  61. package/packages/datadog-instrumentations/src/helpers/instrument.js +25 -8
  62. package/packages/datadog-instrumentations/src/helpers/instrumentation-utils.js +69 -0
  63. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +0 -6
  64. package/packages/datadog-instrumentations/src/helpers/register.js +13 -64
  65. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +109 -29
  66. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentation-registry.js +39 -0
  67. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/azure-durable-functions.js +17 -0
  68. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +3 -16
  69. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +12 -0
  70. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/postgres.js +36 -0
  71. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/supabase.js +128 -0
  72. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +6 -7
  73. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +17 -0
  74. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms/postgres.js +388 -0
  75. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +99 -75
  76. package/packages/datadog-instrumentations/src/helpers/router-helper.js +0 -1
  77. package/packages/datadog-instrumentations/src/jest.js +141 -51
  78. package/packages/datadog-instrumentations/src/knex.js +0 -1
  79. package/packages/datadog-instrumentations/src/langchain.js +1 -1
  80. package/packages/datadog-instrumentations/src/langgraph.js +1 -1
  81. package/packages/datadog-instrumentations/src/mariadb-bundle.js +0 -24
  82. package/packages/datadog-instrumentations/src/mariadb.js +0 -2
  83. package/packages/datadog-instrumentations/src/mercurius.js +1 -1
  84. package/packages/datadog-instrumentations/src/mocha/common.js +0 -1
  85. package/packages/datadog-instrumentations/src/mocha/main.js +22 -25
  86. package/packages/datadog-instrumentations/src/mocha/utils.js +119 -49
  87. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +0 -6
  88. package/packages/datadog-instrumentations/src/mocha/worker.js +14 -17
  89. package/packages/datadog-instrumentations/src/modelcontextprotocol-sdk.js +1 -1
  90. package/packages/datadog-instrumentations/src/mysql2.js +0 -2
  91. package/packages/datadog-instrumentations/src/openai-agents.js +1 -1
  92. package/packages/datadog-instrumentations/src/openai-realtime/audio-accumulator.js +241 -0
  93. package/packages/datadog-instrumentations/src/openai-realtime/events.js +76 -0
  94. package/packages/datadog-instrumentations/src/openai-realtime/index.js +301 -0
  95. package/packages/datadog-instrumentations/src/openai-realtime/session.js +998 -0
  96. package/packages/datadog-instrumentations/src/openai-realtime/turns.js +211 -0
  97. package/packages/datadog-instrumentations/src/openai.js +97 -80
  98. package/packages/datadog-instrumentations/src/oracledb.js +9 -3
  99. package/packages/datadog-instrumentations/src/playwright-reporter.js +3 -16
  100. package/packages/datadog-instrumentations/src/playwright.js +238 -55
  101. package/packages/datadog-instrumentations/src/postgres.js +7 -0
  102. package/packages/datadog-instrumentations/src/router.js +0 -2
  103. package/packages/datadog-instrumentations/src/rum-browser-scripts.js +0 -2
  104. package/packages/datadog-instrumentations/src/sequelize.js +0 -1
  105. package/packages/datadog-instrumentations/src/supabase.js +15 -0
  106. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +18 -18
  107. package/packages/datadog-instrumentations/src/vitest-main.js +37 -17
  108. package/packages/datadog-instrumentations/src/vitest-util.js +7 -1
  109. package/packages/datadog-instrumentations/src/vitest-worker.js +56 -23
  110. package/packages/datadog-instrumentations/src/webdriverio.js +23 -42
  111. package/packages/datadog-instrumentations/src/ws.js +0 -1
  112. package/packages/datadog-plugin-ai/src/utils.js +0 -2
  113. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +0 -1
  114. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +0 -2
  115. package/packages/datadog-plugin-aws-sdk/src/base.js +0 -1
  116. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +7 -2
  117. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +0 -1
  118. package/packages/datadog-plugin-aws-sdk/src/util.js +0 -1
  119. package/packages/datadog-plugin-azure-cosmos/src/index.js +1 -1
  120. package/packages/datadog-plugin-azure-durable-functions/src/index.js +95 -1
  121. package/packages/datadog-plugin-cucumber/src/index.js +10 -0
  122. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +171 -75
  123. package/packages/datadog-plugin-cypress/src/finalization.js +0 -3
  124. package/packages/datadog-plugin-cypress/src/source-map-utils.js +0 -4
  125. package/packages/datadog-plugin-cypress/src/support.js +52 -7
  126. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -24
  127. package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +2 -36
  128. package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-request-context.js +31 -0
  129. package/packages/datadog-plugin-graphql/src/resolve-error.js +32 -11
  130. package/packages/datadog-plugin-graphql/src/utils.js +0 -5
  131. package/packages/datadog-plugin-jest/src/index.js +13 -0
  132. package/packages/datadog-plugin-kafkajs/src/producer.js +0 -1
  133. package/packages/datadog-plugin-mocha/src/index.js +39 -11
  134. package/packages/datadog-plugin-mongodb-core/src/query.js +0 -1
  135. package/packages/datadog-plugin-openai/src/index.js +16 -1
  136. package/packages/datadog-plugin-openai/src/realtime.js +144 -0
  137. package/packages/datadog-plugin-openai-agents/src/integration.js +0 -4
  138. package/packages/datadog-plugin-openai-agents/src/util.js +0 -1
  139. package/packages/datadog-plugin-playwright/src/index.js +76 -9
  140. package/packages/datadog-plugin-postgres/src/index.js +167 -0
  141. package/packages/datadog-plugin-prisma/src/datadog-tracing-helper.js +0 -1
  142. package/packages/datadog-plugin-redis/src/index.js +15 -1
  143. package/packages/datadog-plugin-supabase/src/error.js +18 -0
  144. package/packages/datadog-plugin-supabase/src/index.js +21 -0
  145. package/packages/datadog-plugin-supabase/src/supabase-auth-js-gotrueclient-getuser.js +69 -0
  146. package/packages/datadog-plugin-supabase/src/supabase-functions-js-functionsclient-invoke.js +70 -0
  147. package/packages/datadog-plugin-supabase/src/supabase-postgrest-js-postgrestbuilder-then.js +122 -0
  148. package/packages/datadog-plugin-supabase/src/supabase-realtime-js-realtimechannel-send.js +66 -0
  149. package/packages/datadog-plugin-supabase/src/supabase-storage-js-handle-request.js +93 -0
  150. package/packages/datadog-plugin-supabase/src/url.js +17 -0
  151. package/packages/datadog-plugin-vitest/src/index.js +10 -0
  152. package/packages/datadog-plugin-ws/src/util.js +0 -4
  153. package/packages/datadog-turbopack/index.js +206 -0
  154. package/packages/datadog-turbopack/src/constants.js +5 -0
  155. package/packages/datadog-turbopack/src/loader.js +484 -0
  156. package/packages/datadog-webpack/src/loader.js +0 -1
  157. package/packages/dd-trace/src/aiguard/client.js +2 -3
  158. package/packages/dd-trace/src/aiguard/evaluation.js +6 -12
  159. package/packages/dd-trace/src/aiguard/index.js +3 -2
  160. package/packages/dd-trace/src/aiguard/integrations/anthropic.js +53 -25
  161. package/packages/dd-trace/src/aiguard/integrations/evaluate.js +49 -25
  162. package/packages/dd-trace/src/aiguard/integrations/index.js +4 -3
  163. package/packages/dd-trace/src/aiguard/integrations/openai.js +132 -29
  164. package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +116 -42
  165. package/packages/dd-trace/src/aiguard/messages/anthropic.js +0 -2
  166. package/packages/dd-trace/src/aiguard/messages/openai.js +64 -1
  167. package/packages/dd-trace/src/aiguard/messages/utils.js +29 -2
  168. package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +25 -0
  169. package/packages/dd-trace/src/aiguard/redaction.js +0 -1
  170. package/packages/dd-trace/src/aiguard/sdk.js +1 -1
  171. package/packages/dd-trace/src/appsec/activation.js +1 -1
  172. package/packages/dd-trace/src/appsec/api_security/normalized-route.js +0 -5
  173. package/packages/dd-trace/src/appsec/api_security/sampler.js +0 -4
  174. package/packages/dd-trace/src/appsec/blocking/index.js +3 -3
  175. package/packages/dd-trace/src/appsec/downstream_requests.js +1 -4
  176. package/packages/dd-trace/src/appsec/iast/index.js +1 -1
  177. package/packages/dd-trace/src/appsec/iast/overhead-controller.js +5 -5
  178. package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +1 -1
  179. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +1 -1
  180. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +3 -3
  181. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +8 -10
  182. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +2 -2
  183. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +2 -2
  184. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +4 -1
  185. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +7 -7
  186. package/packages/dd-trace/src/appsec/index.js +2 -2
  187. package/packages/dd-trace/src/appsec/lambda.js +71 -2
  188. package/packages/dd-trace/src/appsec/rasp/utils.js +5 -1
  189. package/packages/dd-trace/src/appsec/remote_config.js +8 -6
  190. package/packages/dd-trace/src/appsec/reporter.js +10 -6
  191. package/packages/dd-trace/src/appsec/rule_manager.js +3 -3
  192. package/packages/dd-trace/src/appsec/sdk/track_event.js +0 -2
  193. package/packages/dd-trace/src/appsec/telemetry/api_security.js +1 -3
  194. package/packages/dd-trace/src/appsec/telemetry/common.js +4 -0
  195. package/packages/dd-trace/src/appsec/telemetry/index.js +23 -5
  196. package/packages/dd-trace/src/appsec/telemetry/rasp.js +1 -20
  197. package/packages/dd-trace/src/appsec/telemetry/user.js +1 -3
  198. package/packages/dd-trace/src/appsec/telemetry/waf.js +1 -20
  199. package/packages/dd-trace/src/appsec/waf/index.js +2 -2
  200. package/packages/dd-trace/src/appsec/waf/waf_manager.js +8 -5
  201. package/packages/dd-trace/src/carrier.js +0 -5
  202. package/packages/dd-trace/src/ci-visibility/dynamic-atr-retries.js +104 -0
  203. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +0 -2
  204. package/packages/dd-trace/src/ci-visibility/efd-retry-policy.js +34 -3
  205. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +0 -1
  206. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +0 -1
  207. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +0 -1
  208. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/index.js +0 -4
  209. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +0 -1
  210. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/sink.js +0 -2
  211. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +37 -15
  212. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +0 -3
  213. package/packages/dd-trace/src/ci-visibility/exporters/request.js +0 -11
  214. package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +0 -1
  215. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +0 -1
  216. package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +0 -6
  217. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +2 -2
  218. package/packages/dd-trace/src/ci-visibility/requests/rate-limit.js +0 -1
  219. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +0 -5
  220. package/packages/dd-trace/src/ci-visibility/requests/video-request.js +0 -1
  221. package/packages/dd-trace/src/ci-visibility/telemetry.js +13 -0
  222. package/packages/dd-trace/src/ci-visibility/test-optimization-cache.js +0 -1
  223. package/packages/dd-trace/src/ci-visibility/test-screenshot.js +0 -2
  224. package/packages/dd-trace/src/ci-visibility/test-video.js +0 -1
  225. package/packages/dd-trace/src/config/defaults.js +3 -2
  226. package/packages/dd-trace/src/config/generated-config-types.d.ts +74 -66
  227. package/packages/dd-trace/src/config/helper.js +9 -2
  228. package/packages/dd-trace/src/config/index.js +44 -29
  229. package/packages/dd-trace/src/config/parsers.js +34 -6
  230. package/packages/dd-trace/src/config/remote_config.js +25 -109
  231. package/packages/dd-trace/src/config/supported-configurations.json +146 -9
  232. package/packages/dd-trace/src/datastreams/encoding.js +0 -1
  233. package/packages/dd-trace/src/datastreams/pathway.js +0 -1
  234. package/packages/dd-trace/src/debugger/constants.js +10 -0
  235. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +76 -17
  236. package/packages/dd-trace/src/debugger/devtools_client/config.js +2 -0
  237. package/packages/dd-trace/src/debugger/devtools_client/guardrail-metrics.js +15 -0
  238. package/packages/dd-trace/src/debugger/devtools_client/index.js +103 -27
  239. package/packages/dd-trace/src/debugger/devtools_client/json-buffer.js +50 -7
  240. package/packages/dd-trace/src/debugger/devtools_client/log.js +3 -1
  241. package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +34 -8
  242. package/packages/dd-trace/src/debugger/devtools_client/send.js +40 -14
  243. package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +53 -13
  244. package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +118 -71
  245. package/packages/dd-trace/src/debugger/devtools_client/snapshot-pruner.js +0 -3
  246. package/packages/dd-trace/src/debugger/devtools_client/status.js +20 -6
  247. package/packages/dd-trace/src/debugger/guardrail-metrics.js +167 -0
  248. package/packages/dd-trace/src/debugger/index.js +50 -2
  249. package/packages/dd-trace/src/debugger/inspect-segment.js +0 -3
  250. package/packages/dd-trace/src/debugger/probe_sampler.js +133 -9
  251. package/packages/dd-trace/src/debugger/probe_sampler_constants.js +31 -1
  252. package/packages/dd-trace/src/dogstatsd.js +0 -23
  253. package/packages/dd-trace/src/encode/0.4-cross-payload.js +0 -3
  254. package/packages/dd-trace/src/encode/0.4.js +0 -7
  255. package/packages/dd-trace/src/encode/0.5.js +0 -2
  256. package/packages/dd-trace/src/encode/tags-processors.js +0 -1
  257. package/packages/dd-trace/src/evp_proxy/discovery.js +3 -5
  258. package/packages/dd-trace/src/evp_proxy/path.js +11 -4
  259. package/packages/dd-trace/src/exporters/agent/writer.js +0 -1
  260. package/packages/dd-trace/src/exporters/agentless/index.js +0 -1
  261. package/packages/dd-trace/src/exporters/agentless/intake.js +0 -1
  262. package/packages/dd-trace/src/exporters/agentless/writer.js +0 -3
  263. package/packages/dd-trace/src/exporters/common/docker.js +4 -1
  264. package/packages/dd-trace/src/exporters/common/final-flush-request-tracker.js +0 -6
  265. package/packages/dd-trace/src/exporters/common/proxy.js +3 -0
  266. package/packages/dd-trace/src/exporters/common/request.js +1 -0
  267. package/packages/dd-trace/src/exporters/common/url.js +0 -2
  268. package/packages/dd-trace/src/exporters/common/writer.js +0 -2
  269. package/packages/dd-trace/src/id.js +0 -11
  270. package/packages/dd-trace/src/llmobs/audio-codec.js +192 -0
  271. package/packages/dd-trace/src/llmobs/audio-utils.js +121 -0
  272. package/packages/dd-trace/src/llmobs/constants/audio.js +35 -0
  273. package/packages/dd-trace/src/llmobs/constants/prompts.js +14 -0
  274. package/packages/dd-trace/src/llmobs/constants/tags.js +17 -0
  275. package/packages/dd-trace/src/llmobs/eval-metric.js +0 -6
  276. package/packages/dd-trace/src/llmobs/experiments/dataset.js +0 -4
  277. package/packages/dd-trace/src/llmobs/experiments/experiment.js +0 -3
  278. package/packages/dd-trace/src/llmobs/experiments/index.js +7 -6
  279. package/packages/dd-trace/src/llmobs/experiments/util.js +1 -11
  280. package/packages/dd-trace/src/llmobs/index.js +1 -3
  281. package/packages/dd-trace/src/llmobs/noop.js +10 -0
  282. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +0 -4
  283. package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +0 -1
  284. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +0 -4
  285. package/packages/dd-trace/src/llmobs/plugins/modelcontextprotocol-sdk/utils.js +0 -2
  286. package/packages/dd-trace/src/llmobs/plugins/openai/constants.js +0 -2
  287. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +4 -6
  288. package/packages/dd-trace/src/llmobs/plugins/openai/realtime.js +285 -0
  289. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +16 -3
  290. package/packages/dd-trace/src/llmobs/prompts/cache.js +272 -0
  291. package/packages/dd-trace/src/llmobs/prompts/manager.js +639 -0
  292. package/packages/dd-trace/src/llmobs/prompts/noop.js +29 -0
  293. package/packages/dd-trace/src/llmobs/prompts/prompt.js +92 -0
  294. package/packages/dd-trace/src/llmobs/sdk.js +21 -6
  295. package/packages/dd-trace/src/llmobs/span_processor.js +81 -2
  296. package/packages/dd-trace/src/llmobs/tagger.js +30 -7
  297. package/packages/dd-trace/src/llmobs/telemetry.js +36 -4
  298. package/packages/dd-trace/src/llmobs/util.js +0 -34
  299. package/packages/dd-trace/src/llmobs/writers/base.js +0 -1
  300. package/packages/dd-trace/src/llmobs/writers/util.js +1 -2
  301. package/packages/dd-trace/src/log/channels.js +3 -0
  302. package/packages/dd-trace/src/log/index.js +23 -5
  303. package/packages/dd-trace/src/log-submission/log-submission-plugin.js +74 -6
  304. package/packages/dd-trace/src/msgpack/chunk.js +0 -1
  305. package/packages/dd-trace/src/openfeature/agentless_configuration_source.js +0 -12
  306. package/packages/dd-trace/src/openfeature/encoding.js +0 -2
  307. package/packages/dd-trace/src/openfeature/eval-metrics-hook.js +0 -1
  308. package/packages/dd-trace/src/openfeature/index.js +0 -4
  309. package/packages/dd-trace/src/openfeature/span-enrichment-hook.js +0 -1
  310. package/packages/dd-trace/src/openfeature/span-enrichment.js +0 -4
  311. package/packages/dd-trace/src/openfeature/writers/base.js +0 -6
  312. package/packages/dd-trace/src/openfeature/writers/exposures.js +0 -5
  313. package/packages/dd-trace/src/openfeature/writers/util.js +0 -4
  314. package/packages/dd-trace/src/opentelemetry/logs/logger.js +0 -1
  315. package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +0 -1
  316. package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +0 -1
  317. package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +0 -2
  318. package/packages/dd-trace/src/opentelemetry/metrics/index.js +1 -1
  319. package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +4 -4
  320. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +0 -1
  321. package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +0 -1
  322. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +103 -35
  323. package/packages/dd-trace/src/opentelemetry/metrics/time.js +0 -1
  324. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +36 -13
  325. package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +0 -1
  326. package/packages/dd-trace/src/opentelemetry/span-helpers.js +0 -1
  327. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +0 -1
  328. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +0 -3
  329. package/packages/dd-trace/src/opentracing/propagation/text_map.js +0 -6
  330. package/packages/dd-trace/src/opentracing/span_context.js +0 -1
  331. package/packages/dd-trace/src/otel-thread-ctx.js +38 -11
  332. package/packages/dd-trace/src/payload-tagging/tagging.js +15 -3
  333. package/packages/dd-trace/src/plugin_manager.js +9 -3
  334. package/packages/dd-trace/src/plugins/ci_plugin.js +0 -1
  335. package/packages/dd-trace/src/plugins/database.js +2 -6
  336. package/packages/dd-trace/src/plugins/index.js +8 -0
  337. package/packages/dd-trace/src/plugins/plugin.js +0 -4
  338. package/packages/dd-trace/src/plugins/tracing.js +1 -1
  339. package/packages/dd-trace/src/plugins/util/git.js +1 -3
  340. package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +0 -1
  341. package/packages/dd-trace/src/plugins/util/llm.js +0 -2
  342. package/packages/dd-trace/src/plugins/util/status-validator.js +0 -1
  343. package/packages/dd-trace/src/plugins/util/test.js +25 -21
  344. package/packages/dd-trace/src/plugins/util/url.js +0 -5
  345. package/packages/dd-trace/src/priority_sampler.js +0 -6
  346. package/packages/dd-trace/src/process-tags/index.js +0 -1
  347. package/packages/dd-trace/src/profiler.js +132 -35
  348. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +0 -1
  349. package/packages/dd-trace/src/profiling/profiler.js +101 -41
  350. package/packages/dd-trace/src/profiling/ssi-heuristics.js +29 -15
  351. package/packages/dd-trace/src/propagation-hash/index.js +0 -1
  352. package/packages/dd-trace/src/proxy.js +21 -45
  353. package/packages/dd-trace/src/random_sampler.js +0 -4
  354. package/packages/dd-trace/src/rate_limiter.js +0 -4
  355. package/packages/dd-trace/src/remote_config/capabilities.js +1 -9
  356. package/packages/dd-trace/src/remote_config/index.js +0 -1
  357. package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +0 -3
  358. package/packages/dd-trace/src/sampler.js +0 -4
  359. package/packages/dd-trace/src/sampling_rule.js +0 -7
  360. package/packages/dd-trace/src/serverless/vercel.js +0 -1
  361. package/packages/dd-trace/src/serverless.js +0 -1
  362. package/packages/dd-trace/src/service-naming/index.js +0 -1
  363. package/packages/dd-trace/src/service-naming/schemas/util.js +0 -1
  364. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +7 -0
  365. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +5 -0
  366. package/packages/dd-trace/src/span_processor.js +2 -2
  367. package/packages/dd-trace/src/span_sampler.js +0 -1
  368. package/packages/dd-trace/src/standalone/product.js +2 -2
  369. package/packages/dd-trace/src/standalone/tracesource.js +0 -1
  370. package/packages/dd-trace/src/startup-log.js +1 -1
  371. package/packages/dd-trace/src/telemetry/endpoints.js +0 -8
  372. package/packages/dd-trace/src/telemetry/logs/index.js +16 -5
  373. package/packages/dd-trace/src/telemetry/logs/log-collector.js +16 -6
  374. package/packages/dd-trace/src/telemetry/metrics.js +0 -2
  375. package/packages/dd-trace/src/telemetry/telemetry.js +4 -1
  376. package/packages/dd-trace/src/util.js +13 -1
  377. package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
  378. package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/utils.js +0 -10
@@ -330,7 +330,6 @@ class Dataset {
330
330
  * @param {string} recordId Dataset record id.
331
331
  * @param {'add' | 'remove' | 'replace'} operation Tag operation to queue.
332
332
  * @param {string[]} tags Tags in key:value format.
333
- * @returns {void}
334
333
  */
335
334
  #queueTagOperation (recordId, operation, tags) {
336
335
  if (operation !== 'replace' && tags.length === 0) return
@@ -503,7 +502,6 @@ class Dataset {
503
502
  * Detach tag changes sent by this batch so edits made while the request is in flight
504
503
  * are queued relative to the response that this batch will commit.
505
504
  * @param {PendingBatch} pending
506
- * @returns {void}
507
505
  */
508
506
  #detachCommittedTagOperations (pending) {
509
507
  pending.inFlightTagOperations = new Map()
@@ -527,7 +525,6 @@ class Dataset {
527
525
  /**
528
526
  * Restore tag changes when a batch request fails, including edits made while it was in flight.
529
527
  * @param {PendingBatch} pending
530
- * @returns {void}
531
528
  */
532
529
  #restoreFailedTagOperations (pending) {
533
530
  if (!pending.inFlightTagOperations) return
@@ -549,7 +546,6 @@ class Dataset {
549
546
  /**
550
547
  * Clear the changes represented by a completed batch while retaining concurrent local edits.
551
548
  * @param {PendingBatch} pending
552
- * @returns {void}
553
549
  */
554
550
  #clearCommittedChanges (pending) {
555
551
  for (const [recordId, payload] of pending.insertPayloads) {
@@ -1015,9 +1015,6 @@ class ExternalExperiment {
1015
1015
  this.#experiment = experiment
1016
1016
  }
1017
1017
 
1018
- /**
1019
- * @returns {string}
1020
- */
1021
1018
  name () {
1022
1019
  return this.#experiment.name()
1023
1020
  }
@@ -35,8 +35,8 @@ class Experiments {
35
35
 
36
36
  constructor (config, llmobs) {
37
37
  this.#config = config
38
- this.#llmobs = config.llmobs?.mlApp || config.service ? llmobs : undefined
39
- this.#projectName = config.llmobs?.projectName || DEFAULT_PROJECT_NAME
38
+ this.#llmobs = config.llmobs?.DD_LLMOBS_ML_APP || config.service ? llmobs : undefined
39
+ this.#projectName = config.llmobs?.DD_LLMOBS_PROJECT_NAME || DEFAULT_PROJECT_NAME
40
40
  this.#client = this.#clientForProject(this.#projectName)
41
41
  }
42
42
 
@@ -179,9 +179,9 @@ class Experiments {
179
179
  const projectName = options?.projectName ?? datasetProjectName
180
180
  const client = this.#clientForOperation(projectName)
181
181
  const usesDatasetOverride = datasetProjectName !== undefined && datasetProjectName !== this.#projectName
182
- const resolvedProjectName = projectName ?? this.#config.llmobs?.projectName
182
+ const resolvedProjectName = projectName ?? this.#config.llmobs?.DD_LLMOBS_PROJECT_NAME
183
183
  const experimentOptions = options?.projectName === undefined &&
184
- (usesDatasetOverride || this.#config.llmobs?.projectName !== undefined) &&
184
+ (usesDatasetOverride || this.#config.llmobs?.DD_LLMOBS_PROJECT_NAME !== undefined) &&
185
185
  resolvedProjectName !== undefined
186
186
  ? { ...options, projectName: resolvedProjectName }
187
187
  : options
@@ -198,8 +198,9 @@ class Experiments {
198
198
  */
199
199
  startExperiment (options) {
200
200
  const client = this.#clientForOperation(options?.projectName)
201
- const experimentOptions = options?.projectName === undefined && this.#config.llmobs?.projectName !== undefined
202
- ? { ...options, projectName: this.#config.llmobs.projectName }
201
+ const experimentOptions = options?.projectName === undefined &&
202
+ this.#config.llmobs?.DD_LLMOBS_PROJECT_NAME !== undefined
203
+ ? { ...options, projectName: this.#config.llmobs.DD_LLMOBS_PROJECT_NAME }
203
204
  : options
204
205
  return new Experiment(client, { ...experimentOptions, external: true }).start()
205
206
  .then(experiment => new ExternalExperiment(experiment))
@@ -14,7 +14,6 @@ const EVALUATOR_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/
14
14
 
15
15
  /**
16
16
  * @param {object | null | undefined} value
17
- * @returns {boolean}
18
17
  */
19
18
  function hasEntries (value) {
20
19
  if (!value) return false
@@ -47,16 +46,12 @@ function tagOperationsAreEmpty (operations) {
47
46
  /**
48
47
  * @param {unknown} value
49
48
  * @param {string} name
50
- * @returns {number}
51
49
  */
52
50
  function normalizePositiveInteger (value, name) {
53
51
  if (!Number.isInteger(value) || value < 1) throw new Error(`${name} must be a positive integer`)
54
- return value
52
+ return /** @type {number} */ (value)
55
53
  }
56
54
 
57
- /**
58
- * @returns {string}
59
- */
60
55
  function generateRunId () {
61
56
  return randomUUID()
62
57
  }
@@ -77,7 +72,6 @@ function validateEvaluatorName (name) {
77
72
  /**
78
73
  * @param {(...args: unknown[]) => unknown} fn
79
74
  * @param {string} fallback
80
- * @returns {string}
81
75
  */
82
76
  function functionName (fn, fallback) {
83
77
  return typeof fn.name === 'string' && fn.name.length > 0 ? fn.name : fallback
@@ -124,7 +118,6 @@ function normalizeEvaluators (evaluators, kind) {
124
118
 
125
119
  /**
126
120
  * @param {unknown} value
127
- * @returns {string}
128
121
  */
129
122
  function inferMetricType (value) {
130
123
  if (typeof value === 'boolean') return 'boolean'
@@ -184,7 +177,6 @@ function normalizeJsonMetricValue (value) {
184
177
 
185
178
  /**
186
179
  * @param {unknown} value
187
- * @returns {string}
188
180
  */
189
181
  function stringify (value) {
190
182
  if (value == null) return ''
@@ -270,7 +262,6 @@ function sleep (ms) {
270
262
  /**
271
263
  * @param {unknown} value
272
264
  * @param {number} fallback
273
- * @returns {number}
274
265
  */
275
266
  function timestampMs (value, fallback = Date.now()) {
276
267
  if (value === null || value === undefined) return fallback
@@ -286,7 +277,6 @@ function timestampMs (value, fallback = Date.now()) {
286
277
  /**
287
278
  * @param {{ durationMs?: unknown, completedAt?: unknown }} row
288
279
  * @param {number} startMs
289
- * @returns {number}
290
280
  */
291
281
  function durationNs (row, startMs) {
292
282
  if (typeof row.durationMs === 'number' && Number.isFinite(row.durationMs)) {
@@ -152,7 +152,6 @@ function disable () {
152
152
  * Keeps retired writers reachable until their destroy-triggered deliveries complete.
153
153
  * @param {LLMObsSpanWriter | null} retiredSpanWriter
154
154
  * @param {LLMObsEvalMetricsWriter | null} retiredEvalWriter
155
- * @returns {void}
156
155
  */
157
156
  function retireWriters (retiredSpanWriter, retiredEvalWriter) {
158
157
  const retiredWriters = [retiredSpanWriter, retiredEvalWriter].filter(Boolean)
@@ -183,7 +182,7 @@ function handleLLMObsInjection (injection) {
183
182
  const mlApp =
184
183
  mlObsSpanTags?.[ML_APP] ||
185
184
  parentContext?._trace?.tags?.[PROPAGATED_ML_APP_KEY] ||
186
- globalTracerConfig.llmobs.mlApp
185
+ globalTracerConfig.llmobs.DD_LLMOBS_ML_APP
187
186
 
188
187
  const sampleRate =
189
188
  mlObsSpanTags?.[SAMPLE_RATE] ?? parentContext?._trace?.tags?.[PROPAGATED_SAMPLE_RATE_KEY]
@@ -239,7 +238,6 @@ function handleLLMObsInjection (injection) {
239
238
  * @param {Function} [done]
240
239
  * @param {LLMObsSpanWriter | null} [currentSpanWriter]
241
240
  * @param {LLMObsEvalMetricsWriter | null} [currentEvalWriter]
242
- * @returns {boolean} `true` when a writer throws synchronously.
243
241
  */
244
242
  function flushWriters (done, currentSpanWriter = spanWriter, currentEvalWriter = evalWriter) {
245
243
  let failed = false
@@ -1,5 +1,7 @@
1
1
  'use strict'
2
2
 
3
+ const NoopPrompts = require('./prompts/noop')
4
+
3
5
  let NoopExperiments
4
6
 
5
7
  class NoopLLMObs {
@@ -16,6 +18,14 @@ class NoopLLMObs {
16
18
  return new NoopExperiments('LLM Observability is not enabled')
17
19
  }
18
20
 
21
+ /**
22
+ * Prompt Management API.
23
+ * @returns {import('../../../../index').llmobs.Prompts}
24
+ */
25
+ get prompts () {
26
+ return new NoopPrompts()
27
+ }
28
+
19
29
  enable (options) {}
20
30
 
21
31
  disable () {}
@@ -337,7 +337,6 @@ function getToolNameFromTags (tags) {
337
337
 
338
338
  /**
339
339
  * @param {unknown} value
340
- * @returns {string}
341
340
  */
342
341
  function stringifyToolCallResult (value) {
343
342
  return JSON.stringify(value) ?? UNPARSABLE_TOOL_RESULT
@@ -345,7 +344,6 @@ function stringifyToolCallResult (value) {
345
344
 
346
345
  /**
347
346
  * @param {ToolCallContentPart[]} value
348
- * @returns {string}
349
347
  */
350
348
  function formatToolCallContent (value) {
351
349
  if (!Array.isArray(value)) return UNPARSABLE_TOOL_RESULT
@@ -388,7 +386,6 @@ function formatToolCallContent (value) {
388
386
 
389
387
  /**
390
388
  * @param {ToolCallResultContent | null | undefined} content
391
- * @returns {string}
392
389
  */
393
390
  function getToolCallResultContent (content) {
394
391
  try {
@@ -426,7 +423,6 @@ function getToolCallResultContent (content) {
426
423
  * Computes the LLM Observability `ai` span name
427
424
  * @param {string} operation
428
425
  * @param {string} functionId
429
- * @returns {string}
430
426
  */
431
427
  function getLlmObsSpanName (operation, functionId) {
432
428
  return functionId ? `${functionId}.${operation}` : operation
@@ -62,7 +62,6 @@ function formatAnthropicToolResultContent (content) {
62
62
  *
63
63
  * @param {AnthropicLlmObsMessage[]} messages
64
64
  * @param {{ role: string, content: string | Array<TextBlock | ImageBlock | ToolUseBlock | ToolResultBlock> }} message
65
- * @returns {void}
66
65
  */
67
66
  function appendMessage (messages, { role, content }) {
68
67
  if (typeof content === 'string') {
@@ -55,7 +55,6 @@ function groupPartsByRole (parts) {
55
55
  /**
56
56
  * Check if parts contain thought/reasoning content
57
57
  * @param {Array<{thought?: boolean}>} parts
58
- * @returns {boolean}
59
58
  */
60
59
  function hasThoughtParts (parts) {
61
60
  return parts.some(part => part.thought === true)
@@ -65,7 +64,6 @@ function hasThoughtParts (parts) {
65
64
  * Determine the role from a candidate and its parts
66
65
  * @param {object} candidate
67
66
  * @param {Array<{thought?: boolean}>} parts
68
- * @returns {string}
69
67
  */
70
68
  function determineRole (candidate, parts = []) {
71
69
  // Check parts for thought indicators
@@ -84,7 +82,6 @@ function determineRole (candidate, parts = []) {
84
82
  /**
85
83
  * Normalize role to standard values
86
84
  * @param {string} role
87
- * @returns {string}
88
85
  */
89
86
  function normalizeRole (role) {
90
87
  if (role === ROLES.MODEL) return ROLES.ASSISTANT
@@ -520,7 +517,6 @@ function formatOutputMessages (response, isStreaming = false) {
520
517
  /**
521
518
  * Format embedding output from response
522
519
  * @param {object} response
523
- * @returns {string}
524
520
  */
525
521
  function formatEmbeddingOutput (response) {
526
522
  if (!response?.embeddings?.length) {
@@ -4,7 +4,6 @@
4
4
  * Formats tool call input as a JSON string.
5
5
  * @param {string} toolName - The name of the tool being called
6
6
  * @param {object} toolArguments - The arguments passed to the tool
7
- * @returns {string} Formatted input string
8
7
  */
9
8
  function formatInput (toolName, toolArguments) {
10
9
  if (!toolName && !toolArguments) return ''
@@ -25,7 +24,6 @@ function formatInput (toolName, toolArguments) {
25
24
  * MCP tool results contain a `content` array with items like:
26
25
  * `[{ type: 'text', text: '...' }, { type: 'image', data: '...', mimeType: '...' }]`
27
26
  * @param {object} result - The MCP CallToolResult
28
- * @returns {string} JSON string of `{ content: Array<{type, text, annotations, meta}>, isError: boolean }`
29
27
  */
30
28
  function formatOutput (result) {
31
29
  if (!result) return ''
@@ -6,7 +6,6 @@ const INPUT_TYPE_TEXT = 'input_text'
6
6
 
7
7
  const IMAGE_FALLBACK = '[image]'
8
8
  const FILE_FALLBACK = '[file]'
9
- const AUDIO_FALLBACK = '[audio]'
10
9
 
11
10
  // OpenAI audio `format` values that don't map cleanly to `audio/<format>`.
12
11
  const AUDIO_MIME_TYPES = {
@@ -19,6 +18,5 @@ module.exports = {
19
18
  INPUT_TYPE_TEXT,
20
19
  IMAGE_FALLBACK,
21
20
  FILE_FALLBACK,
22
- AUDIO_FALLBACK,
23
21
  AUDIO_MIME_TYPES,
24
22
  }
@@ -6,7 +6,8 @@ const {
6
6
  PROMPT_MULTIMODAL,
7
7
  INSTRUMENTATION_METHOD_AUTO,
8
8
  } = require('../../constants/tags')
9
- const { audioMimeTypeFromFormat, formatAudioPart, safeJsonParse } = require('../../util')
9
+ const { audioMimeTypeFromFormat, formatAudioPart } = require('../../audio-utils')
10
+ const { safeJsonParse } = require('../../util')
10
11
  const { AUDIO_MIME_TYPES } = require('./constants')
11
12
  const {
12
13
  extractChatTemplateFromInstructions,
@@ -14,7 +15,7 @@ const {
14
15
  extractTextFromContentItem,
15
16
  extractContentParts,
16
17
  hasMultimodalInputs,
17
- getOpenAIModelProvider,
18
+ getModelProviderAndClient,
18
19
  } = require('./utils')
19
20
 
20
21
  const allowedParamKeys = new Set([
@@ -108,10 +109,7 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
108
109
  }
109
110
 
110
111
  _getModelProviderAndClient (baseUrl = '') {
111
- const modelProvider = getOpenAIModelProvider(baseUrl)
112
- if (modelProvider === 'azure_openai') return { modelProvider, client: 'AzureOpenAI' }
113
- if (modelProvider === 'deepseek') return { modelProvider, client: 'DeepSeek' }
114
- return { modelProvider, client: 'OpenAI' }
112
+ return getModelProviderAndClient(baseUrl)
115
113
  }
116
114
 
117
115
  _extractMetrics (response) {
@@ -0,0 +1,285 @@
1
+ 'use strict'
2
+
3
+ const {
4
+ g711ToPcm16,
5
+ g711Variant,
6
+ isPcm16AudioMime,
7
+ pcm16ToWav,
8
+ } = require('../../audio-codec')
9
+ const { fitsInlineAudioBudget, formatAudioPartWithGuard } = require('../../audio-utils')
10
+ const {
11
+ AUDIO_FALLBACK,
12
+ G711_SAMPLE_RATE,
13
+ LLMOBS_AUDIO_INLINE_MAX_BYTES,
14
+ PCM16_BYTES_PER_SAMPLE,
15
+ WAV_HEADER_BYTES,
16
+ } = require('../../constants/audio')
17
+ const { storage: llmobsStorage } = require('../../storage')
18
+ const { safeJsonParse } = require('../../util')
19
+ const LLMObsPlugin = require('../base')
20
+ const { getModelProviderAndClient } = require('./utils')
21
+
22
+ /**
23
+ * @typedef {import('../../audio-utils').AudioPart} AudioPart
24
+ * @typedef {{
25
+ * role: string,
26
+ * content: string,
27
+ * audioParts?: AudioPart[],
28
+ * toolCalls?: object[],
29
+ * toolResults?: object[],
30
+ * }} Message
31
+ */
32
+
33
+ /**
34
+ * Turn a turn side's raw audio into a playable audio part.
35
+ *
36
+ * Realtime audio is raw PCM16 (24 kHz mono) by default, which the UI can't render, so it is wrapped
37
+ * in a WAV container — lossless, just a header. G.711 telephony audio, used by phone-call
38
+ * integrations, is decoded to PCM16 and likewise WAV-wrapped at its fixed 8 kHz rate. Anything else
39
+ * is passed to the size guard as-is, which keeps it only if it is renderable and within budget.
40
+ *
41
+ * Both conversions expand what they are given — G.711 doubles it, and either way a WAV header is
42
+ * prepended — so the budget is checked against the size the conversion *would* produce before
43
+ * running it. Checking afterwards, as the guard alone does, means decoding and copying megabytes
44
+ * only to discard them: the retention cap admits roughly twice what a G.711 turn can ever inline.
45
+ *
46
+ * @param {Buffer} audio
47
+ * @param {string} mimeType
48
+ * @param {number} sampleRate
49
+ * @param {number} maxBytes - Encoded bytes still available to this turn. See `setLLMObsTags`.
50
+ * @returns {AudioPart | undefined}
51
+ */
52
+ function buildAudioPart (audio, mimeType, sampleRate, maxBytes) {
53
+ if (!audio.length) return
54
+
55
+ if (isPcm16AudioMime(mimeType)) {
56
+ if (!fitsInlineAudioBudget(audio.length + WAV_HEADER_BYTES, maxBytes)) return
57
+ return formatAudioPartWithGuard(pcm16ToWav(audio, sampleRate), 'audio/wav', maxBytes)
58
+ }
59
+
60
+ const variant = g711Variant(mimeType)
61
+ if (variant !== undefined) {
62
+ if (!fitsInlineAudioBudget(audio.length * PCM16_BYTES_PER_SAMPLE + WAV_HEADER_BYTES, maxBytes)) return
63
+ return formatAudioPartWithGuard(
64
+ pcm16ToWav(g711ToPcm16(audio, variant), G711_SAMPLE_RATE), 'audio/wav', maxBytes
65
+ )
66
+ }
67
+
68
+ return formatAudioPartWithGuard(audio, mimeType, maxBytes)
69
+ }
70
+
71
+ /**
72
+ * Encoded size an already-built message contributes to the span event, which for audio is the
73
+ * base64 content itself.
74
+ *
75
+ * @param {Message | undefined} message
76
+ */
77
+ function audioBytesOf (message) {
78
+ return message?.audioParts?.[0]?.content?.length ?? 0
79
+ }
80
+
81
+ /**
82
+ * @param {object[]} toolCalls
83
+ * @returns {object[]}
84
+ */
85
+ function parseToolCallArguments (toolCalls) {
86
+ return toolCalls.map(toolCall => ({ ...toolCall, arguments: safeJsonParse(toolCall.arguments ?? '', {}) }))
87
+ }
88
+
89
+ /**
90
+ * Build the message for one side of a turn.
91
+ *
92
+ * @param {string} role
93
+ * @param {import('../../../../../datadog-instrumentations/src/openai-realtime/session').TurnSide} side
94
+ * @param {number} maxAudioBytes - Encoded audio bytes still available to this turn.
95
+ * @returns {Message | undefined}
96
+ */
97
+ function buildMessage (role, side, maxAudioBytes) {
98
+ const audioPart = buildAudioPart(side.audio, side.mimeType, side.sampleRate, maxAudioBytes)
99
+
100
+ let content = side.transcript || side.text
101
+ if (!content && audioPart === undefined && side.audioPresent) {
102
+ // Audio was captured but couldn't be turned into a playable part — an unsupported format, or
103
+ // over the size budget — and there is no transcript, so surface a marker rather than let the
104
+ // turn look silently empty.
105
+ content = AUDIO_FALLBACK
106
+ }
107
+
108
+ const toolCalls = side.toolCalls?.length ? parseToolCallArguments(side.toolCalls) : undefined
109
+ const toolResults = side.toolResults.length ? side.toolResults : undefined
110
+
111
+ if (!content && audioPart === undefined && toolCalls === undefined && toolResults === undefined) return
112
+
113
+ /** @type {Message} */
114
+ const message = { role, content: content || '' }
115
+ if (audioPart !== undefined) message.audioParts = [audioPart]
116
+ if (toolCalls !== undefined) message.toolCalls = toolCalls
117
+ if (toolResults !== undefined) message.toolResults = toolResults
118
+
119
+ return message
120
+ }
121
+
122
+ /**
123
+ * @param {{ input_tokens?: number, output_tokens?: number, total_tokens?: number } | undefined} usage
124
+ * @returns {Record<string, number> | undefined}
125
+ */
126
+ function usageMetrics (usage) {
127
+ if (!usage) return
128
+
129
+ const { input_tokens: inputTokens, output_tokens: outputTokens } = usage
130
+ let { total_tokens: totalTokens } = usage
131
+
132
+ /** @type {Record<string, number>} */
133
+ const metrics = {}
134
+ if (inputTokens != null) metrics.input_tokens = inputTokens
135
+ if (outputTokens != null) metrics.output_tokens = outputTokens
136
+ // Mirror the chat and responses fallback.
137
+ if (totalTokens == null && inputTokens != null && outputTokens != null) {
138
+ totalTokens = inputTokens + outputTokens
139
+ }
140
+ if (totalTokens != null) metrics.total_tokens = totalTokens
141
+
142
+ return metrics.total_tokens === undefined && metrics.input_tokens === undefined ? undefined : metrics
143
+ }
144
+
145
+ /**
146
+ * LLM Observability spans for one OpenAI Realtime turn.
147
+ *
148
+ * The turn is modelled as a small tree — a `workflow` root with a `user speech` window, the `llm`
149
+ * generation span, and an `agent speech` window — so span duration is meaningful (the llm span
150
+ * measures model work, not the human's speaking time) and time-to-first-agent-audio falls out of the
151
+ * span boundaries. Consumers identify the phase spans by span kind and name, so those strings are a
152
+ * contract: renaming one is a breaking change for the UI and the backend metric alike.
153
+ */
154
+ class RealtimeLLMObsPlugin extends LLMObsPlugin {
155
+ static integration = 'openai'
156
+ static system = 'openai'
157
+
158
+ /**
159
+ * The instrumentation replays the turn with `traceSync`, which never publishes `asyncEnd`, so tag
160
+ * on `end` instead — before the sibling tracing plugin's `end` finishes the span, since the LLM
161
+ * Observability plugins are registered first.
162
+ *
163
+ * @param {object} ctx
164
+ */
165
+ end (ctx) {
166
+ super.end(ctx)
167
+ super.asyncEnd(ctx)
168
+ }
169
+ }
170
+
171
+ /** The whole perceived turn. Carries the transcripts so the waterfall row reads as a conversation. */
172
+ class RealtimeTurnLLMObsPlugin extends RealtimeLLMObsPlugin {
173
+ static id = 'openai_realtime_turn_llmobs'
174
+ static prefix = 'tracing:apm:openai:realtime:turn'
175
+
176
+ constructor (...args) {
177
+ super(...args)
178
+
179
+ this.addSub('dd-trace:openai:realtime:capture-context', turn => {
180
+ if (!this._tracerConfig.llmobs.DD_LLMOBS_ENABLED) return
181
+
182
+ // Composed on top of the tracing plugin's, so the replayed tree restores both the APM and the
183
+ // LLM Observability context the caller had active when the turn began.
184
+ const store = llmobsStorage.getStore()
185
+ const previous = turn.runInContext ?? (fn => fn())
186
+ turn.runInContext = fn => previous(() => llmobsStorage.run(store, fn))
187
+ })
188
+ }
189
+
190
+ getLLMObsSpanRegisterOptions (ctx) {
191
+ return {
192
+ kind: 'workflow',
193
+ // Provider-agnostic `audio turn` marker, so a consumer can gate on "is this a voice turn?".
194
+ name: 'realtime audio turn',
195
+ sessionId: ctx.turn.sessionId,
196
+ }
197
+ }
198
+
199
+ setLLMObsTags (ctx) {
200
+ const span = ctx.currentStore?.span
201
+ if (!span) return
202
+
203
+ const { input, output } = ctx.turn
204
+ this._tagger.tagTextIO(span, input.transcript || input.text, output.transcript || output.text)
205
+ }
206
+ }
207
+
208
+ /** The model's generation work. The turn's audio, tool calls and token usage ride here. */
209
+ class RealtimeResponseLLMObsPlugin extends RealtimeLLMObsPlugin {
210
+ static id = 'openai_realtime_response_llmobs'
211
+ static prefix = 'tracing:apm:openai:realtime:response'
212
+
213
+ constructor (...args) {
214
+ super(...args)
215
+
216
+ // This plugin is the only consumer of a turn's audio bytes, and the instrumentation keeps them
217
+ // only while something is subscribed here — a disabled plugin unsubscribes, so an APM-only
218
+ // process never buffers audio it cannot use. Nothing is ever published; the subscription is the
219
+ // signal. Keep it if `buildMessage` stops reading `side.audio`, and the buffering stops with it.
220
+ this.addSub('dd-trace:openai:realtime:audio', () => {})
221
+ }
222
+
223
+ getLLMObsSpanRegisterOptions (ctx) {
224
+ const { turn } = ctx
225
+ const { modelProvider, client } = getModelProviderAndClient(turn.basePath)
226
+
227
+ return {
228
+ kind: 'llm',
229
+ name: `${client}.createRealtimeResponse`,
230
+ modelName: turn.model || 'unknown_model',
231
+ modelProvider,
232
+ sessionId: turn.sessionId,
233
+ }
234
+ }
235
+
236
+ setLLMObsTags (ctx) {
237
+ const span = ctx.currentStore?.span
238
+ if (!span) return
239
+
240
+ const { turn } = ctx
241
+
242
+ // One budget for the whole event, spent across both sides rather than offered to each.
243
+ // A turn normally carries audio on both sides, so a per-message budget lets two individually
244
+ // accepted clips add up to twice the limit — and `writers/spans.js` responds by truncating the
245
+ // event's *entire* input and output, losing the transcripts too. Input is served first and the
246
+ // output falls back to its transcript when nothing is left, which is the same graceful
247
+ // degradation an oversize single clip already gets.
248
+ const inputMessage = buildMessage('user', turn.input, LLMOBS_AUDIO_INLINE_MAX_BYTES)
249
+ const remainingAudioBytes = LLMOBS_AUDIO_INLINE_MAX_BYTES - audioBytesOf(inputMessage)
250
+ const outputMessage = buildMessage('assistant', turn.output, remainingAudioBytes)
251
+
252
+ this._tagger.tagLLMIO(span, inputMessage ? [inputMessage] : [], outputMessage ? [outputMessage] : [])
253
+ this._tagger.tagMetadata(span, turn.metadata)
254
+
255
+ const metrics = usageMetrics(turn.usage)
256
+ if (metrics !== undefined) this._tagger.tagMetrics(span, metrics)
257
+ }
258
+ }
259
+
260
+ /** A speaking window — the human's or the agent's. These are timing regions; the audio rides on the llm span. */
261
+ class RealtimeSpeechLLMObsPlugin extends RealtimeLLMObsPlugin {
262
+ static id = 'openai_realtime_speech_llmobs'
263
+ static prefix = 'tracing:apm:openai:realtime:speech'
264
+
265
+ getLLMObsSpanRegisterOptions (ctx) {
266
+ return {
267
+ kind: 'workflow',
268
+ name: ctx.phase.llmobsName,
269
+ sessionId: ctx.turn.sessionId,
270
+ }
271
+ }
272
+
273
+ setLLMObsTags (ctx) {
274
+ const span = ctx.currentStore?.span
275
+ if (!span) return
276
+
277
+ this._tagger.tagTextIO(span, undefined, ctx.transcript)
278
+ }
279
+ }
280
+
281
+ module.exports = [
282
+ RealtimeTurnLLMObsPlugin,
283
+ RealtimeResponseLLMObsPlugin,
284
+ RealtimeSpeechLLMObsPlugin,
285
+ ]
@@ -1,13 +1,13 @@
1
1
  'use strict'
2
2
 
3
+ const { audioMimeTypeFromFormat, formatAudioPart } = require('../../audio-utils')
4
+ const { AUDIO_FALLBACK } = require('../../constants/audio')
3
5
  const { UNKNOWN_MODEL_PROVIDER } = require('../../constants/tags')
4
- const { audioMimeTypeFromFormat, formatAudioPart } = require('../../util')
5
6
  const {
6
7
  INPUT_TYPE_IMAGE,
7
8
  INPUT_TYPE_FILE,
8
9
  IMAGE_FALLBACK,
9
10
  FILE_FALLBACK,
10
- AUDIO_FALLBACK,
11
11
  AUDIO_MIME_TYPES,
12
12
  } = require('./constants')
13
13
 
@@ -177,7 +177,6 @@ function extractContentParts (parts) {
177
177
  * client baseURL convention.
178
178
  *
179
179
  * @param {string} baseUrl
180
- * @returns {string}
181
180
  */
182
181
  function getOpenAIModelProvider (baseUrl = '') {
183
182
  if (baseUrl.includes('azure')) return 'azure_openai'
@@ -186,11 +185,25 @@ function getOpenAIModelProvider (baseUrl = '') {
186
185
  return UNKNOWN_MODEL_PROVIDER
187
186
  }
188
187
 
188
+ /**
189
+ * Pair an OpenAI-compatible base URL's model provider with the client label used in span names.
190
+ *
191
+ * @param {string} [baseUrl]
192
+ * @returns {{ modelProvider: string, client: string }}
193
+ */
194
+ function getModelProviderAndClient (baseUrl = '') {
195
+ const modelProvider = getOpenAIModelProvider(baseUrl)
196
+ if (modelProvider === 'azure_openai') return { modelProvider, client: 'AzureOpenAI' }
197
+ if (modelProvider === 'deepseek') return { modelProvider, client: 'DeepSeek' }
198
+ return { modelProvider, client: 'OpenAI' }
199
+ }
200
+
189
201
  module.exports = {
190
202
  extractChatTemplateFromInstructions,
191
203
  normalizePromptVariables,
192
204
  extractTextFromContentItem,
193
205
  extractContentParts,
194
206
  hasMultimodalInputs,
207
+ getModelProviderAndClient,
195
208
  getOpenAIModelProvider,
196
209
  }