dd-trace 6.16.0 → 6.18.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 (356) hide show
  1. package/ci/diagnose.js +0 -23
  2. package/ci/test-optimization-validation/approval-artifacts.js +0 -3
  3. package/ci/test-optimization-validation/approval.js +0 -6
  4. package/ci/test-optimization-validation/artifact-id.js +0 -1
  5. package/ci/test-optimization-validation/bounded-json.js +0 -1
  6. package/ci/test-optimization-validation/cli.js +0 -12
  7. package/ci/test-optimization-validation/command-output-policy.js +0 -1
  8. package/ci/test-optimization-validation/command-runner.js +0 -3
  9. package/ci/test-optimization-validation/environment.js +0 -4
  10. package/ci/test-optimization-validation/executable-approval.js +0 -1
  11. package/ci/test-optimization-validation/executable.js +0 -5
  12. package/ci/test-optimization-validation/framework-adapters/cucumber.js +0 -6
  13. package/ci/test-optimization-validation/framework-adapters/cypress.js +0 -3
  14. package/ci/test-optimization-validation/framework-adapters/playwright.js +0 -6
  15. package/ci/test-optimization-validation/framework-adapters/vitest.js +0 -4
  16. package/ci/test-optimization-validation/generated-test-contract.js +0 -5
  17. package/ci/test-optimization-validation/generated-verifier.js +0 -1
  18. package/ci/test-optimization-validation/literal-environment.js +0 -1
  19. package/ci/test-optimization-validation/manifest-scaffold.js +0 -17
  20. package/ci/test-optimization-validation/manifest-schema.js +0 -21
  21. package/ci/test-optimization-validation/offline-fixtures.js +0 -4
  22. package/ci/test-optimization-validation/plan-writer.js +0 -11
  23. package/ci/test-optimization-validation/preflight-runner.js +0 -1
  24. package/ci/test-optimization-validation/redaction.js +0 -4
  25. package/ci/test-optimization-validation/report-writer.js +0 -16
  26. package/ci/test-optimization-validation/runner-command.js +0 -3
  27. package/ci/test-optimization-validation/runner-contract.js +0 -4
  28. package/ci/test-optimization-validation/safe-files.js +0 -1
  29. package/ci/test-optimization-validation/scenarios/basic-reporting.js +0 -1
  30. package/ci/test-optimization-validation/scenarios/ci-wiring.js +0 -1
  31. package/ci/test-optimization-validation/source-text.js +0 -3
  32. package/ci/vitest-no-worker-init-setup.mjs +41 -16
  33. package/index.d.ts +208 -0
  34. package/package.json +17 -16
  35. package/packages/datadog-esbuild/src/utils.js +0 -2
  36. package/packages/datadog-instrumentations/src/aerospike.js +28 -6
  37. package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +0 -2
  38. package/packages/datadog-instrumentations/src/aws-sdk.js +0 -2
  39. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +0 -2
  40. package/packages/datadog-instrumentations/src/console.js +97 -0
  41. package/packages/datadog-instrumentations/src/cucumber.js +77 -12
  42. package/packages/datadog-instrumentations/src/cypress-config.js +27 -38
  43. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +6 -0
  44. package/packages/datadog-instrumentations/src/helpers/channel.js +0 -2
  45. package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +2 -1
  46. package/packages/datadog-instrumentations/src/helpers/graphql-jit-runtime.js +0 -5
  47. package/packages/datadog-instrumentations/src/helpers/hooks.js +8 -6
  48. package/packages/datadog-instrumentations/src/helpers/instrumentation-utils.js +0 -3
  49. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +0 -6
  50. package/packages/datadog-instrumentations/src/helpers/register.js +24 -0
  51. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +111 -26
  52. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentation-registry.js +41 -0
  53. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +3 -17
  54. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +55 -0
  55. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/postgres.js +36 -0
  56. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/supabase.js +128 -0
  57. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +13 -0
  58. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.js +28 -3
  59. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +20 -0
  60. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms/postgres.js +388 -0
  61. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +0 -5
  62. package/packages/datadog-instrumentations/src/helpers/router-helper.js +0 -1
  63. package/packages/datadog-instrumentations/src/jest.js +165 -51
  64. package/packages/datadog-instrumentations/src/knex.js +0 -1
  65. package/packages/datadog-instrumentations/src/mariadb-bundle.js +0 -24
  66. package/packages/datadog-instrumentations/src/mariadb.js +0 -2
  67. package/packages/datadog-instrumentations/src/mocha/common.js +0 -1
  68. package/packages/datadog-instrumentations/src/mocha/main.js +25 -26
  69. package/packages/datadog-instrumentations/src/mocha/utils.js +119 -49
  70. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +0 -6
  71. package/packages/datadog-instrumentations/src/mocha/worker.js +14 -17
  72. package/packages/datadog-instrumentations/src/mysql2.js +0 -2
  73. package/packages/datadog-instrumentations/src/openai-realtime/audio-accumulator.js +241 -0
  74. package/packages/datadog-instrumentations/src/openai-realtime/events.js +76 -0
  75. package/packages/datadog-instrumentations/src/openai-realtime/index.js +301 -0
  76. package/packages/datadog-instrumentations/src/openai-realtime/session.js +998 -0
  77. package/packages/datadog-instrumentations/src/openai-realtime/turns.js +211 -0
  78. package/packages/datadog-instrumentations/src/openai.js +51 -12
  79. package/packages/datadog-instrumentations/src/oracledb.js +9 -3
  80. package/packages/datadog-instrumentations/src/playwright-reporter.js +6 -20
  81. package/packages/datadog-instrumentations/src/playwright.js +280 -56
  82. package/packages/datadog-instrumentations/src/{azure-cosmos.js → postgres.js} +1 -1
  83. package/packages/datadog-instrumentations/src/router.js +0 -2
  84. package/packages/datadog-instrumentations/src/rum-browser-scripts.js +0 -2
  85. package/packages/datadog-instrumentations/src/sequelize.js +0 -1
  86. package/packages/datadog-instrumentations/src/supabase.js +15 -0
  87. package/packages/datadog-instrumentations/src/undici.js +7 -3
  88. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +18 -18
  89. package/packages/datadog-instrumentations/src/vitest-main.js +121 -30
  90. package/packages/datadog-instrumentations/src/vitest-util.js +7 -1
  91. package/packages/datadog-instrumentations/src/vitest-worker.js +49 -13
  92. package/packages/datadog-instrumentations/src/webdriverio.js +44 -47
  93. package/packages/datadog-instrumentations/src/ws.js +41 -2
  94. package/packages/datadog-plugin-ai/src/utils.js +0 -2
  95. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +0 -1
  96. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +0 -2
  97. package/packages/datadog-plugin-aws-sdk/src/base.js +0 -1
  98. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +0 -1
  99. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +0 -1
  100. package/packages/datadog-plugin-aws-sdk/src/util.js +0 -1
  101. package/packages/datadog-plugin-azure-durable-functions/src/index.js +0 -1
  102. package/packages/datadog-plugin-cucumber/src/index.js +9 -0
  103. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +211 -85
  104. package/packages/datadog-plugin-cypress/src/finalization.js +0 -3
  105. package/packages/datadog-plugin-cypress/src/index.js +3 -1
  106. package/packages/datadog-plugin-cypress/src/source-map-utils.js +0 -4
  107. package/packages/datadog-plugin-cypress/src/support.js +52 -7
  108. package/packages/datadog-plugin-graphql/src/resolve-error.js +1 -2
  109. package/packages/datadog-plugin-graphql/src/utils.js +0 -5
  110. package/packages/datadog-plugin-jest/src/index.js +12 -0
  111. package/packages/datadog-plugin-kafkajs/src/producer.js +0 -1
  112. package/packages/datadog-plugin-mocha/src/index.js +38 -11
  113. package/packages/datadog-plugin-mongodb-core/src/query.js +0 -1
  114. package/packages/datadog-plugin-openai/src/index.js +16 -1
  115. package/packages/datadog-plugin-openai/src/realtime.js +144 -0
  116. package/packages/datadog-plugin-openai-agents/src/integration.js +0 -4
  117. package/packages/datadog-plugin-openai-agents/src/util.js +0 -1
  118. package/packages/datadog-plugin-playwright/src/index.js +81 -9
  119. package/packages/datadog-plugin-postgres/src/index.js +167 -0
  120. package/packages/datadog-plugin-prisma/src/datadog-tracing-helper.js +0 -1
  121. package/packages/datadog-plugin-redis/src/index.js +15 -1
  122. package/packages/datadog-plugin-supabase/src/error.js +18 -0
  123. package/packages/datadog-plugin-supabase/src/index.js +21 -0
  124. package/packages/datadog-plugin-supabase/src/supabase-auth-js-gotrueclient-getuser.js +69 -0
  125. package/packages/datadog-plugin-supabase/src/supabase-functions-js-functionsclient-invoke.js +70 -0
  126. package/packages/datadog-plugin-supabase/src/supabase-postgrest-js-postgrestbuilder-then.js +122 -0
  127. package/packages/datadog-plugin-supabase/src/supabase-realtime-js-realtimechannel-send.js +66 -0
  128. package/packages/datadog-plugin-supabase/src/supabase-storage-js-handle-request.js +93 -0
  129. package/packages/datadog-plugin-supabase/src/url.js +17 -0
  130. package/packages/datadog-plugin-vitest/src/index.js +9 -0
  131. package/packages/datadog-plugin-ws/src/producer.js +1 -1
  132. package/packages/datadog-plugin-ws/src/util.js +0 -4
  133. package/packages/datadog-shimmer/src/shimmer.js +14 -10
  134. package/packages/datadog-turbopack/index.js +4 -7
  135. package/packages/datadog-turbopack/src/loader.js +6 -18
  136. package/packages/datadog-webpack/src/loader.js +0 -1
  137. package/packages/dd-trace/src/agent/info.js +5 -4
  138. package/packages/dd-trace/src/aiguard/client.js +2 -3
  139. package/packages/dd-trace/src/aiguard/evaluation.js +6 -12
  140. package/packages/dd-trace/src/aiguard/index.js +2 -2
  141. package/packages/dd-trace/src/aiguard/integrations/index.js +1 -1
  142. package/packages/dd-trace/src/aiguard/integrations/openai.js +81 -7
  143. package/packages/dd-trace/src/aiguard/messages/anthropic.js +0 -2
  144. package/packages/dd-trace/src/aiguard/messages/openai.js +64 -1
  145. package/packages/dd-trace/src/aiguard/messages/utils.js +0 -1
  146. package/packages/dd-trace/src/aiguard/redaction.js +0 -1
  147. package/packages/dd-trace/src/aiguard/sdk.js +1 -1
  148. package/packages/dd-trace/src/appsec/activation.js +1 -1
  149. package/packages/dd-trace/src/appsec/api_security/normalized-route.js +0 -5
  150. package/packages/dd-trace/src/appsec/api_security/sampler.js +0 -4
  151. package/packages/dd-trace/src/appsec/blocking/index.js +3 -3
  152. package/packages/dd-trace/src/appsec/downstream_requests.js +1 -4
  153. package/packages/dd-trace/src/appsec/iast/index.js +1 -1
  154. package/packages/dd-trace/src/appsec/iast/overhead-controller.js +5 -5
  155. package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +1 -1
  156. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +1 -1
  157. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +3 -3
  158. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +8 -10
  159. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +2 -2
  160. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +4 -1
  161. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +7 -7
  162. package/packages/dd-trace/src/appsec/index.js +2 -2
  163. package/packages/dd-trace/src/appsec/lambda.js +71 -2
  164. package/packages/dd-trace/src/appsec/rasp/utils.js +5 -1
  165. package/packages/dd-trace/src/appsec/remote_config.js +8 -6
  166. package/packages/dd-trace/src/appsec/reporter.js +10 -6
  167. package/packages/dd-trace/src/appsec/rule_manager.js +3 -3
  168. package/packages/dd-trace/src/appsec/sdk/track_event.js +0 -2
  169. package/packages/dd-trace/src/appsec/waf/index.js +2 -2
  170. package/packages/dd-trace/src/appsec/waf/waf_manager.js +8 -5
  171. package/packages/dd-trace/src/carrier.js +0 -5
  172. package/packages/dd-trace/src/ci-visibility/dynamic-atr-retries.js +104 -0
  173. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +0 -2
  174. package/packages/dd-trace/src/ci-visibility/efd-retry-policy.js +34 -3
  175. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +0 -1
  176. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +0 -1
  177. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +0 -1
  178. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/index.js +0 -4
  179. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +0 -1
  180. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/sink.js +0 -2
  181. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +37 -15
  182. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +0 -3
  183. package/packages/dd-trace/src/ci-visibility/exporters/request.js +0 -11
  184. package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +0 -1
  185. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +0 -1
  186. package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +0 -6
  187. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +2 -2
  188. package/packages/dd-trace/src/ci-visibility/requests/rate-limit.js +0 -1
  189. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +0 -5
  190. package/packages/dd-trace/src/ci-visibility/requests/video-request.js +0 -1
  191. package/packages/dd-trace/src/ci-visibility/telemetry.js +13 -0
  192. package/packages/dd-trace/src/ci-visibility/test-optimization-cache.js +0 -1
  193. package/packages/dd-trace/src/ci-visibility/test-screenshot.js +0 -2
  194. package/packages/dd-trace/src/ci-visibility/test-video.js +0 -1
  195. package/packages/dd-trace/src/config/defaults.js +3 -2
  196. package/packages/dd-trace/src/config/generated-config-types.d.ts +88 -82
  197. package/packages/dd-trace/src/config/helper.js +0 -1
  198. package/packages/dd-trace/src/config/index.js +31 -30
  199. package/packages/dd-trace/src/config/parsers.js +34 -6
  200. package/packages/dd-trace/src/config/remote_config.js +25 -109
  201. package/packages/dd-trace/src/config/supported-configurations.json +150 -10
  202. package/packages/dd-trace/src/datastreams/encoding.js +0 -1
  203. package/packages/dd-trace/src/datastreams/pathway.js +0 -1
  204. package/packages/dd-trace/src/debugger/constants.js +10 -0
  205. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +62 -10
  206. package/packages/dd-trace/src/debugger/devtools_client/config.js +4 -1
  207. package/packages/dd-trace/src/debugger/devtools_client/guardrail-metrics.js +15 -0
  208. package/packages/dd-trace/src/debugger/devtools_client/index.js +103 -27
  209. package/packages/dd-trace/src/debugger/devtools_client/json-buffer.js +50 -7
  210. package/packages/dd-trace/src/debugger/devtools_client/log.js +3 -1
  211. package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +21 -8
  212. package/packages/dd-trace/src/debugger/devtools_client/send.js +41 -15
  213. package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +53 -13
  214. package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +118 -71
  215. package/packages/dd-trace/src/debugger/devtools_client/snapshot/redaction.js +5 -3
  216. package/packages/dd-trace/src/debugger/devtools_client/snapshot-pruner.js +0 -3
  217. package/packages/dd-trace/src/debugger/devtools_client/status.js +21 -7
  218. package/packages/dd-trace/src/debugger/guardrail-metrics.js +167 -0
  219. package/packages/dd-trace/src/debugger/index.js +51 -3
  220. package/packages/dd-trace/src/debugger/inspect-segment.js +0 -3
  221. package/packages/dd-trace/src/debugger/probe_sampler.js +133 -9
  222. package/packages/dd-trace/src/debugger/probe_sampler_constants.js +31 -1
  223. package/packages/dd-trace/src/dogstatsd.js +0 -23
  224. package/packages/dd-trace/src/encode/0.4-cross-payload.js +0 -3
  225. package/packages/dd-trace/src/encode/0.4.js +0 -7
  226. package/packages/dd-trace/src/encode/0.5.js +0 -2
  227. package/packages/dd-trace/src/encode/tags-processors.js +0 -1
  228. package/packages/dd-trace/src/evp_proxy/constants.js +2 -0
  229. package/packages/dd-trace/src/evp_proxy/direct.js +17 -13
  230. package/packages/dd-trace/src/evp_proxy/discovery.js +14 -11
  231. package/packages/dd-trace/src/evp_proxy/path.js +21 -9
  232. package/packages/dd-trace/src/exporters/agent/writer.js +0 -1
  233. package/packages/dd-trace/src/exporters/agentless/index.js +0 -1
  234. package/packages/dd-trace/src/exporters/agentless/intake.js +0 -1
  235. package/packages/dd-trace/src/exporters/agentless/writer.js +0 -3
  236. package/packages/dd-trace/src/exporters/common/final-flush-request-tracker.js +0 -6
  237. package/packages/dd-trace/src/exporters/common/request.js +1 -0
  238. package/packages/dd-trace/src/exporters/common/url.js +33 -4
  239. package/packages/dd-trace/src/exporters/common/writer.js +0 -2
  240. package/packages/dd-trace/src/id.js +0 -11
  241. package/packages/dd-trace/src/llmobs/audio-codec.js +192 -0
  242. package/packages/dd-trace/src/llmobs/audio-utils.js +121 -0
  243. package/packages/dd-trace/src/llmobs/constants/audio.js +35 -0
  244. package/packages/dd-trace/src/llmobs/constants/prompts.js +14 -0
  245. package/packages/dd-trace/src/llmobs/constants/tags.js +17 -0
  246. package/packages/dd-trace/src/llmobs/eval-metric.js +0 -6
  247. package/packages/dd-trace/src/llmobs/experiments/dataset.js +0 -4
  248. package/packages/dd-trace/src/llmobs/experiments/experiment.js +0 -3
  249. package/packages/dd-trace/src/llmobs/experiments/index.js +7 -6
  250. package/packages/dd-trace/src/llmobs/experiments/util.js +1 -11
  251. package/packages/dd-trace/src/llmobs/index.js +1 -3
  252. package/packages/dd-trace/src/llmobs/noop.js +10 -0
  253. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +0 -4
  254. package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +0 -1
  255. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +0 -4
  256. package/packages/dd-trace/src/llmobs/plugins/modelcontextprotocol-sdk/utils.js +0 -2
  257. package/packages/dd-trace/src/llmobs/plugins/openai/constants.js +0 -2
  258. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +4 -6
  259. package/packages/dd-trace/src/llmobs/plugins/openai/realtime.js +285 -0
  260. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +16 -3
  261. package/packages/dd-trace/src/llmobs/prompts/cache.js +272 -0
  262. package/packages/dd-trace/src/llmobs/prompts/manager.js +647 -0
  263. package/packages/dd-trace/src/llmobs/prompts/noop.js +29 -0
  264. package/packages/dd-trace/src/llmobs/prompts/prompt.js +158 -0
  265. package/packages/dd-trace/src/llmobs/sdk.js +21 -6
  266. package/packages/dd-trace/src/llmobs/span_processor.js +81 -2
  267. package/packages/dd-trace/src/llmobs/tagger.js +39 -11
  268. package/packages/dd-trace/src/llmobs/telemetry.js +36 -4
  269. package/packages/dd-trace/src/llmobs/util.js +0 -34
  270. package/packages/dd-trace/src/llmobs/writers/base.js +0 -1
  271. package/packages/dd-trace/src/llmobs/writers/util.js +1 -2
  272. package/packages/dd-trace/src/log/channels.js +3 -0
  273. package/packages/dd-trace/src/log/index.js +23 -5
  274. package/packages/dd-trace/src/log-submission/log-submission-plugin.js +74 -6
  275. package/packages/dd-trace/src/msgpack/chunk.js +0 -1
  276. package/packages/dd-trace/src/openfeature/agentless_configuration_source.js +0 -12
  277. package/packages/dd-trace/src/openfeature/configuration_source.js +4 -1
  278. package/packages/dd-trace/src/openfeature/encoding.js +0 -2
  279. package/packages/dd-trace/src/openfeature/eval-metrics-hook.js +0 -1
  280. package/packages/dd-trace/src/openfeature/flagging_provider.js +3 -3
  281. package/packages/dd-trace/src/openfeature/index.js +6 -6
  282. package/packages/dd-trace/src/openfeature/span-enrichment-hook.js +0 -1
  283. package/packages/dd-trace/src/openfeature/span-enrichment.js +0 -4
  284. package/packages/dd-trace/src/openfeature/writers/base.js +90 -23
  285. package/packages/dd-trace/src/openfeature/writers/exposures.js +4 -5
  286. package/packages/dd-trace/src/openfeature/writers/util.js +104 -37
  287. package/packages/dd-trace/src/opentelemetry/logs/logger.js +0 -1
  288. package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +0 -1
  289. package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +0 -1
  290. package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +0 -2
  291. package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +0 -1
  292. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +0 -1
  293. package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +0 -1
  294. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -14
  295. package/packages/dd-trace/src/opentelemetry/metrics/time.js +0 -1
  296. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +30 -13
  297. package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +0 -1
  298. package/packages/dd-trace/src/opentelemetry/span-helpers.js +0 -1
  299. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +0 -1
  300. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +0 -3
  301. package/packages/dd-trace/src/opentracing/propagation/text_map.js +0 -6
  302. package/packages/dd-trace/src/opentracing/span_context.js +0 -1
  303. package/packages/dd-trace/src/opentracing/tracer.js +3 -3
  304. package/packages/dd-trace/src/otel-thread-ctx.js +38 -11
  305. package/packages/dd-trace/src/payload-tagging/tagging.js +15 -3
  306. package/packages/dd-trace/src/plugin_manager.js +27 -8
  307. package/packages/dd-trace/src/plugins/ci_plugin.js +1 -2
  308. package/packages/dd-trace/src/plugins/database.js +2 -6
  309. package/packages/dd-trace/src/plugins/index.js +7 -0
  310. package/packages/dd-trace/src/plugins/plugin.js +0 -4
  311. package/packages/dd-trace/src/plugins/tracing.js +1 -1
  312. package/packages/dd-trace/src/plugins/util/git.js +1 -3
  313. package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +0 -1
  314. package/packages/dd-trace/src/plugins/util/llm.js +0 -2
  315. package/packages/dd-trace/src/plugins/util/status-validator.js +0 -1
  316. package/packages/dd-trace/src/plugins/util/test.js +26 -20
  317. package/packages/dd-trace/src/plugins/util/url.js +0 -5
  318. package/packages/dd-trace/src/priority_sampler.js +0 -6
  319. package/packages/dd-trace/src/process-tags/index.js +0 -1
  320. package/packages/dd-trace/src/profiler.js +132 -35
  321. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +0 -1
  322. package/packages/dd-trace/src/profiling/profiler.js +101 -41
  323. package/packages/dd-trace/src/profiling/ssi-heuristics.js +29 -15
  324. package/packages/dd-trace/src/propagation-hash/index.js +0 -1
  325. package/packages/dd-trace/src/proxy.js +23 -47
  326. package/packages/dd-trace/src/random_sampler.js +0 -4
  327. package/packages/dd-trace/src/rate_limiter.js +0 -4
  328. package/packages/dd-trace/src/remote_config/capabilities.js +1 -9
  329. package/packages/dd-trace/src/remote_config/index.js +0 -1
  330. package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +0 -3
  331. package/packages/dd-trace/src/sampler.js +0 -4
  332. package/packages/dd-trace/src/sampling_rule.js +0 -7
  333. package/packages/dd-trace/src/serverless/vercel.js +0 -1
  334. package/packages/dd-trace/src/serverless.js +0 -1
  335. package/packages/dd-trace/src/service-naming/index.js +0 -1
  336. package/packages/dd-trace/src/service-naming/schemas/util.js +0 -1
  337. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +7 -0
  338. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +5 -0
  339. package/packages/dd-trace/src/span_processor.js +2 -2
  340. package/packages/dd-trace/src/span_sampler.js +0 -1
  341. package/packages/dd-trace/src/standalone/product.js +2 -2
  342. package/packages/dd-trace/src/standalone/tracesource.js +0 -1
  343. package/packages/dd-trace/src/startup-log.js +1 -1
  344. package/packages/dd-trace/src/telemetry/endpoints.js +0 -8
  345. package/packages/dd-trace/src/telemetry/logs/index.js +16 -5
  346. package/packages/dd-trace/src/telemetry/logs/log-collector.js +16 -6
  347. package/packages/dd-trace/src/telemetry/metrics.js +0 -2
  348. package/packages/dd-trace/src/telemetry/send-data.js +2 -1
  349. package/packages/dd-trace/src/telemetry/telemetry.js +4 -1
  350. package/packages/dd-trace/src/tracer.js +5 -4
  351. package/packages/dd-trace/src/util.js +13 -1
  352. package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
  353. package/packages/datadog-instrumentations/src/bullmq.js +0 -11
  354. package/packages/datadog-instrumentations/src/langchain.js +0 -7
  355. package/packages/datadog-instrumentations/src/langgraph.js +0 -7
  356. package/packages/datadog-instrumentations/src/mercurius.js +0 -11
@@ -0,0 +1,158 @@
1
+ 'use strict'
2
+
3
+ /** @typedef {import('../../../../../index').llmobs.FormattedPromptMessage} FormattedPromptMessage */
4
+
5
+ const VARIABLE_PATTERN = /(?<!\{)(?:\{\{\s*(\w+)\s*\}\}(?!\})|\{\s*(\w+)\s*\}(?!\}))/g
6
+
7
+ function isMessage (value) {
8
+ return typeof value?.role === 'string' &&
9
+ value?.type !== 'placeholder' &&
10
+ (value?.content == null || typeof value.content === 'string') &&
11
+ (typeof value?.content === 'string' ||
12
+ (Array.isArray(value?.tool_calls) && value.tool_calls.length > 0) ||
13
+ (Array.isArray(value?.tool_results) && value.tool_results.length > 0))
14
+ }
15
+
16
+ function isPlaceholder (value) {
17
+ return value?.type === 'placeholder' && typeof value?.name === 'string'
18
+ }
19
+
20
+ function isObject (value) {
21
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
22
+ }
23
+
24
+ function isTool (value) {
25
+ return isObject(value) &&
26
+ (value.id == null || typeof value.id === 'string') &&
27
+ (value.type == null || typeof value.type === 'string') &&
28
+ (value.tool_id == null || typeof value.tool_id === 'string') &&
29
+ (value.name == null || typeof value.name === 'string')
30
+ }
31
+
32
+ function hasValidTools (message) {
33
+ return (message.tool_call_id == null || typeof message.tool_call_id === 'string') &&
34
+ (message.tool_calls == null || (Array.isArray(message.tool_calls) && message.tool_calls.every(call =>
35
+ isTool(call) && (call.function == null ||
36
+ (isObject(call.function) &&
37
+ typeof call.function.name === 'string' && typeof call.function.arguments === 'string'))))) &&
38
+ (message.tool_results == null || (Array.isArray(message.tool_results) && message.tool_results.every(isTool)))
39
+ }
40
+
41
+ function render (template, variables) {
42
+ return template.replaceAll(VARIABLE_PATTERN, (match, doubleName, singleName) => {
43
+ const name = doubleName ?? singleName
44
+ return Object.hasOwn(variables, name) ? String(variables[name]) : match
45
+ })
46
+ }
47
+
48
+ function freezeConfig (config) {
49
+ if (config === undefined) return Object.freeze({})
50
+ if (!config || typeof config !== 'object' || Array.isArray(config)) {
51
+ throw new TypeError('Invalid prompt config: expected a JSON object')
52
+ }
53
+ const copy = structuredClone(config)
54
+ const freeze = value => {
55
+ if (!value || typeof value !== 'object' || Object.isFrozen(value)) return value
56
+ for (const nested of Object.values(value)) freeze(nested)
57
+ return Object.freeze(value)
58
+ }
59
+ return freeze(copy)
60
+ }
61
+ class ManagedPrompt {
62
+ /**
63
+ * @param {object} data
64
+ * @param {string} data.id
65
+ * @param {string} data.version
66
+ * @param {'registry'|'cache'|'fallback'|'ff'|'resolve'} data.source
67
+ * @param {string | Array<{role: string, content: string} | {type: 'placeholder', name: string}>} data.template
68
+ * @param {Record<string, unknown>} [data.config]
69
+ * @param {string} [data.promptUuid]
70
+ * @param {string} [data.promptVersionUuid]
71
+ */
72
+ constructor ({ id, version, source, template, config, promptUuid, promptVersionUuid }) {
73
+ this.id = id
74
+ this.version = version
75
+ this.source = source
76
+ this.template = Array.isArray(template)
77
+ ? Object.freeze(template.map(item => Object.freeze(isPlaceholder(item)
78
+ ? { type: 'placeholder', name: item.name }
79
+ : { role: item.role, content: item.content })))
80
+ : template
81
+ this.config = freezeConfig(config)
82
+ this.promptUuid = promptUuid
83
+ this.promptVersionUuid = promptVersionUuid
84
+ Object.freeze(this)
85
+ }
86
+
87
+ /**
88
+ * Render the prompt without changing its stored template.
89
+ * @param {Record<string, unknown>} [variables]
90
+ * @returns {string | FormattedPromptMessage[]}
91
+ */
92
+ format (variables = {}) {
93
+ if (typeof this.template === 'string') return render(this.template, variables)
94
+ return this.template.flatMap(item => {
95
+ if (!isPlaceholder(item)) return [{ ...item, content: render(item.content, variables) }]
96
+ if (!Object.hasOwn(variables, item.name)) {
97
+ throw new TypeError(`Missing message placeholder variable '${item.name}'`)
98
+ }
99
+ const messages = variables[item.name]
100
+ if (!Array.isArray(messages) || !messages.every(message => isMessage(message) && hasValidTools(message))) {
101
+ throw new TypeError(`Invalid message placeholder variable '${item.name}': expected an array of messages`)
102
+ }
103
+ return structuredClone(messages)
104
+ })
105
+ }
106
+
107
+ /**
108
+ * Convert the managed prompt to the existing public annotation shape.
109
+ * @param {Record<string, unknown>} [variables]
110
+ * @returns {Record<string, unknown>}
111
+ */
112
+ toAnnotation (variables) {
113
+ const annotation = {
114
+ id: this.id,
115
+ version: this.version,
116
+ template: typeof this.template === 'string' ? this.template : this.template.map(message => ({ ...message })),
117
+ }
118
+ const placeholderNames = new Set(Array.isArray(this.template)
119
+ ? this.template.filter(isPlaceholder).map(item => item.name)
120
+ : [])
121
+ const entries = Object.entries(variables ?? {}).filter(([name]) => !placeholderNames.has(name))
122
+ if (entries.length) {
123
+ annotation.variables = Object.fromEntries(entries.map(([name, value]) => [name, String(value)]))
124
+ }
125
+ if (this.promptUuid) annotation.promptUuid = this.promptUuid
126
+ if (this.promptVersionUuid) annotation.promptVersionUuid = this.promptVersionUuid
127
+ return annotation
128
+ }
129
+
130
+ /**
131
+ * Convert a caller fallback to a managed prompt.
132
+ * @param {string} promptId
133
+ * @param {string | object | Array<{role: string, content: string} | {type: 'placeholder', name: string}> |
134
+ * (() => string | object | Array<{role: string, content: string} | {type: 'placeholder', name: string}>)} fallback
135
+ * @returns {ManagedPrompt}
136
+ */
137
+ static fromFallback (promptId, fallback) {
138
+ const value = typeof fallback === 'function' ? fallback() : fallback
139
+ const promptLike = value && !Array.isArray(value) && typeof value === 'object'
140
+ const template = promptLike ? value.template : value
141
+ const validTemplate = typeof template === 'string' || (
142
+ Array.isArray(template) && template.every(item =>
143
+ isPlaceholder(item) || (isMessage(item) && typeof item.content === 'string'))
144
+ )
145
+ if (!validTemplate) {
146
+ throw new TypeError('Invalid prompt fallback: expected a string, chat message array, or object with a template')
147
+ }
148
+ return new ManagedPrompt({
149
+ id: promptId,
150
+ version: String(promptLike && value.version ? value.version : 'fallback'),
151
+ source: 'fallback',
152
+ template,
153
+ config: promptLike ? value.config : undefined,
154
+ })
155
+ }
156
+ }
157
+
158
+ module.exports = ManagedPrompt
@@ -31,6 +31,7 @@ const { storage } = require('./storage')
31
31
  const telemetry = require('./telemetry')
32
32
  const LLMObsTagger = require('./tagger')
33
33
  const { createExperiments } = require('./experiments')
34
+ const PromptManager = require('./prompts/manager')
34
35
 
35
36
  // communicating with writer
36
37
  const evalMetricAppendCh = channel('llmobs:eval-metric:append')
@@ -45,12 +46,17 @@ class LLMObs extends NoopLLMObs {
45
46
  */
46
47
  #hasUserSpanProcessor = false
47
48
 
49
+ #promptManager
50
+
51
+ #getOpenFeatureProvider
52
+
48
53
  /**
49
54
  * @param {import('../tracer')} tracer - Tracer instance
50
55
  * @param {import('./index')} llmobsModule - LLMObs module instance
51
56
  * @param {import('../config/config-base')} config - Tracer configuration
57
+ * @param {() => object} getOpenFeatureProvider - Lazy getter for the tracer's existing OpenFeature provider
52
58
  */
53
- constructor (tracer, llmobsModule, config) {
59
+ constructor (tracer, llmobsModule, config, getOpenFeatureProvider = () => {}) {
54
60
  super(tracer)
55
61
 
56
62
  /** @type {import('../config/config-base')} */
@@ -58,6 +64,7 @@ class LLMObs extends NoopLLMObs {
58
64
 
59
65
  this._llmobsModule = llmobsModule
60
66
  this._tagger = new LLMObsTagger(config)
67
+ this.#getOpenFeatureProvider = getOpenFeatureProvider
61
68
  }
62
69
 
63
70
  get enabled () {
@@ -73,6 +80,15 @@ class LLMObs extends NoopLLMObs {
73
80
  return createExperiments(this._config, this)
74
81
  }
75
82
 
83
+ /**
84
+ * Prompt Management API.
85
+ * @returns {import('../../../../index').llmobs.Prompts}
86
+ */
87
+ get prompts () {
88
+ this.#promptManager ??= new PromptManager(this._config, this.#getOpenFeatureProvider)
89
+ return this.#promptManager
90
+ }
91
+
76
92
  enable (options = {}) {
77
93
  logger.warn(
78
94
  'Enabling LLM Observability via `llmobs.enable()` is deprecated and will be removed in dd-trace@7.0.0. ' +
@@ -97,8 +113,8 @@ class LLMObs extends NoopLLMObs {
97
113
 
98
114
  // TODO: These configs should be passed through directly at construction time instead.
99
115
  this._config.llmobs.DD_LLMOBS_ENABLED = true
100
- this._config.llmobs.mlApp = options.mlApp
101
- this._config.llmobs.agentlessEnabled = options.agentlessEnabled
116
+ this._config.llmobs.DD_LLMOBS_ML_APP = options.mlApp
117
+ this._config.llmobs.DD_LLMOBS_AGENTLESS_ENABLED = options.agentlessEnabled
102
118
 
103
119
  // configure writers and channel subscribers
104
120
  this._llmobsModule.enable(this._config)
@@ -393,7 +409,7 @@ class LLMObs extends NoopLLMObs {
393
409
  'spanId and traceId must both be specified for the given evaluation metric to be submitted.'
394
410
  )
395
411
  }
396
- const mlApp = options.mlApp || this._config.llmobs.mlApp
412
+ const mlApp = options.mlApp || this._config.llmobs.DD_LLMOBS_ML_APP
397
413
  if (!mlApp) {
398
414
  err = 'missing_ml_app'
399
415
  throw new Error(
@@ -475,7 +491,6 @@ class LLMObs extends NoopLLMObs {
475
491
  * @param {number} [options.timestampMs] - When the feedback was generated. Defaults to now.
476
492
  * @param {'pass' | 'fail'} [options.assessment] - Assessment of the feedback.
477
493
  * @param {string} [options.reasoning] - Explanation of the feedback.
478
- * @returns {void}
479
494
  */
480
495
  submitFeedback (options = {}) {
481
496
  if (!this.enabled) return
@@ -532,7 +547,7 @@ class LLMObs extends NoopLLMObs {
532
547
  throw new TypeError('submitter.type must be a string')
533
548
  }
534
549
 
535
- const mlApp = options.mlApp || this._config.llmobs.mlApp
550
+ const mlApp = options.mlApp || this._config.llmobs.DD_LLMOBS_ML_APP
536
551
  if (!mlApp) {
537
552
  err = 'missing_ml_app'
538
553
  throw new Error('ML App name is required for sending feedback. Feedback data will not be sent.')
@@ -38,11 +38,41 @@ const {
38
38
  SAMPLE_RATE,
39
39
  SAMPLING_DECISION,
40
40
  TRACE_ID,
41
+ INPUT_TOKENS_METRIC_KEY,
42
+ OUTPUT_TOKENS_METRIC_KEY,
43
+ TOTAL_TOKENS_METRIC_KEY,
44
+ CACHE_READ_INPUT_TOKENS_METRIC_KEY,
45
+ CACHE_WRITE_INPUT_TOKENS_METRIC_KEY,
46
+ REASONING_OUTPUT_TOKENS_METRIC_KEY,
47
+ GEN_AI_OPERATION_NAME,
48
+ GEN_AI_REQUEST_MODEL,
49
+ GEN_AI_PROVIDER_NAME,
50
+ GEN_AI_APPLICATION_NAME,
51
+ GEN_AI_CONVERSATION_ID,
52
+ GEN_AI_USAGE_INPUT_TOKENS_METRIC_KEY,
53
+ GEN_AI_USAGE_OUTPUT_TOKENS_METRIC_KEY,
54
+ GEN_AI_USAGE_TOTAL_TOKENS_METRIC_KEY,
55
+ GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS_METRIC_KEY,
56
+ GEN_AI_USAGE_CACHE_WRITE_INPUT_TOKENS_METRIC_KEY,
57
+ GEN_AI_USAGE_REASONING_OUTPUT_TOKENS_METRIC_KEY,
58
+ ARTIFICIAL_GEN_AI_TAGS,
41
59
  } = require('./constants/tags')
42
60
  const { UNSERIALIZABLE_VALUE_TEXT } = require('./constants/text')
43
61
  const telemetry = require('./telemetry')
44
62
  const LLMObsTagger = require('./tagger')
45
63
 
64
+ const DEFAULT_MODEL = 'custom'
65
+ const MODEL_BACKED_SPAN_KINDS = new Set(['llm', 'embedding'])
66
+
67
+ const GEN_AI_TOKEN_METRIC_KEYS = [
68
+ [INPUT_TOKENS_METRIC_KEY, GEN_AI_USAGE_INPUT_TOKENS_METRIC_KEY],
69
+ [OUTPUT_TOKENS_METRIC_KEY, GEN_AI_USAGE_OUTPUT_TOKENS_METRIC_KEY],
70
+ [TOTAL_TOKENS_METRIC_KEY, GEN_AI_USAGE_TOTAL_TOKENS_METRIC_KEY],
71
+ [CACHE_READ_INPUT_TOKENS_METRIC_KEY, GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS_METRIC_KEY],
72
+ [CACHE_WRITE_INPUT_TOKENS_METRIC_KEY, GEN_AI_USAGE_CACHE_WRITE_INPUT_TOKENS_METRIC_KEY],
73
+ [REASONING_OUTPUT_TOKENS_METRIC_KEY, GEN_AI_USAGE_REASONING_OUTPUT_TOKENS_METRIC_KEY],
74
+ ]
75
+
46
76
  class LLMObservabilitySpan {
47
77
  /**
48
78
  * @param {string} kind span kind
@@ -90,6 +120,12 @@ class LLMObsSpanProcessor {
90
120
  // if the span is not in our private tagger map, it is not an llmobs span
91
121
  if (!LLMObsTagger.tagMap.has(span)) return
92
122
 
123
+ try {
124
+ this.#setGenAiApmTags(span)
125
+ } catch (e) {
126
+ logger.debug('Failed to set gen_ai APM tags:', e.message)
127
+ }
128
+
93
129
  try {
94
130
  const formattedEvent = this.format(span)
95
131
  telemetry.incrementLLMObsSpanFinishedCount(span)
@@ -138,8 +174,8 @@ class LLMObsSpanProcessor {
138
174
  const output = {}
139
175
 
140
176
  if (['llm', 'embedding'].includes(spanKind)) {
141
- meta.model_name = mlObsTags[MODEL_NAME] || 'custom'
142
- meta.model_provider = (mlObsTags[MODEL_PROVIDER] || 'custom').toLowerCase()
177
+ meta.model_name = mlObsTags[MODEL_NAME] || DEFAULT_MODEL
178
+ meta.model_provider = (mlObsTags[MODEL_PROVIDER] || DEFAULT_MODEL).toLowerCase()
143
179
  }
144
180
 
145
181
  if (mlObsTags[METADATA] || mlObsTags[COST_TAGS]) {
@@ -283,6 +319,49 @@ class LLMObsSpanProcessor {
283
319
  return llmObsSpanEvent
284
320
  }
285
321
 
322
+ /**
323
+ * Writes the scalar `gen_ai.*` attributes onto the APM span, so model, provider, application,
324
+ * conversation and token usage are searchable in APM. Message bodies stay off the APM span.
325
+ *
326
+ * @param {import('../opentracing/span')} span
327
+ */
328
+ #setGenAiApmTags (span) {
329
+ const mlObsTags = LLMObsTagger.tagMap.get(span)
330
+ const spanContext = span.context()
331
+ const spanKind = mlObsTags[SPAN_KIND]
332
+
333
+ if (spanKind) spanContext.setTag(GEN_AI_OPERATION_NAME, spanKind)
334
+
335
+ const modelName = mlObsTags[MODEL_NAME]
336
+ const modelProvider = mlObsTags[MODEL_PROVIDER]
337
+ const modelBacked = MODEL_BACKED_SPAN_KINDS.has(spanKind)
338
+ if (modelBacked) {
339
+ spanContext.setTag(GEN_AI_REQUEST_MODEL, modelName || DEFAULT_MODEL)
340
+ spanContext.setTag(GEN_AI_PROVIDER_NAME, (modelProvider || DEFAULT_MODEL).toLowerCase())
341
+ } else {
342
+ if (modelName) spanContext.setTag(GEN_AI_REQUEST_MODEL, modelName)
343
+ if (modelProvider) spanContext.setTag(GEN_AI_PROVIDER_NAME, modelProvider.toLowerCase())
344
+ }
345
+
346
+ const mlApp = mlObsTags[ML_APP]
347
+ if (mlApp) spanContext.setTag(GEN_AI_APPLICATION_NAME, mlApp)
348
+
349
+ const sessionId = mlObsTags[SESSION_ID]
350
+ if (sessionId) spanContext.setTag(GEN_AI_CONVERSATION_ID, sessionId)
351
+
352
+ const metrics = mlObsTags[METRICS]
353
+ // Other kinds carry unrelated metrics that would be misleading under a `gen_ai.usage.*` key.
354
+ if (modelBacked && metrics) {
355
+ for (const [metricKey, genAiKey] of GEN_AI_TOKEN_METRIC_KEYS) {
356
+ const value = metrics[metricKey]
357
+ if (value != null) spanContext.setTag(genAiKey, value)
358
+ }
359
+ }
360
+
361
+ // matches the value dd-trace-py writes
362
+ spanContext.setTag(ARTIFICIAL_GEN_AI_TAGS, 'true')
363
+ }
364
+
286
365
  // For now, this only applies to metadata, as we let users annotate this field with any object
287
366
  // However, we want to protect against circular references or BigInts (unserializable)
288
367
  // This function can be reused for other fields if needed
@@ -87,7 +87,7 @@ class LLMObsTagger {
87
87
  }
88
88
 
89
89
  /**
90
- * The sampler reads its rate from `config.llmobs.sampleRate`, which can change
90
+ * The sampler reads its rate from `config.llmobs.DD_LLMOBS_SAMPLE_RATE`, which can change
91
91
  * at runtime (e.g. via remote config). Rebuild the sampler whenever the rate
92
92
  * changes so decisions reflect the current config, while reusing the existing
93
93
  * sampler when it hasn't.
@@ -95,7 +95,7 @@ class LLMObsTagger {
95
95
  * @returns {import('../sampler')}
96
96
  */
97
97
  #getSampler () {
98
- const rate = this.#config.llmobs?.sampleRate ?? 1
98
+ const rate = this.#config.llmobs?.DD_LLMOBS_SAMPLE_RATE ?? 1
99
99
  if (this.#sampler === null || rate !== this.#sampler.rate()) {
100
100
  this.#sampler = new Sampler(rate)
101
101
  }
@@ -128,7 +128,7 @@ class LLMObsTagger {
128
128
  mlApp ||
129
129
  registry.get(parent)?.[ML_APP] ||
130
130
  span.context()._trace.tags[PROPAGATED_ML_APP_KEY] ||
131
- this.#config.llmobs.mlApp ||
131
+ this.#config.llmobs.DD_LLMOBS_ML_APP ||
132
132
  this.#config.service // this should always have a default
133
133
 
134
134
  if (!spanMlApp) {
@@ -415,6 +415,8 @@ class LLMObsTagger {
415
415
  template,
416
416
  contextVariables,
417
417
  queryVariables,
418
+ promptUuid,
419
+ promptVersionUuid,
418
420
  } = prompt
419
421
 
420
422
  if (strictValidation) {
@@ -429,6 +431,8 @@ class LLMObsTagger {
429
431
  }
430
432
  }
431
433
 
434
+ const currentPrompt = registry.get(span)?.[INPUT_PROMPT]
435
+ const replacesPrompt = id != null || version != null || template != null
432
436
  const finalPromptId = id ?? `${mlApp}_${DEFAULT_PROMPT_NAME}`
433
437
  const finalCtxVariablesKeys = contextVariables ?? ['context']
434
438
  const finalQueryVariablesKeys = queryVariables ?? ['question']
@@ -471,6 +475,16 @@ class LLMObsTagger {
471
475
  return
472
476
  }
473
477
 
478
+ if (promptUuid != null && typeof promptUuid !== 'string') {
479
+ this.#handleFailure('Prompt UUID must be a string.', 'invalid_prompt')
480
+ return
481
+ }
482
+
483
+ if (promptVersionUuid != null && typeof promptVersionUuid !== 'string') {
484
+ this.#handleFailure('Prompt version UUID must be a string.', 'invalid_prompt')
485
+ return
486
+ }
487
+
474
488
  // validate prompt tags
475
489
  if (tags && (typeof tags !== 'object' || tags instanceof Map)) {
476
490
  this.#handleFailure('Prompt tags must be an non-Map object.', 'invalid_prompt')
@@ -491,10 +505,13 @@ class LLMObsTagger {
491
505
  }
492
506
 
493
507
  if (Array.isArray(template)) {
494
- for (const message of template) {
495
- if (typeof message !== 'object' || !message.role || !message.content) {
508
+ for (const item of template) {
509
+ const valid = item?.type === 'placeholder'
510
+ ? typeof item.name === 'string'
511
+ : typeof item?.role === 'string' && typeof item?.content === 'string'
512
+ if (!valid) {
496
513
  this.#handleFailure(
497
- 'Prompt chat template must be an array of objects with role and content properties.', 'invalid_prompt'
514
+ 'Prompt chat template must contain messages or message placeholders.', 'invalid_prompt'
498
515
  )
499
516
  return
500
517
  }
@@ -518,21 +535,32 @@ class LLMObsTagger {
518
535
  if (typeof template === 'string') {
519
536
  finalTemplate = template
520
537
  } else if (Array.isArray(template)) {
521
- finalChatTemplate = template.map(message => ({ role: message.role, content: message.content }))
538
+ finalChatTemplate = template.map(item => item.type === 'placeholder'
539
+ ? { type: 'placeholder', name: item.name }
540
+ : { role: item.role, content: item.content })
522
541
  }
523
542
 
524
543
  const validatedPrompt = {}
525
- if (finalPromptId) validatedPrompt.id = finalPromptId
544
+ if (finalPromptId && (!currentPrompt || replacesPrompt)) validatedPrompt.id = finalPromptId
526
545
  if (version) validatedPrompt.version = version
546
+ if (promptUuid) validatedPrompt.prompt_uuid = promptUuid
547
+ if (promptVersionUuid) validatedPrompt.prompt_version_uuid = promptVersionUuid
527
548
  if (variables) validatedPrompt.variables = variables
528
549
  if (finalTemplate) validatedPrompt.template = finalTemplate
529
550
  if (finalChatTemplate?.length) validatedPrompt.chat_template = finalChatTemplate
530
551
  if (tags) validatedPrompt.tags = tags
531
- if (finalCtxVariablesKeys) validatedPrompt[INTERNAL_CONTEXT_VARIABLE_KEYS] = finalCtxVariablesKeys
532
- if (finalQueryVariablesKeys) validatedPrompt[INTERNAL_QUERY_VARIABLE_KEYS] = finalQueryVariablesKeys
552
+ if (finalCtxVariablesKeys && (!currentPrompt || replacesPrompt || contextVariables != null)) {
553
+ validatedPrompt[INTERNAL_CONTEXT_VARIABLE_KEYS] = finalCtxVariablesKeys
554
+ }
555
+ if (finalQueryVariablesKeys && (!currentPrompt || replacesPrompt || queryVariables != null)) {
556
+ validatedPrompt[INTERNAL_QUERY_VARIABLE_KEYS] = finalQueryVariablesKeys
557
+ }
533
558
 
534
- const currentPrompt = registry.get(span)?.[INPUT_PROMPT]
535
559
  if (currentPrompt) {
560
+ if (replacesPrompt) {
561
+ if (promptUuid == null) currentPrompt.prompt_uuid = undefined
562
+ if (promptVersionUuid == null) currentPrompt.prompt_version_uuid = undefined
563
+ }
536
564
  Object.assign(currentPrompt, validatedPrompt)
537
565
  } else {
538
566
  this._setTag(span, INPUT_PROMPT, validatedPrompt)
@@ -84,11 +84,11 @@ function recordLLMObsEnabled (startTime, config, value = 1) {
84
84
  const autoEnabled = !!config._env?.['llmobs.enabled']
85
85
  const tags = {
86
86
  error: 0,
87
- agentless: Number(config.llmobs.agentlessEnabled),
87
+ agentless: Number(config.llmobs.DD_LLMOBS_AGENTLESS_ENABLED),
88
88
  site: config.site,
89
89
  auto: Number(autoEnabled),
90
- ml_app: config.llmobs.mlApp,
91
- sample_rate: config.llmobs.sampleRate,
90
+ ml_app: config.llmobs.DD_LLMOBS_ML_APP,
91
+ sample_rate: config.llmobs.DD_LLMOBS_SAMPLE_RATE,
92
92
  }
93
93
  llmobsMetrics.count('product_enabled', tags).inc(value)
94
94
  llmobsMetrics.distribution('init_time', tags).track(initTimeMs)
@@ -188,7 +188,6 @@ function recordSubmitEvaluation (options, err, value = 1) {
188
188
  * @param {string} targetType - The payload key the feedback was attached to, `'other'` if unresolved.
189
189
  * @param {string} err - The telemetry error tag, empty when the submission succeeded.
190
190
  * @param {number} [value]
191
- * @returns {void}
192
191
  */
193
192
  function recordSubmitFeedback (metricType, targetType, err, value = 1) {
194
193
  const tags = {
@@ -205,6 +204,36 @@ function recordLLMObsUserProcessorCalled (error, value = 1) {
205
204
  llmobsMetrics.count('user_processor_called', tags).inc(value)
206
205
  }
207
206
 
207
+ /**
208
+ * @param {'hot_cache'|'warm_cache'|'registry'|'resolve'|'fallback'|'ff'} source
209
+ * @param {number} [value]
210
+ */
211
+ function recordPromptSource (source, value = 1) {
212
+ llmobsMetrics.count('prompt.source', { from: source }).inc(value)
213
+ }
214
+
215
+ /**
216
+ * @param {'NotFound'|'FetchError'} errorType
217
+ * @param {number} [value]
218
+ */
219
+ function recordPromptFetchError (errorType, value = 1) {
220
+ llmobsMetrics.count('prompt.fetch.error', { error_type: errorType }).inc(value)
221
+ }
222
+
223
+ /**
224
+ * @param {'GET'|'POST'|'PATCH'|'DELETE'} method
225
+ * @param {string} errorType
226
+ * @param {number} status
227
+ * @param {number} [value]
228
+ */
229
+ function recordPromptCrudError (method, errorType, status, value = 1) {
230
+ llmobsMetrics.count('prompt.crud.error', {
231
+ method,
232
+ error_type: errorType,
233
+ status: String(status),
234
+ }).inc(value)
235
+ }
236
+
208
237
  module.exports = {
209
238
  recordLLMObsEnabled,
210
239
  incrementLLMObsSpanStartCount,
@@ -220,4 +249,7 @@ module.exports = {
220
249
  recordSubmitEvaluation,
221
250
  recordSubmitFeedback,
222
251
  recordLLMObsUserProcessorCalled,
252
+ recordPromptSource,
253
+ recordPromptFetchError,
254
+ recordPromptCrudError,
223
255
  }
@@ -306,7 +306,6 @@ function getFunctionArguments (fn, args = []) {
306
306
  // keys), so a name containing `=` is safe and must not be dropped.
307
307
  /**
308
308
  * @param {string} name
309
- * @returns {boolean}
310
309
  */
311
310
  function agentNameWireSafe (name) {
312
311
  // Conservative slice of the 512B shared tagset budget, mirroring dd-trace-py.
@@ -413,7 +412,6 @@ function findGenAIAncestorSpanId (span) {
413
412
  /**
414
413
  * Generate a 128-bit LLMObs trace ID with the span start time encoded in its high bits.
415
414
  * @param {number} startTime
416
- * @returns {string}
417
415
  */
418
416
  function generateLlmObsTraceId (startTime) {
419
417
  const identifier = id()
@@ -455,45 +453,13 @@ function normalizeLlmObsTraceId (traceId) {
455
453
  return identifier > MAX_UINT_64 ? identifier.toString(16).padStart(32, '0') : traceId
456
454
  }
457
455
 
458
- // Maps an audio `format` (e.g. "wav", "mp3") to a MIME type. Defaults to `audio/wav` when the
459
- // format is missing. Provider-specific overrides (e.g. OpenAI's mp3 -> audio/mpeg) are passed in
460
- // via `mimeTypeLookup` so this stays provider-agnostic. A non-string `format` is treated as missing
461
- // so a malformed auto-instrumented payload can't throw and disable the plugin.
462
- /**
463
- * @param {string} fmt
464
- * @param {Record<string, string>} [mimeTypeLookup]
465
- * @returns {string}
466
- */
467
- function audioMimeTypeFromFormat (fmt, mimeTypeLookup = {}) {
468
- fmt = typeof fmt === 'string' ? fmt.trim().toLowerCase() : ''
469
- if (!fmt) return 'audio/wav'
470
- return mimeTypeLookup[fmt] ?? `audio/${fmt}`
471
- }
472
-
473
- // Builds an audio part from raw audio bytes (base64-encoded) or an existing base64 string. Only
474
- // Buffer/Uint8Array inputs are base64-encoded; any other shape is passed through so a malformed
475
- // auto-instrumented payload can't throw (the tagger soft-skips a non-string `content`).
476
- /**
477
- * @param {Buffer | Uint8Array | string} data
478
- * @param {string} mimeType
479
- * @returns {{ mimeType: string, content: string }}
480
- */
481
- function formatAudioPart (data, mimeType) {
482
- const content = Buffer.isBuffer(data) || ArrayBuffer.isView(data)
483
- ? Buffer.from(data).toString('base64')
484
- : data
485
- return { mimeType, content }
486
- }
487
-
488
456
  module.exports = {
489
457
  agentNameWireSafe,
490
- audioMimeTypeFromFormat,
491
458
  encodeUnicode,
492
459
  findGenAIAncestorSpanId,
493
460
  generateLlmObsTraceId,
494
461
  llmObsTraceIdToWire,
495
462
  normalizeLlmObsTraceId,
496
- formatAudioPart,
497
463
  resolveAgentAttribution,
498
464
  validateCostTags,
499
465
  validateKind,
@@ -102,7 +102,6 @@ class BaseLLMObsWriter {
102
102
  * @param {Record<string, unknown>} event
103
103
  * @param {{ apiKey?: string, site?: string }} [routing]
104
104
  * @param {number} [byteLength]
105
- * @returns {boolean} `true` if the event was buffered, `false` if it was dropped
106
105
  * (e.g. the per-routing buffer was full). Callers that depend on the event
107
106
  * actually being submitted should check this value.
108
107
  */
@@ -8,10 +8,9 @@ const { fetchAgentInfo } = require('../../agent/info')
8
8
  /**
9
9
  * @param {import('../../config/config-base')} config
10
10
  * @param {(agentless: boolean) => void} setWritersAgentlessValue
11
- * @returns {void}
12
11
  */
13
12
  function setAgentStrategy (config, setWritersAgentlessValue) {
14
- const agentlessEnabled = config.llmobs.agentlessEnabled
13
+ const agentlessEnabled = config.llmobs.DD_LLMOBS_AGENTLESS_ENABLED
15
14
 
16
15
  if (agentlessEnabled != null) {
17
16
  setWritersAgentlessValue(agentlessEnabled)
@@ -9,6 +9,8 @@ const debugChannel = channel('datadog:log:debug')
9
9
  const infoChannel = channel('datadog:log:info')
10
10
  const warnChannel = channel('datadog:log:warn')
11
11
  const errorChannel = channel('datadog:log:error')
12
+ // Telemetry needs the unformatted message, cause and transmission policy, not console output.
13
+ const errorRecordChannel = channel('datadog:log:error:record')
12
14
 
13
15
  const defaultLevel = Level.debug
14
16
 
@@ -67,4 +69,5 @@ module.exports = {
67
69
  infoChannel,
68
70
  warnChannel,
69
71
  errorChannel,
72
+ errorRecordChannel,
70
73
  }