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
@@ -0,0 +1,92 @@
1
+ 'use strict'
2
+
3
+ const VARIABLE_PATTERN = /(?<!\{)(?:\{\{\s*(\w+)\s*\}\}(?!\})|\{\s*(\w+)\s*\}(?!\}))/g
4
+
5
+ function render (template, variables) {
6
+ return template.replaceAll(VARIABLE_PATTERN, (match, doubleName, singleName) => {
7
+ const name = doubleName ?? singleName
8
+ return Object.hasOwn(variables, name) ? String(variables[name]) : match
9
+ })
10
+ }
11
+
12
+ class ManagedPrompt {
13
+ /**
14
+ * @param {object} data
15
+ * @param {string} data.id
16
+ * @param {string} data.version
17
+ * @param {'registry'|'cache'|'fallback'|'ff'|'resolve'} data.source
18
+ * @param {string | Array<{role: string, content: string}>} data.template
19
+ * @param {string} [data.promptUuid]
20
+ * @param {string} [data.promptVersionUuid]
21
+ */
22
+ constructor ({ id, version, source, template, promptUuid, promptVersionUuid }) {
23
+ this.id = id
24
+ this.version = version
25
+ this.source = source
26
+ this.template = Array.isArray(template)
27
+ ? Object.freeze(template.map(message => Object.freeze({ role: message.role, content: message.content })))
28
+ : template
29
+ this.promptUuid = promptUuid
30
+ this.promptVersionUuid = promptVersionUuid
31
+ Object.freeze(this)
32
+ }
33
+
34
+ /**
35
+ * Render the prompt without changing its stored template.
36
+ * @param {Record<string, unknown>} [variables]
37
+ * @returns {string | Array<{role: string, content: string}>}
38
+ */
39
+ format (variables = {}) {
40
+ if (typeof this.template === 'string') return render(this.template, variables)
41
+ return this.template.map(message => ({ role: message.role, content: render(message.content, variables) }))
42
+ }
43
+
44
+ /**
45
+ * Convert the managed prompt to the existing public annotation shape.
46
+ * @param {Record<string, unknown>} [variables]
47
+ * @returns {Record<string, unknown>}
48
+ */
49
+ toAnnotation (variables) {
50
+ const annotation = {
51
+ id: this.id,
52
+ version: this.version,
53
+ template: typeof this.template === 'string' ? this.template : this.template.map(message => ({ ...message })),
54
+ }
55
+ const entries = Object.entries(variables ?? {})
56
+ if (entries.length) {
57
+ annotation.variables = Object.fromEntries(entries.map(([name, value]) => [name, String(value)]))
58
+ }
59
+ if (this.promptUuid) annotation.promptUuid = this.promptUuid
60
+ if (this.promptVersionUuid) annotation.promptVersionUuid = this.promptVersionUuid
61
+ return annotation
62
+ }
63
+
64
+ /**
65
+ * Convert a caller fallback to a managed prompt.
66
+ * @param {string} promptId
67
+ * @param {string | object | Array<{role: string, content: string}> |
68
+ * (() => string | object | Array<{role: string, content: string}>)} fallback
69
+ * @returns {ManagedPrompt}
70
+ */
71
+ static fromFallback (promptId, fallback) {
72
+ const value = typeof fallback === 'function' ? fallback() : fallback
73
+ const promptLike = value && !Array.isArray(value) && typeof value === 'object'
74
+ const template = promptLike ? value.template : value
75
+ const validTemplate = typeof template === 'string' || (
76
+ Array.isArray(template) && template.every(message => {
77
+ return message && typeof message.role === 'string' && typeof message.content === 'string'
78
+ })
79
+ )
80
+ if (!validTemplate) {
81
+ throw new TypeError('Invalid prompt fallback: expected a string, chat message array, or object with a template')
82
+ }
83
+ return new ManagedPrompt({
84
+ id: promptId,
85
+ version: String(promptLike && value.version ? value.version : 'fallback'),
86
+ source: 'fallback',
87
+ template,
88
+ })
89
+ }
90
+ }
91
+
92
+ 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')
@@ -522,17 +536,26 @@ class LLMObsTagger {
522
536
  }
523
537
 
524
538
  const validatedPrompt = {}
525
- if (finalPromptId) validatedPrompt.id = finalPromptId
539
+ if (finalPromptId && (!currentPrompt || replacesPrompt)) validatedPrompt.id = finalPromptId
526
540
  if (version) validatedPrompt.version = version
541
+ if (promptUuid) validatedPrompt.prompt_uuid = promptUuid
542
+ if (promptVersionUuid) validatedPrompt.prompt_version_uuid = promptVersionUuid
527
543
  if (variables) validatedPrompt.variables = variables
528
544
  if (finalTemplate) validatedPrompt.template = finalTemplate
529
545
  if (finalChatTemplate?.length) validatedPrompt.chat_template = finalChatTemplate
530
546
  if (tags) validatedPrompt.tags = tags
531
- if (finalCtxVariablesKeys) validatedPrompt[INTERNAL_CONTEXT_VARIABLE_KEYS] = finalCtxVariablesKeys
532
- if (finalQueryVariablesKeys) validatedPrompt[INTERNAL_QUERY_VARIABLE_KEYS] = finalQueryVariablesKeys
547
+ if (finalCtxVariablesKeys && (!currentPrompt || replacesPrompt || contextVariables != null)) {
548
+ validatedPrompt[INTERNAL_CONTEXT_VARIABLE_KEYS] = finalCtxVariablesKeys
549
+ }
550
+ if (finalQueryVariablesKeys && (!currentPrompt || replacesPrompt || queryVariables != null)) {
551
+ validatedPrompt[INTERNAL_QUERY_VARIABLE_KEYS] = finalQueryVariablesKeys
552
+ }
533
553
 
534
- const currentPrompt = registry.get(span)?.[INPUT_PROMPT]
535
554
  if (currentPrompt) {
555
+ if (replacesPrompt) {
556
+ if (promptUuid == null) currentPrompt.prompt_uuid = undefined
557
+ if (promptVersionUuid == null) currentPrompt.prompt_version_uuid = undefined
558
+ }
536
559
  Object.assign(currentPrompt, validatedPrompt)
537
560
  } else {
538
561
  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
  }
@@ -8,7 +8,7 @@ const { getValueFromEnvSources } = require('../config/helper')
8
8
  // `config/defaults` defers its own `dns` require until after it exports, so this no longer hits
9
9
  // the `config/defaults` <-> log parse cycle that motivated the lazy require below.
10
10
  const { defaults } = require('../config/defaults')
11
- const { traceChannel, debugChannel, infoChannel, warnChannel, errorChannel } = require('./channels')
11
+ const { traceChannel, debugChannel, infoChannel, warnChannel, errorChannel, errorRecordChannel } = require('./channels')
12
12
  const logWriter = require('./writer')
13
13
  const { Log, LogConfig, NoTransmitError } = require('./log')
14
14
 
@@ -95,10 +95,25 @@ const log = {
95
95
  }
96
96
 
97
97
  function publishFormatted (ch, formatter, ...args) {
98
- if (ch.hasSubscribers) {
99
- const log = Log.parse(...args)
100
- const { formatted, cause } = getErrorLog(log)
98
+ const publishRecord = ch === errorChannel && errorRecordChannel.hasSubscribers
99
+ const publishLog = ch.hasSubscribers
100
+ if (!publishLog && !publishRecord) return
101
+
102
+ let record
103
+ try {
104
+ const parsed = Log.parse(...args)
105
+ if (!publishLog && !parsed.sendViaTelemetry) return
106
+ record = getErrorLog(parsed)
107
+ } catch (err) {
108
+ // Telemetry must not expose parsing failures while the configured logger is disabled.
109
+ if (!publishLog) return
110
+ throw err
111
+ }
112
+
113
+ if (publishRecord) errorRecordChannel.publish(record)
101
114
 
115
+ if (publishLog) {
116
+ const { formatted, cause } = record
102
117
  // calling twice ch.publish() because Error cause is only available in Node.js v16.9.0
103
118
  // TODO: replace it with Error(message, { cause }) when cause has broad support
104
119
  if (formatted) ch.publish(formatter?.(formatted) || formatted)
@@ -109,7 +124,10 @@ function publishFormatted (ch, formatter, ...args) {
109
124
  function getErrorLog (err) {
110
125
  if (typeof err?.delegate === 'function') {
111
126
  const result = err.delegate(...err.args)
112
- return Array.isArray(result) ? Log.parse(...result) : Log.parse(result)
127
+ const resolved = Array.isArray(result) ? Log.parse(...result) : Log.parse(result)
128
+ resolved.cause ??= err.cause
129
+ resolved.sendViaTelemetry &&= err.sendViaTelemetry
130
+ return resolved
113
131
  }
114
132
  return err
115
133
  }