dd-trace 6.10.0 → 6.11.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 (137) hide show
  1. package/README.md +0 -17
  2. package/index.d.ts +108 -3
  3. package/initialize.mjs +28 -17
  4. package/openfeature.js +2 -2
  5. package/package.json +11 -8
  6. package/packages/datadog-esbuild/index.js +0 -34
  7. package/packages/datadog-instrumentations/src/anthropic.js +210 -15
  8. package/packages/datadog-instrumentations/src/aws-sdk.js +9 -1
  9. package/packages/datadog-instrumentations/src/browser-bunyan.js +11 -0
  10. package/packages/datadog-instrumentations/src/bunyan.js +3 -16
  11. package/packages/datadog-instrumentations/src/cucumber.js +26 -11
  12. package/packages/datadog-instrumentations/src/cypress-config.js +182 -16
  13. package/packages/datadog-instrumentations/src/cypress-legacy-finalizers.js +30 -0
  14. package/packages/datadog-instrumentations/src/cypress.js +2 -0
  15. package/packages/datadog-instrumentations/src/fastify.js +2 -0
  16. package/packages/datadog-instrumentations/src/helpers/bunyan.js +22 -0
  17. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -1
  18. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +9 -4
  19. package/packages/datadog-instrumentations/src/helpers/register.js +10 -8
  20. package/packages/datadog-instrumentations/src/jest.js +25 -17
  21. package/packages/datadog-instrumentations/src/mariadb.js +278 -21
  22. package/packages/datadog-instrumentations/src/mocha/main.js +539 -55
  23. package/packages/datadog-instrumentations/src/mocha/utils.js +130 -17
  24. package/packages/datadog-instrumentations/src/playwright-reporter.js +99 -0
  25. package/packages/datadog-instrumentations/src/playwright.js +149 -2
  26. package/packages/datadog-instrumentations/src/router.js +2 -0
  27. package/packages/datadog-instrumentations/src/vitest-main.js +11 -3
  28. package/packages/datadog-plugin-aws-sdk/src/base.js +1 -0
  29. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +163 -44
  30. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -8
  31. package/packages/datadog-plugin-azure-functions/src/index.js +1 -1
  32. package/packages/datadog-plugin-browser-bunyan/src/index.js +9 -0
  33. package/packages/datadog-plugin-bunyan/src/index.js +5 -8
  34. package/packages/datadog-plugin-cucumber/src/index.js +11 -0
  35. package/packages/datadog-plugin-cypress/src/after-run.js +5 -1
  36. package/packages/datadog-plugin-cypress/src/after-spec.js +5 -1
  37. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +81 -29
  38. package/packages/datadog-plugin-cypress/src/finalization.js +76 -0
  39. package/packages/datadog-plugin-cypress/src/index.js +12 -6
  40. package/packages/datadog-plugin-cypress/src/plugin.js +16 -3
  41. package/packages/datadog-plugin-dd-trace-api/src/index.js +4 -0
  42. package/packages/datadog-plugin-electron/src/index.js +2 -0
  43. package/packages/datadog-plugin-fs/src/index.js +1 -1
  44. package/packages/datadog-plugin-jest/src/index.js +6 -0
  45. package/packages/datadog-plugin-mocha/src/index.js +56 -1
  46. package/packages/datadog-plugin-nats/src/index.js +1 -1
  47. package/packages/datadog-plugin-openai/src/tracing.js +41 -30
  48. package/packages/datadog-plugin-openai-agents/src/integration.js +1 -0
  49. package/packages/datadog-plugin-playwright/src/index.js +42 -13
  50. package/packages/datadog-plugin-vitest/src/index.js +6 -0
  51. package/packages/datadog-webpack/index.js +0 -11
  52. package/packages/dd-trace/index.js +0 -1
  53. package/packages/dd-trace/src/agent/info.js +6 -5
  54. package/packages/dd-trace/src/aiguard/client.js +100 -1
  55. package/packages/dd-trace/src/aiguard/errors.js +41 -0
  56. package/packages/dd-trace/src/aiguard/evaluation.js +357 -0
  57. package/packages/dd-trace/src/aiguard/integrations/anthropic.js +70 -0
  58. package/packages/dd-trace/src/aiguard/integrations/index.js +4 -0
  59. package/packages/dd-trace/src/aiguard/messages/anthropic.js +423 -0
  60. package/packages/dd-trace/src/aiguard/messages/openai.js +1 -24
  61. package/packages/dd-trace/src/aiguard/messages/utils.js +28 -0
  62. package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +1 -11
  63. package/packages/dd-trace/src/aiguard/sdk.js +22 -278
  64. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +9 -12
  65. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +1 -0
  66. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +27 -3
  67. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +22 -6
  68. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +21 -5
  69. package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +164 -0
  70. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +22 -6
  71. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +365 -32
  72. package/packages/dd-trace/src/ci-visibility/exporters/request.js +201 -0
  73. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +5 -3
  74. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +9 -5
  75. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -1
  76. package/packages/dd-trace/src/ci-visibility/requests/rate-limit.js +39 -0
  77. package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -6
  78. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +6 -2
  79. package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -0
  80. package/packages/dd-trace/src/config/index.js +13 -2
  81. package/packages/dd-trace/src/config/major-overrides.js +7 -0
  82. package/packages/dd-trace/src/config/supported-configurations.json +14 -0
  83. package/packages/dd-trace/src/datastreams/index.js +2 -0
  84. package/packages/dd-trace/src/datastreams/manager.js +9 -2
  85. package/packages/dd-trace/src/datastreams/processor.js +15 -13
  86. package/packages/dd-trace/src/datastreams/size.js +5 -0
  87. package/packages/dd-trace/src/debugger/devtools_client/index.js +3 -2
  88. package/packages/dd-trace/src/exporters/agent/writer.js +49 -9
  89. package/packages/dd-trace/src/exporters/common/buffering-exporter.js +6 -4
  90. package/packages/dd-trace/src/exporters/common/writer.js +11 -6
  91. package/packages/dd-trace/src/llmobs/experiments/client.js +163 -5
  92. package/packages/dd-trace/src/llmobs/experiments/dataset.js +241 -87
  93. package/packages/dd-trace/src/llmobs/experiments/experiment.js +280 -20
  94. package/packages/dd-trace/src/llmobs/experiments/index.js +58 -59
  95. package/packages/dd-trace/src/llmobs/experiments/noop.js +71 -3
  96. package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
  97. package/packages/dd-trace/src/llmobs/experiments/util.js +41 -5
  98. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +12 -3
  99. package/packages/dd-trace/src/llmobs/plugins/base.js +4 -2
  100. package/packages/dd-trace/src/noop/proxy.js +3 -4
  101. package/packages/dd-trace/src/openfeature/flagging_provider.js +18 -2
  102. package/packages/dd-trace/src/openfeature/remote_config.js +1 -1
  103. package/packages/dd-trace/src/opentelemetry/context_manager.js +7 -7
  104. package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +2 -1
  105. package/packages/dd-trace/src/opentelemetry/logs/logger.js +67 -11
  106. package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +14 -3
  107. package/packages/dd-trace/src/opentelemetry/metrics/index.js +25 -12
  108. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +2 -5
  109. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +96 -43
  110. package/packages/dd-trace/src/opentracing/tracer.js +5 -2
  111. package/packages/dd-trace/src/otel-thread-ctx.js +387 -0
  112. package/packages/dd-trace/src/plugin_manager.js +3 -3
  113. package/packages/dd-trace/src/plugins/ci_plugin.js +6 -1
  114. package/packages/dd-trace/src/plugins/index.js +1 -0
  115. package/packages/dd-trace/src/plugins/util/jest.js +4 -3
  116. package/packages/dd-trace/src/plugins/util/test.js +9 -9
  117. package/packages/dd-trace/src/plugins/util/web.js +3 -0
  118. package/packages/dd-trace/src/profiling/profiler.js +1 -1
  119. package/packages/dd-trace/src/profiling/profilers/space.js +1 -3
  120. package/packages/dd-trace/src/profiling/profilers/wall.js +37 -90
  121. package/packages/dd-trace/src/profiling/webspan-utils.js +45 -0
  122. package/packages/dd-trace/src/proxy.js +38 -29
  123. package/packages/dd-trace/src/ritm.js +9 -2
  124. package/packages/dd-trace/src/serverless.js +37 -0
  125. package/packages/dd-trace/src/span_format.js +1 -0
  126. package/packages/dd-trace/src/span_stats.js +30 -3
  127. package/packages/dd-trace/src/storage-channels.js +86 -0
  128. package/packages/dd-trace/src/tracer.js +9 -2
  129. package/packages/dd-trace/src/tracer_metadata.js +14 -1
  130. package/packages/dd-trace/src/web-tags-cache.js +132 -0
  131. package/vendor/dist/@datadog/openfeature-node-server/LICENSE +201 -0
  132. package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -0
  133. package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +0 -65
  134. package/packages/datadog-webpack/src/optional-peer-loader.js +0 -17
  135. package/packages/dd-trace/src/feature-registry.js +0 -29
  136. package/packages/dd-trace/src/openfeature/register.js +0 -35
  137. package/packages/dd-trace/src/openfeature/require-provider.js +0 -18
@@ -79,6 +79,9 @@ function isPlaywrightFailureScreenshot (attachment) {
79
79
  class PlaywrightPlugin extends CiPlugin {
80
80
  static id = 'playwright'
81
81
 
82
+ #isFinalizingAfterError = false
83
+ #pendingTestFinishCallbacks = new Map()
84
+
82
85
  constructor (...args) {
83
86
  super(...args)
84
87
 
@@ -99,6 +102,7 @@ class PlaywrightPlugin extends CiPlugin {
99
102
  })
100
103
 
101
104
  this.addSub('ci:playwright:session:start', ({ isFailureScreenshotEnabled }) => {
105
+ this.#isFinalizingAfterError = false
102
106
  if (!getConfig().testOptimization.DD_TEST_FAILURE_SCREENSHOTS_ENABLED) return
103
107
 
104
108
  if (!isFailureScreenshotEnabled) {
@@ -127,8 +131,21 @@ class PlaywrightPlugin extends CiPlugin {
127
131
  isEarlyFlakeDetectionEnabled,
128
132
  isEarlyFlakeDetectionFaulty,
129
133
  isTestManagementTestsEnabled,
134
+ error,
130
135
  onDone,
131
136
  }) => {
137
+ if (error) {
138
+ this.#isFinalizingAfterError = true
139
+ for (const testSuiteSpan of this._testSuiteSpansByTestSuiteAbsolutePath.values()) {
140
+ testSuiteSpan.setTag(TEST_STATUS, 'fail')
141
+ testSuiteSpan.setTag('error', error)
142
+ }
143
+ for (const [finishTest, abortController] of this.#pendingTestFinishCallbacks) {
144
+ finishTest(SCREENSHOT_UPLOAD_RESULT_ERROR)
145
+ abortController.abort()
146
+ }
147
+ }
148
+
132
149
  const finishSession = () => {
133
150
  this.testModuleSpan.setTag(TEST_STATUS, status)
134
151
  this.testSessionSpan.setTag(TEST_STATUS, status)
@@ -139,20 +156,24 @@ class PlaywrightPlugin extends CiPlugin {
139
156
  if (isEarlyFlakeDetectionFaulty) {
140
157
  this.testSessionSpan.setTag(TEST_EARLY_FLAKE_ABORT_REASON, 'faulty')
141
158
  }
142
- if (status === 'fail' && this.numFailedSuites > 0) {
159
+ let sessionError = error
160
+ if (!sessionError && status === 'fail' && this.numFailedSuites > 0) {
143
161
  let errorMessage = `Test suites failed: ${this.numFailedSuites}.`
144
162
  if (this.numFailedTests > 0) {
145
163
  errorMessage += ` Tests failed: ${this.numFailedTests}`
146
164
  }
147
- const error = new Error(errorMessage)
148
- this.testModuleSpan.setTag('error', error)
149
- this.testSessionSpan.setTag('error', error)
165
+ sessionError = new Error(errorMessage)
166
+ }
167
+ if (sessionError) {
168
+ this.testModuleSpan.setTag('error', sessionError)
169
+ this.testSessionSpan.setTag('error', sessionError)
150
170
  }
151
171
 
152
172
  if (isTestManagementTestsEnabled) {
153
173
  this.testSessionSpan.setTag(TEST_MANAGEMENT_ENABLED, 'true')
154
174
  }
155
175
 
176
+ this.tracer._exporter.exportDeferredTestSuiteSpans?.()
156
177
  this.testModuleSpan.finish()
157
178
  this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module')
158
179
  this.testSessionSpan.finish()
@@ -234,6 +255,7 @@ class PlaywrightPlugin extends CiPlugin {
234
255
  this.numFailedSuites++
235
256
  }
236
257
 
258
+ this.tracer._exporter.deferTestSuiteSpan?.(testSuiteSpan)
237
259
  testSuiteSpan.finish()
238
260
  this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'suite')
239
261
  })
@@ -304,16 +326,16 @@ class PlaywrightPlugin extends CiPlugin {
304
326
  }
305
327
  }
306
328
 
307
- if (!formattedTestSpan || !screenshots) {
329
+ if (!formattedTestSpan || !screenshots || this.#isFinalizingAfterError) {
308
330
  exportTraces()
309
331
  return
310
332
  }
311
333
 
312
334
  this.pendingTestFinishes++
313
- const uploadStarted = this.uploadTestScreenshots({
314
- screenshots,
315
- traceId: formattedTestSpan.trace_id.toString(10),
316
- }, (screenshotUploadResult) => {
335
+ const abortController = new AbortController()
336
+ const finishTest = (screenshotUploadResult) => {
337
+ if (!this.#pendingTestFinishCallbacks.delete(finishTest)) return
338
+
317
339
  const screenshotUploadTag = getScreenshotUploadTag(screenshotUploadResult)
318
340
  if (screenshotUploadTag) {
319
341
  formattedTestSpan.meta[screenshotUploadTag] = 'true'
@@ -323,11 +345,16 @@ class PlaywrightPlugin extends CiPlugin {
323
345
  if (this.pendingTestFinishes === 0 && this.finishSession) {
324
346
  this.finishSession()
325
347
  }
326
- })
348
+ }
349
+ this.#pendingTestFinishCallbacks.set(finishTest, abortController)
350
+ const uploadStarted = this.uploadTestScreenshots({
351
+ screenshots,
352
+ traceId: formattedTestSpan.trace_id.toString(10),
353
+ signal: abortController.signal,
354
+ }, finishTest)
327
355
  if (uploadStarted) return
328
356
 
329
- this.pendingTestFinishes--
330
- exportTraces()
357
+ finishTest()
331
358
  })
332
359
 
333
360
  this.addBind('ci:playwright:test:start', (ctx) => {
@@ -552,10 +579,11 @@ class PlaywrightPlugin extends CiPlugin {
552
579
  * @param {object} options - Upload options
553
580
  * @param {Array<object>} options.screenshots - Playwright test attachments
554
581
  * @param {string} options.traceId - Test trace id used as the screenshot key
582
+ * @param {AbortSignal} options.signal - Signal used to cancel uploads during error finalization
555
583
  * @param {(result: string|undefined) => void} onDone - Completion callback
556
584
  * @returns {boolean} Whether at least one upload was started
557
585
  */
558
- uploadTestScreenshots ({ screenshots, traceId }, onDone) {
586
+ uploadTestScreenshots ({ screenshots, traceId, signal }, onDone) {
559
587
  const exporter = this.tracer?._exporter
560
588
  if (!Array.isArray(screenshots) || !screenshots.length ||
561
589
  !exporter?.canUploadTestScreenshots?.() ||
@@ -581,6 +609,7 @@ class PlaywrightPlugin extends CiPlugin {
581
609
  traceId,
582
610
  idempotencyKey: `${traceId}:${basename(filePath)}`,
583
611
  capturedAtMs: getScreenshotCapturedAtMs(filePath, filePath),
612
+ signal,
584
613
  }, (error, uploaded = true) => {
585
614
  if (uploaded) {
586
615
  uploadResults[resultIndex] = error
@@ -523,6 +523,7 @@ class VitestPlugin extends CiPlugin {
523
523
  this.telemetry.ciVisEvent(TELEMETRY_CODE_COVERAGE_FINISHED, 'suite', { library: coverageLibrary })
524
524
  this.telemetry.distribution(TELEMETRY_CODE_COVERAGE_NUM_FILES, {}, relativeFiles.length)
525
525
  }
526
+ this.tracer._exporter.deferTestSuiteSpan?.(testSuiteSpan)
526
527
  testSuiteSpan.finish()
527
528
  finishAllTraceSpans(testSuiteSpan)
528
529
  }
@@ -566,6 +567,7 @@ class VitestPlugin extends CiPlugin {
566
567
  this.addSub('ci:vitest:session:finish', ({
567
568
  status,
568
569
  error,
570
+ isTestSessionFinalizationError,
569
571
  testCodeCoverageLinesTotal,
570
572
  isEarlyFlakeDetectionEnabled,
571
573
  isEarlyFlakeDetectionFaulty,
@@ -588,6 +590,9 @@ class VitestPlugin extends CiPlugin {
588
590
  this.testSessionSpan.setTag(TEST_STATUS, status)
589
591
  this.testModuleSpan.setTag(TEST_STATUS, status)
590
592
  if (error) {
593
+ if (isTestSessionFinalizationError) {
594
+ this.tracer._exporter.setDeferredTestSuiteError?.(error)
595
+ }
591
596
  this.testModuleSpan.setTag('error', error)
592
597
  this.testSessionSpan.setTag('error', error)
593
598
  }
@@ -617,6 +622,7 @@ class VitestPlugin extends CiPlugin {
617
622
  if (vitestPool) {
618
623
  this.testSessionSpan.setTag(VITEST_POOL, vitestPool)
619
624
  }
625
+ this.tracer._exporter.exportDeferredTestSuiteSpans?.()
620
626
  this.testModuleSpan.finish()
621
627
  this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module')
622
628
  this.testSessionSpan.finish()
@@ -6,7 +6,6 @@ const fs = require('node:fs')
6
6
  const instrumentations = require('../datadog-instrumentations/src/helpers/instrumentations')
7
7
  const extractPackageAndModulePath = require('../datadog-instrumentations/src/helpers/extract-package-and-module-path')
8
8
  const hooks = require('../datadog-instrumentations/src/helpers/hooks')
9
- const { matchesOptionalPeerFile } = require('../datadog-instrumentations/src/helpers/optional-peer-bundler')
10
9
  const { isESMFile } = require('../datadog-esbuild/src/utils')
11
10
  const log = require('./src/log')
12
11
 
@@ -137,16 +136,6 @@ class DatadogWebpackPlugin {
137
136
 
138
137
  const normalizedResource = resource.replaceAll('\\', '/')
139
138
 
140
- // Rewrite optional-peer loads so installed peers get bundled and survive relocation
141
- // (#8980); absent peers stay opaque, so a build that does not opt into the feature does
142
- // not follow their dependency chain (#8635).
143
- if (matchesOptionalPeerFile(normalizedResource)) {
144
- createData.loaders ||= []
145
- createData.loaders.push({ loader: require.resolve('./src/optional-peer-loader') })
146
- log.debug('INLINE: optional-peer loader applied to %s', normalizedResource)
147
- return
148
- }
149
-
150
139
  if (!resource.includes('node_modules')) {
151
140
  return
152
141
  }
@@ -1,4 +1,3 @@
1
1
  'use strict'
2
2
 
3
- require('./src/openfeature/register')
4
3
  module.exports = require('./src/bootstrap')
@@ -17,8 +17,10 @@ module.exports = {
17
17
  * Fetches agent information from the /info endpoint
18
18
  * @param {URL} url - The agent URL
19
19
  * @param {Function} callback - Callback function with signature (err, agentInfo)
20
+ * @param {{ deadline?: number, signal?: AbortSignal }} [options] - Request finalization options
21
+ * @param {Function} [makeRequest] - Request implementation
20
22
  */
21
- function fetchAgentInfo (url, callback) {
23
+ function fetchAgentInfo (url, callback, options = {}, makeRequest = request) {
22
24
  const urlKey = url.href
23
25
 
24
26
  if (cachedUrl !== null && cachedUrl !== urlKey) {
@@ -29,10 +31,9 @@ function fetchAgentInfo (url, callback) {
29
31
  return process.nextTick(callback, null, cachedData)
30
32
  }
31
33
 
32
- request('', {
33
- path: '/info',
34
- url,
35
- }, (err, res) => {
34
+ options.path = '/info'
35
+ options.url = url
36
+ makeRequest('', options, (err, res) => {
36
37
  if (err) {
37
38
  return callback(err)
38
39
  }
@@ -1,5 +1,27 @@
1
1
  'use strict'
2
2
 
3
+ const tracerVersion = require('../../../../package.json').version
4
+ const { AIGuardClientError } = require('./errors')
5
+ const { parseEvaluationResponse } = require('./evaluation')
6
+ const TAGS = require('./tags')
7
+
8
+ /**
9
+ * Resolves the AI Guard host for a Datadog site.
10
+ *
11
+ * @param {string} site
12
+ * @returns {string}
13
+ */
14
+ function aiGuardHost (site) {
15
+ return site.split('.').length === 2 ? `app.${site}` : site
16
+ }
17
+
18
+ /**
19
+ * Sends a request to the AI Guard service.
20
+ *
21
+ * @param {object} body
22
+ * @param {{ url: string, headers: Record<string, string>, timeout: number }} opts
23
+ * @returns {Promise<{ status: number, body: unknown }>}
24
+ */
3
25
  async function executeRequest (body, opts) {
4
26
  const postData = JSON.stringify(body)
5
27
  const headers = {
@@ -22,4 +44,81 @@ async function executeRequest (body, opts) {
22
44
  }
23
45
  }
24
46
 
25
- module.exports = executeRequest
47
+ class AIGuardClient {
48
+ #headers
49
+ #evaluateUrl
50
+ #timeout
51
+
52
+ /**
53
+ * @param {import('../config/config-base')} config
54
+ */
55
+ constructor (config) {
56
+ this.#headers = {
57
+ 'DD-API-KEY': config.DD_API_KEY,
58
+ 'DD-APPLICATION-KEY': config.DD_APP_KEY,
59
+ 'DD-AI-GUARD-VERSION': tracerVersion,
60
+ 'DD-AI-GUARD-SOURCE': 'SDK',
61
+ 'DD-AI-GUARD-LANGUAGE': 'nodejs',
62
+ }
63
+ const endpoint = config.experimental.aiguard.endpoint || `https://${aiGuardHost(config.site)}/api/v2/ai-guard`
64
+ this.#evaluateUrl = `${endpoint}/evaluate`
65
+ this.#timeout = config.experimental.aiguard.timeout
66
+ }
67
+
68
+ /**
69
+ * Evaluates messages and converts the service response to the internal evaluation contract.
70
+ *
71
+ * @param {import('../../../../index').aiguard.Message[]} messages
72
+ * @param {{ service: string, env: string }} meta
73
+ * @returns {Promise<NonNullable<ReturnType<typeof parseEvaluationResponse>>>}
74
+ */
75
+ evaluate (messages, meta) {
76
+ const payload = {
77
+ data: {
78
+ attributes: {
79
+ messages,
80
+ meta,
81
+ },
82
+ },
83
+ }
84
+ return executeRequest(
85
+ payload,
86
+ { url: this.#evaluateUrl, headers: this.#headers, timeout: this.#timeout }
87
+ )
88
+ .then(response => this.#parseResponse(response))
89
+ .catch(cause => {
90
+ if (cause instanceof AIGuardClientError) throw cause
91
+
92
+ throw new AIGuardClientError(`Unexpected error calling AI Guard service: ${cause.message}`, {
93
+ cause,
94
+ telemetryType: TAGS.ERROR_TYPE_CLIENT,
95
+ })
96
+ })
97
+ }
98
+
99
+ /**
100
+ * Validates an AI Guard HTTP response.
101
+ *
102
+ * @param {{ status: number, body: unknown }} response
103
+ * @returns {NonNullable<ReturnType<typeof parseEvaluationResponse>>}
104
+ */
105
+ #parseResponse (response) {
106
+ if (response.status !== 200) {
107
+ throw new AIGuardClientError(`AI Guard service call failed, status ${response.status}`, {
108
+ errors: response.body?.errors,
109
+ telemetryType: TAGS.ERROR_TYPE_STATUS,
110
+ })
111
+ }
112
+
113
+ const evaluation = parseEvaluationResponse(response.body)
114
+ if (!evaluation) {
115
+ throw new AIGuardClientError(`AI Guard service returned unexpected response : ${response.body}`, {
116
+ telemetryType: TAGS.ERROR_TYPE_RESPONSE,
117
+ })
118
+ }
119
+
120
+ return evaluation
121
+ }
122
+ }
123
+
124
+ module.exports = AIGuardClient
@@ -0,0 +1,41 @@
1
+ 'use strict'
2
+
3
+ class AIGuardAbortError extends Error {
4
+ /**
5
+ * @param {string|undefined} reason
6
+ * @param {Array<unknown>} tags
7
+ * @param {Record<string, unknown>} tagProbabilities
8
+ * @param {Array<unknown>} sds
9
+ */
10
+ constructor (reason, tags, tagProbabilities, sds) {
11
+ super(reason)
12
+ this.name = 'AIGuardAbortError'
13
+ this.reason = reason
14
+ this.tags = tags
15
+ this.tagProbabilities = tagProbabilities
16
+ this.sds = sds || []
17
+ }
18
+ }
19
+
20
+ class AIGuardClientError extends Error {
21
+ /**
22
+ * @param {string} message
23
+ * @param {{ telemetryType: string, errors?: Array<unknown>, cause?: Error }} opts
24
+ */
25
+ constructor (message, opts) {
26
+ super(message)
27
+ this.name = 'AIGuardClientError'
28
+ this.telemetryType = opts.telemetryType
29
+ if (opts.errors) {
30
+ this.errors = opts.errors
31
+ }
32
+ if (opts.cause) {
33
+ this.cause = opts.cause
34
+ }
35
+ }
36
+ }
37
+
38
+ module.exports = {
39
+ AIGuardAbortError,
40
+ AIGuardClientError,
41
+ }