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
@@ -9,7 +9,6 @@ const TAGS = require('./tags')
9
9
  * Resolves the AI Guard host for a Datadog site.
10
10
  *
11
11
  * @param {string} site
12
- * @returns {string}
13
12
  */
14
13
  function aiGuardHost (site) {
15
14
  return site.split('.').length === 2 ? `app.${site}` : site
@@ -60,9 +59,9 @@ class AIGuardClient {
60
59
  'DD-AI-GUARD-SOURCE': 'SDK',
61
60
  'DD-AI-GUARD-LANGUAGE': 'nodejs',
62
61
  }
63
- const endpoint = config.experimental.aiguard.endpoint || `https://${aiGuardHost(config.site)}/api/v2/ai-guard`
62
+ const endpoint = config.aiguard.DD_AI_GUARD_ENDPOINT || `https://${aiGuardHost(config.site)}/api/v2/ai-guard`
64
63
  this.#evaluateUrl = `${endpoint}/evaluate`
65
- this.#timeout = config.experimental.aiguard.timeout
64
+ this.#timeout = config.aiguard.DD_AI_GUARD_TIMEOUT
66
65
  }
67
66
 
68
67
  /**
@@ -9,6 +9,7 @@ const { keepTrace } = require('../priority_sampler')
9
9
  const { extractIp } = require('../plugins/util/ip_extractor')
10
10
  const { AI_GUARD } = require('../standalone/product')
11
11
  const telemetryMetrics = require('../telemetry/metrics')
12
+ const { truncateString } = require('../util')
12
13
  const { normalizeRedactionReplacements, redactMessages } = require('./redaction')
13
14
  const TAGS = require('./tags')
14
15
 
@@ -96,7 +97,6 @@ function parseEvaluationResponse (body) {
96
97
  *
97
98
  * @param {boolean} block
98
99
  * @param {{ action: string, blockingEnabled: boolean }} evaluation
99
- * @returns {boolean}
100
100
  */
101
101
  function shouldBlockEvaluation (block, evaluation) {
102
102
  return block && evaluation.blockingEnabled && evaluation.action !== ALLOW
@@ -158,9 +158,9 @@ class EvaluationReporter {
158
158
  */
159
159
  constructor (config) {
160
160
  this.#config = config
161
- this.#maxMessagesLength = config.experimental.aiguard.maxMessagesLength
162
- this.#maxContentSize = config.experimental.aiguard.maxContentSize
163
- this.#redactionEnabled = config.experimental.aiguard.redactionEnabled
161
+ this.#maxMessagesLength = config.aiguard.DD_AI_GUARD_MAX_MESSAGES_LENGTH
162
+ this.#maxContentSize = config.aiguard.DD_AI_GUARD_MAX_CONTENT_SIZE
163
+ this.#redactionEnabled = config.aiguard.DD_AI_GUARD_REDACTION_ENABLED
164
164
  }
165
165
 
166
166
  /**
@@ -211,7 +211,6 @@ class EvaluationReporter {
211
211
  *
212
212
  * @param {EvaluationReport} report
213
213
  * @param {string} errorType
214
- * @returns {void}
215
214
  */
216
215
  fail (report, errorType) {
217
216
  report.metaStruct.messages = this.#buildMessagesForMetaStruct(report.messages, report.telemetryTags)
@@ -224,7 +223,6 @@ class EvaluationReporter {
224
223
  *
225
224
  * @param {EvaluationReport} report
226
225
  * @param {EvaluationOutcome} outcome
227
- * @returns {void}
228
226
  */
229
227
  finish (report, outcome) {
230
228
  const { result, redaction, shouldBlock } = outcome
@@ -299,14 +297,13 @@ class EvaluationReporter {
299
297
  * Truncates text in a cloned message to one shared content-size limit.
300
298
  *
301
299
  * @param {{ content?: string|ContentPart[] }} message
302
- * @returns {boolean}
303
300
  */
304
301
  #truncateMessageContent (message) {
305
302
  const { content } = message
306
303
  if (typeof content === 'string') {
307
304
  if (content.length <= this.#maxContentSize) return false
308
305
 
309
- message.content = content.slice(0, this.#maxContentSize)
306
+ message.content = truncateString(content, this.#maxContentSize)
310
307
  return true
311
308
  }
312
309
 
@@ -319,7 +316,7 @@ class EvaluationReporter {
319
316
  if (typeof text !== 'string') continue
320
317
 
321
318
  if (text.length > remainingContentSize) {
322
- part.text = text.slice(0, remainingContentSize)
319
+ part.text = truncateString(text, remainingContentSize)
323
320
  truncated = true
324
321
  remainingContentSize = 0
325
322
  } else {
@@ -333,7 +330,6 @@ class EvaluationReporter {
333
330
  * Returns whether a message represents a tool call or tool output.
334
331
  *
335
332
  * @param {Message} message
336
- * @returns {boolean}
337
333
  */
338
334
  #isToolCall (message) {
339
335
  return Boolean(message.tool_calls || message.tool_call_id)
@@ -370,7 +366,6 @@ class EvaluationReporter {
370
366
  * Adds missing client IP tags to the service entry span.
371
367
  *
372
368
  * @param {Span} rootSpan
373
- * @returns {void}
374
369
  */
375
370
  #setRootSpanClientIpTags (rootSpan) {
376
371
  const currentTags = rootSpan.context().getTags()
@@ -411,7 +406,6 @@ class EvaluationReporter {
411
406
  *
412
407
  * @param {Span} guardSpan
413
408
  * @param {Span} rootSpan
414
- * @returns {void}
415
409
  */
416
410
  #copyServiceEntryTagsToGuardSpan (guardSpan, rootSpan) {
417
411
  const rootTags = rootSpan.context().getTags()
@@ -17,10 +17,11 @@ function enable (tracer, config) {
17
17
 
18
18
  try {
19
19
  aiguard = new AIGuard(tracer, config)
20
- const block = config.experimental?.aiguard?.block !== false
20
+ const block = config.aiguard.DD_AI_GUARD_BLOCK !== false
21
+ const analyzeStreamResponses = config.aiguard.DD_AI_GUARD_ANALYZE_STREAM_RESPONSES_ENABLED
21
22
 
22
23
  incomingHttpRequestStart.subscribe(onIncomingHttpRequestStart)
23
- integrations.enable(aiguard, block)
24
+ integrations.enable(aiguard, block, analyzeStreamResponses)
24
25
 
25
26
  isEnabled = true
26
27
  } catch (err) {
@@ -2,20 +2,20 @@
2
2
 
3
3
  const { channel } = require('dc-polyfill')
4
4
 
5
- const log = require('../../log')
6
5
  const { getMessagesInputMessages, getMessagesOutputMessages } = require('../messages/anthropic')
6
+ const { decode } = require('../messages/utils')
7
7
  const { SOURCE_AUTO } = require('../tags')
8
- const { pushEvaluation } = require('./evaluate')
8
+ const { evaluate } = require('./evaluate')
9
9
 
10
- const messagesBeforeChannel = channel('dd-trace:anthropic:messages:before')
11
- const messagesAfterChannel = channel('dd-trace:anthropic:messages:after')
10
+ const messagesPrepareChannel = channel('dd-trace:anthropic:messages:prepare')
11
+ const messagesInterceptChannel = channel('dd-trace:anthropic:messages:intercept')
12
12
 
13
13
  let isEnabled = false
14
14
  let aiguard
15
15
  let opts
16
16
 
17
17
  /**
18
- * Subscribes AI Guard to Anthropic lifecycle channels.
18
+ * Subscribes AI Guard to the Anthropic interception channels.
19
19
  *
20
20
  * @param {object} aiguardInstance
21
21
  * @param {boolean} block
@@ -26,8 +26,8 @@ function enable (aiguardInstance, block) {
26
26
  aiguard = aiguardInstance
27
27
  opts = { block, source: SOURCE_AUTO, integration: 'anthropic' }
28
28
 
29
- messagesBeforeChannel.subscribe(onMessagesBefore)
30
- messagesAfterChannel.subscribe(onMessagesAfter)
29
+ messagesPrepareChannel.subscribe(onMessagesPrepare)
30
+ messagesInterceptChannel.subscribe(onMessagesIntercept)
31
31
 
32
32
  isEnabled = true
33
33
  }
@@ -35,36 +35,64 @@ function enable (aiguardInstance, block) {
35
35
  function disable () {
36
36
  if (!isEnabled) return
37
37
 
38
- messagesBeforeChannel.unsubscribe(onMessagesBefore)
39
- messagesAfterChannel.unsubscribe(onMessagesAfter)
38
+ messagesPrepareChannel.unsubscribe(onMessagesPrepare)
39
+ messagesInterceptChannel.unsubscribe(onMessagesIntercept)
40
40
 
41
41
  aiguard = undefined
42
42
  opts = undefined
43
43
  isEnabled = false
44
44
  }
45
45
 
46
- function onMessagesBefore (ctx) {
47
- pushEvaluation(ctx, aiguard, getMessagesInputMessages(ctx.args?.[0]), opts)
46
+ /**
47
+ * Replaces the outgoing options with a JSON snapshot of the evaluated fields, so AI Guard judges
48
+ * exactly what the SDK serializes and sends, immune to later caller mutation.
49
+ *
50
+ * @param {{ arguments: Array<unknown> }} ctx
51
+ */
52
+ function onMessagesPrepare (ctx) {
53
+ const options = ctx.arguments[0]
54
+ if (!options || typeof options !== 'object') return
55
+
56
+ const evaluated = { messages: options.messages }
57
+ if (options.system !== undefined) evaluated.system = options.system
58
+
59
+ try {
60
+ // eslint-disable-next-line unicorn/prefer-structured-clone
61
+ ctx.arguments[0] = { ...options, ...JSON.parse(JSON.stringify(evaluated)) }
62
+ } catch {
63
+ // Unserializable input — judge the caller's options as they are.
64
+ }
48
65
  }
49
66
 
50
- function onMessagesAfter (ctx) {
51
- const inputMessages = getMessagesInputMessages(ctx.args?.[0])
67
+ function onMessagesIntercept (ctx) {
68
+ const inputMessages = getMessagesInputMessages(ctx.arguments?.[0])
52
69
  if (!inputMessages?.length) return
53
70
 
54
- let body = ctx.body
55
- if (typeof body === 'string') {
56
- try {
57
- body = JSON.parse(body)
58
- } catch {
59
- log.error('AIGuard: unable to decode Anthropic response body')
60
- return
61
- }
71
+ // `parse` and `asResponse` are both wrapped, and either may run more than once per call.
72
+ let inputEvaluation
73
+ ctx.beforeResult = () => {
74
+ if (!isEnabled) return
75
+ inputEvaluation ??= evaluate(ctx, aiguard, [inputMessages], opts)
76
+ return inputEvaluation
62
77
  }
63
78
 
64
- const outputMessages = getMessagesOutputMessages(body)
65
- if (!outputMessages.length) return
66
-
67
- pushEvaluation(ctx, aiguard, [...inputMessages, ...outputMessages], opts)
79
+ // One model call has one output however many readers observe it: `parse`, `json()`, `text()`
80
+ // and every `clone()` of the raw response share this callback.
81
+ let outputEvaluation
82
+ ctx.onResult = body => {
83
+ if (!isEnabled) return body
84
+
85
+ const outputMessages = decode(
86
+ () => getMessagesOutputMessages(body),
87
+ null,
88
+ 'AIGuard: unable to decode Anthropic response body: %s'
89
+ )
90
+ if (outputMessages === null) return body
91
+ if (!outputMessages.length) return body
92
+
93
+ outputEvaluation ??= evaluate(ctx, aiguard, [[...inputMessages, ...outputMessages]], opts)
94
+ return outputEvaluation.then(() => body)
95
+ }
68
96
  }
69
97
 
70
98
  module.exports = { enable, disable }
@@ -3,44 +3,68 @@
3
3
  const log = require('../../log')
4
4
 
5
5
  /**
6
- * Starts one AI Guard evaluation for a lifecycle ctx.
6
+ * Runs one evaluation per conversation and rejects if any of them says block.
7
7
  *
8
- * Async evaluations are pushed synchronously during channel publication. If
9
- * evaluation throws synchronously, the error is handled before publish returns.
8
+ * Evaluations run concurrently and all of them are awaited, so a slow one cannot hide a block
9
+ * from a fast one. Any failure that is not a verdict is logged and ignored, because AI Guard
10
+ * must never break the caller.
10
11
  *
11
- * @param {object} ctx
12
- * @param {AbortController} ctx.abortController
13
- * @param {object} [ctx.parentSpan]
14
- * @param {Array<Promise<void>>} ctx.pending
12
+ * @param {object} interceptCtx The intercept payload, whose tracing ctx parents the span.
15
13
  * @param {object} aiguard
16
- * @param {Array<object>|undefined} messages
14
+ * @param {Array<Array<object>>} conversations One complete message list per evaluation.
17
15
  * @param {object} opts
16
+ * @returns {Promise<void>|undefined} `undefined` when there is nothing to evaluate.
18
17
  */
19
- function pushEvaluation (ctx, aiguard, messages, opts) {
20
- if (!messages?.length) return
18
+ function evaluate (interceptCtx, aiguard, conversations, opts) {
19
+ if (!conversations?.length) return
21
20
 
22
- const evaluateOpts = ctx.parentSpan ? { ...opts, childOf: ctx.parentSpan } : opts
21
+ const parentSpan = interceptCtx?.tracingContext?.currentStore?.span
22
+ const evaluateOpts = parentSpan ? { ...opts, childOf: parentSpan } : opts
23
+ const evaluations = []
23
24
 
24
- try {
25
- ctx.pending.push(aiguard.evaluate(messages, evaluateOpts).catch(handleEvaluationError.bind(null, ctx)))
26
- } catch (err) {
27
- handleEvaluationError(ctx, err)
25
+ for (const messages of conversations) {
26
+ if (messages?.length) evaluations.push(evaluateOne(aiguard, messages, evaluateOpts))
28
27
  }
28
+
29
+ if (evaluations.length === 0) return
30
+
31
+ return Promise.allSettled(evaluations).then(throwFirstBlock)
29
32
  }
30
33
 
31
34
  /**
32
- * Handles an AI Guard evaluation failure.
35
+ * Turns a synchronous throw into a rejection, so every outcome is inspected the same way.
33
36
  *
34
- * @param {object} ctx
35
- * @param {AbortController} ctx.abortController
36
- * @param {Error} err
37
+ * @param {object} aiguard
38
+ * @param {Array<object>} messages
39
+ * @param {object} opts
40
+ * @returns {Promise<unknown>}
41
+ */
42
+ function evaluateOne (aiguard, messages, opts) {
43
+ try {
44
+ return aiguard.evaluate(messages, opts)
45
+ } catch (error) {
46
+ return Promise.reject(error)
47
+ }
48
+ }
49
+
50
+ /**
51
+ * @param {Array<{ status: string, reason?: Error }>} results
52
+ * @throws {Error} The first block verdict, if any evaluation returned one.
37
53
  */
38
- function handleEvaluationError (ctx, err) {
39
- if (err.name === 'AIGuardAbortError') {
40
- ctx.abortController.abort(err)
41
- } else {
42
- log.error('AIGuard: unexpected error during evaluation: %s', err.message)
54
+ function throwFirstBlock (results) {
55
+ let block
56
+
57
+ for (const result of results) {
58
+ if (result.status === 'fulfilled') continue
59
+
60
+ if (result.reason?.name === 'AIGuardAbortError') {
61
+ block ??= result.reason
62
+ } else {
63
+ log.error('AIGuard: unexpected error during evaluation: %s', result.reason?.message)
64
+ }
43
65
  }
66
+
67
+ if (block) throw block
44
68
  }
45
69
 
46
- module.exports = { pushEvaluation }
70
+ module.exports = { evaluate }
@@ -11,13 +11,14 @@ let isEnabled = false
11
11
  *
12
12
  * @param {object} aiguard
13
13
  * @param {boolean} block
14
+ * @param {boolean} analyzeStreamResponses
14
15
  */
15
- function enable (aiguard, block) {
16
+ function enable (aiguard, block, analyzeStreamResponses) {
16
17
  if (isEnabled) return
17
18
 
18
19
  anthropic.enable(aiguard, block)
19
- openai.enable(aiguard, block)
20
- vercelAi.enable(aiguard, block)
20
+ openai.enable(aiguard, block, analyzeStreamResponses)
21
+ vercelAi.enable(aiguard, block, analyzeStreamResponses)
21
22
 
22
23
  isEnabled = true
23
24
  }
@@ -7,35 +7,37 @@ const {
7
7
  getChatCompletionsOutputMessages,
8
8
  getResponsesInputMessages,
9
9
  getResponsesOutputMessages,
10
+ getStreamedChatCompletionsOutputMessages,
11
+ getStreamedResponsesOutputMessages,
10
12
  } = require('../messages/openai')
13
+ const { decode } = require('../messages/utils')
11
14
  const { SOURCE_AUTO } = require('../tags')
12
- const { pushEvaluation } = require('./evaluate')
15
+ const { evaluate } = require('./evaluate')
13
16
 
14
- const chatCompletionsBeforeChannel = channel('dd-trace:openai:chat.completions:before')
15
- const chatCompletionsAfterChannel = channel('dd-trace:openai:chat.completions:after')
16
- const responsesBeforeChannel = channel('dd-trace:openai:responses:before')
17
- const responsesAfterChannel = channel('dd-trace:openai:responses:after')
17
+ const chatCompletionsInterceptChannel = channel('dd-trace:openai:chat.completions:intercept')
18
+ const responsesInterceptChannel = channel('dd-trace:openai:responses:intercept')
18
19
 
19
20
  let isEnabled = false
20
21
  let aiguard
21
22
  let opts
23
+ let analyzeStreamResponses
22
24
 
23
25
  /**
24
- * Subscribes AI Guard to OpenAI lifecycle channels.
26
+ * Subscribes AI Guard to the OpenAI interception channels.
25
27
  *
26
28
  * @param {object} aiguardInstance
27
29
  * @param {boolean} block
30
+ * @param {boolean} analyzeStreams
28
31
  */
29
- function enable (aiguardInstance, block) {
32
+ function enable (aiguardInstance, block, analyzeStreams) {
30
33
  if (isEnabled) return
31
34
 
32
35
  aiguard = aiguardInstance
33
36
  opts = { block, source: SOURCE_AUTO, integration: 'openai' }
37
+ analyzeStreamResponses = analyzeStreams
34
38
 
35
- chatCompletionsBeforeChannel.subscribe(onChatCompletionsBefore)
36
- chatCompletionsAfterChannel.subscribe(onChatCompletionsAfter)
37
- responsesBeforeChannel.subscribe(onResponsesBefore)
38
- responsesAfterChannel.subscribe(onResponsesAfter)
39
+ chatCompletionsInterceptChannel.subscribe(onChatCompletions)
40
+ responsesInterceptChannel.subscribe(onResponses)
39
41
 
40
42
  isEnabled = true
41
43
  }
@@ -43,38 +45,139 @@ function enable (aiguardInstance, block) {
43
45
  function disable () {
44
46
  if (!isEnabled) return
45
47
 
46
- chatCompletionsBeforeChannel.unsubscribe(onChatCompletionsBefore)
47
- chatCompletionsAfterChannel.unsubscribe(onChatCompletionsAfter)
48
- responsesBeforeChannel.unsubscribe(onResponsesBefore)
49
- responsesAfterChannel.unsubscribe(onResponsesAfter)
48
+ chatCompletionsInterceptChannel.unsubscribe(onChatCompletions)
49
+ responsesInterceptChannel.unsubscribe(onResponses)
50
50
 
51
51
  aiguard = undefined
52
52
  opts = undefined
53
+ analyzeStreamResponses = undefined
53
54
  isEnabled = false
54
55
  }
55
56
 
56
- function onChatCompletionsBefore (ctx) {
57
- pushEvaluation(ctx, aiguard, getChatCompletionsInputMessages(ctx.args?.[0]), opts)
57
+ function onChatCompletions (ctx) {
58
+ const inputMessages = getChatCompletionsInputMessages(ctx.arguments?.[0])
59
+ if (!inputMessages?.length) return
60
+
61
+ let inputEvaluation
62
+ ctx.beforeResult = () => {
63
+ if (!isEnabled) return
64
+ inputEvaluation ??= evaluate(ctx, aiguard, [inputMessages], opts)
65
+ return inputEvaluation
66
+ }
67
+
68
+ const isStream = ctx.arguments[0].stream
69
+ if (isStream && !analyzeStreamResponses) return
70
+
71
+ let outputEvaluation
72
+ ctx.onResult = body => {
73
+ if (!isEnabled) return body
74
+
75
+ if (isStream) {
76
+ outputEvaluation ??= interceptStream(body, chunks => {
77
+ return getStreamedChatCompletionsOutputMessages(chunks)
78
+ .map(message => [...inputMessages, message])
79
+ }, ctx)
80
+ return outputEvaluation
81
+ }
82
+
83
+ const conversations = decode(
84
+ () => getChatCompletionsOutputMessages(body).map(message => [...inputMessages, message]),
85
+ null,
86
+ 'AIGuard: unable to decode OpenAI response body: %s'
87
+ )
88
+ if (!conversations?.length) return body
89
+
90
+ outputEvaluation ??= evaluate(ctx, aiguard, conversations, opts)
91
+ return outputEvaluation.then(() => body)
92
+ }
58
93
  }
59
94
 
60
- function onChatCompletionsAfter (ctx) {
61
- const inputMessages = getChatCompletionsInputMessages(ctx.args?.[0])
95
+ function onResponses (ctx) {
96
+ const inputMessages = getResponsesInputMessages(ctx.arguments?.[0])
62
97
  if (!inputMessages?.length) return
63
- for (const message of getChatCompletionsOutputMessages(ctx.body)) {
64
- pushEvaluation(ctx, aiguard, [...inputMessages, message], opts)
98
+
99
+ let inputEvaluation
100
+ ctx.beforeResult = () => {
101
+ if (!isEnabled) return
102
+ inputEvaluation ??= evaluate(ctx, aiguard, [inputMessages], opts)
103
+ return inputEvaluation
104
+ }
105
+
106
+ const isStream = ctx.arguments[0].stream
107
+ if (isStream && !analyzeStreamResponses) return
108
+
109
+ let outputEvaluation
110
+ ctx.onResult = body => {
111
+ if (!isEnabled) return body
112
+
113
+ if (isStream) {
114
+ outputEvaluation ??= interceptStream(body, chunks => {
115
+ const outputMessages = getStreamedResponsesOutputMessages(chunks)
116
+ return outputMessages.length ? [[...inputMessages, ...outputMessages]] : []
117
+ }, ctx)
118
+ return outputEvaluation
119
+ }
120
+
121
+ const outputMessages = decode(
122
+ () => getResponsesOutputMessages(body),
123
+ null,
124
+ 'AIGuard: unable to decode OpenAI response body: %s'
125
+ )
126
+ if (!outputMessages?.length) return body
127
+
128
+ outputEvaluation ??= evaluate(ctx, aiguard, [[...inputMessages, ...outputMessages]], opts)
129
+ return outputEvaluation.then(() => body)
65
130
  }
66
131
  }
67
132
 
68
- function onResponsesBefore (ctx) {
69
- pushEvaluation(ctx, aiguard, getResponsesInputMessages(ctx.args?.[0]), opts)
133
+ /**
134
+ * Uses the OpenAI SDK's stream splitting support to evaluate one branch and return the other.
135
+ *
136
+ * @param {object} stream
137
+ * @param {(chunks: Array<object>) => Array<Array<object>>} getConversations
138
+ * @param {object} ctx
139
+ * @returns {object|Promise<object>}
140
+ */
141
+ function interceptStream (stream, getConversations, ctx) {
142
+ if (typeof stream?.tee !== 'function') return stream
143
+
144
+ let branches
145
+ try {
146
+ branches = stream.tee()
147
+ } catch {
148
+ return stream
149
+ }
150
+
151
+ const [evaluationStream, resultStream] = branches
152
+ return drainStream(evaluationStream).then(chunks => {
153
+ if (!isEnabled) return resultStream
154
+ const conversations = decode(
155
+ () => getConversations(chunks),
156
+ null,
157
+ 'AIGuard: unable to decode the streamed OpenAI response: %s'
158
+ )
159
+ if (!conversations?.length) return resultStream
160
+ return evaluate(ctx, aiguard, conversations, opts).then(() => resultStream)
161
+ }, () => resultStream)
70
162
  }
71
163
 
72
- function onResponsesAfter (ctx) {
73
- const inputMessages = getResponsesInputMessages(ctx.args?.[0])
74
- if (!inputMessages?.length) return
75
- const outputMessages = getResponsesOutputMessages(ctx.body)
76
- if (!outputMessages.length) return
77
- pushEvaluation(ctx, aiguard, [...inputMessages, ...outputMessages], opts)
164
+ /**
165
+ * @param {object} stream
166
+ * @returns {Promise<Array<object>>}
167
+ */
168
+ function drainStream (stream) {
169
+ const chunks = []
170
+ const iterator = stream[Symbol.asyncIterator]()
171
+
172
+ function readAll () {
173
+ return iterator.next().then(({ done, value }) => {
174
+ if (done) return chunks
175
+ chunks.push(value)
176
+ return readAll()
177
+ })
178
+ }
179
+
180
+ return readAll()
78
181
  }
79
182
 
80
183
  module.exports = { enable, disable }