dd-trace 5.94.0 → 5.96.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 (52) hide show
  1. package/LICENSE-3rdparty.csv +46 -44
  2. package/index.d.ts +191 -13
  3. package/package.json +2 -2
  4. package/packages/datadog-instrumentations/src/ai.js +112 -0
  5. package/packages/datadog-instrumentations/src/anthropic.js +1 -1
  6. package/packages/datadog-instrumentations/src/helpers/ai-messages.js +182 -0
  7. package/packages/datadog-instrumentations/src/helpers/rewriter/{orchestrion/compiler.js → compiler.js} +4 -13
  8. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +16 -2
  9. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +25 -0
  10. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/langgraph.js +2 -2
  11. package/packages/datadog-instrumentations/src/helpers/rewriter/{orchestrion/transforms.js → transforms.js} +3 -89
  12. package/packages/datadog-instrumentations/src/mocha/utils.js +10 -0
  13. package/packages/datadog-plugin-dd-trace-api/src/index.js +1 -4
  14. package/packages/dd-trace/src/aiguard/index.js +64 -0
  15. package/packages/dd-trace/src/azure_metadata.js +15 -15
  16. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +73 -1
  17. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +76 -1
  18. package/packages/dd-trace/src/ci-visibility/lage.js +39 -0
  19. package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +259 -0
  20. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +56 -0
  21. package/packages/dd-trace/src/config/config-base.d.ts +7 -0
  22. package/packages/dd-trace/src/config/config-base.js +5 -0
  23. package/packages/dd-trace/src/config/config-types.d.ts +78 -0
  24. package/packages/dd-trace/src/config/generated-config-types.d.ts +582 -0
  25. package/packages/dd-trace/src/config/index.js +5 -2
  26. package/packages/dd-trace/src/config/supported-configurations.json +24 -0
  27. package/packages/dd-trace/src/constants.js +1 -0
  28. package/packages/dd-trace/src/exporter.js +5 -2
  29. package/packages/dd-trace/src/llmobs/constants/tags.js +1 -0
  30. package/packages/dd-trace/src/llmobs/constants/text.js +4 -1
  31. package/packages/dd-trace/src/llmobs/constants/writers.js +1 -1
  32. package/packages/dd-trace/src/llmobs/index.js +9 -4
  33. package/packages/dd-trace/src/llmobs/plugins/anthropic.js +11 -2
  34. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +4 -1
  35. package/packages/dd-trace/src/llmobs/writers/spans.js +1 -1
  36. package/packages/dd-trace/src/plugins/util/test.js +5 -0
  37. package/packages/dd-trace/src/priority_sampler.js +1 -1
  38. package/packages/dd-trace/src/proxy.js +4 -0
  39. package/packages/dd-trace/src/rate_limiter.js +2 -1
  40. package/packages/dd-trace/src/startup-log.js +9 -0
  41. package/packages/dd-trace/src/tagger.js +31 -35
  42. package/vendor/dist/@apm-js-collab/code-transformer/LICENSE +28 -0
  43. package/vendor/dist/@apm-js-collab/code-transformer/index.js +133 -0
  44. package/vendor/dist/@opentelemetry/core/index.js +1 -1
  45. package/vendor/dist/@opentelemetry/resources/index.js +1 -1
  46. package/vendor/dist/esquery/index.js +1 -1
  47. package/vendor/dist/meriyah/index.js +1 -1
  48. package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/index.js +0 -43
  49. package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/matcher.js +0 -49
  50. package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/transformer.js +0 -121
  51. package/vendor/dist/astring/LICENSE +0 -19
  52. package/vendor/dist/astring/index.js +0 -1
@@ -0,0 +1,182 @@
1
+ 'use strict'
2
+
3
+ /**
4
+ * Converts a LanguageModelV2FilePart with an image mediaType to an AI guard style image_url content part.
5
+ *
6
+ * @param {{type: 'file', data: URL|string|Uint8Array, mediaType: string}} part
7
+ * @returns {{type: 'image_url', image_url: {url: string}}|undefined}
8
+ */
9
+ function convertFilePartToImageUrl (part) {
10
+ const { data, mediaType } = part
11
+
12
+ if (data instanceof URL) {
13
+ return { type: 'image_url', image_url: { url: data.toString() } }
14
+ }
15
+
16
+ if (typeof data === 'string') {
17
+ if (data.startsWith('http') || data.startsWith('data:')) {
18
+ return { type: 'image_url', image_url: { url: data } }
19
+ }
20
+ return { type: 'image_url', image_url: { url: `data:${mediaType};base64,${data}` } }
21
+ }
22
+
23
+ if (data instanceof Uint8Array) {
24
+ return { type: 'image_url', image_url: { url: `data:${mediaType};base64,${Buffer.from(data).toString('base64')}` } }
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Converts a LanguageModelV2Prompt to the AI guard style message format.
30
+ *
31
+ * Vercel AI v2 prompt entries use content arrays with typed parts (e.g. { type: 'text', text },
32
+ * { type: 'file', data, mediaType }). This function converts them to AI guard style messages.
33
+ * When file parts with image media types are present, the content is an array of text and
34
+ * image_url parts; otherwise it is a plain string.
35
+ *
36
+ * @param {Array<{role: string, content: string|Array<{type: string}>}>} prompt
37
+ * @returns {Array<{role: string, content?: string|Array<{type: string}>, tool_calls?: Array, tool_call_id?: string}>}
38
+ */
39
+ function convertVercelPromptToMessages (prompt) {
40
+ if (!Array.isArray(prompt)) return []
41
+
42
+ const messages = []
43
+ for (const msg of prompt) {
44
+ switch (msg.role) {
45
+ case 'system':
46
+ messages.push({ role: 'system', content: typeof msg.content === 'string' ? msg.content : '' })
47
+ break
48
+
49
+ case 'user': {
50
+ if (!Array.isArray(msg.content)) break
51
+
52
+ const contentParts = []
53
+ for (const part of msg.content) {
54
+ if (part.type === 'text') {
55
+ contentParts.push({ type: 'text', text: part.text })
56
+ } else if (part.type === 'file' && part.mediaType?.startsWith('image/')) {
57
+ const converted = convertFilePartToImageUrl(part)
58
+ if (converted) contentParts.push(converted)
59
+ }
60
+ }
61
+
62
+ if (contentParts.length === 0) break
63
+
64
+ const hasImages = contentParts.some(p => p.type === 'image_url')
65
+ if (hasImages) {
66
+ messages.push({ role: 'user', content: contentParts })
67
+ } else {
68
+ messages.push({ role: 'user', content: contentParts.map(p => p.text).join('\n') })
69
+ }
70
+ break
71
+ }
72
+
73
+ case 'assistant': {
74
+ const textParts = []
75
+ const toolCalls = []
76
+ if (!Array.isArray(msg.content)) break
77
+
78
+ for (const part of msg.content) {
79
+ if (part.type === 'text') {
80
+ textParts.push(part.text)
81
+ } else if (part.type === 'tool-call') {
82
+ const args = part.args ?? part.input
83
+ toolCalls.push({
84
+ id: part.toolCallId,
85
+ function: {
86
+ name: part.toolName,
87
+ arguments: typeof args === 'string' ? args : JSON.stringify(args),
88
+ },
89
+ })
90
+ }
91
+ }
92
+
93
+ if (toolCalls.length > 0) {
94
+ messages.push({ role: 'assistant', tool_calls: toolCalls })
95
+ } else if (textParts.length > 0) {
96
+ messages.push({ role: 'assistant', content: textParts.join('\n') })
97
+ }
98
+ break
99
+ }
100
+
101
+ case 'tool': {
102
+ if (!Array.isArray(msg.content)) break
103
+
104
+ for (const part of msg.content) {
105
+ if (part.type === 'tool-result') {
106
+ const result = part.result ?? part.output
107
+ messages.push({
108
+ role: 'tool',
109
+ tool_call_id: part.toolCallId,
110
+ content: typeof result === 'string' ? result : JSON.stringify(result),
111
+ })
112
+ }
113
+ }
114
+ break
115
+ }
116
+ }
117
+ }
118
+ return messages
119
+ }
120
+
121
+ /**
122
+ * Converts LLM output tool calls to AI guard style message format.
123
+ *
124
+ * @param {Array<object>} inputMessages - The input messages already in AI guard style format
125
+ * @param {Array<{toolCallId: string, toolName: string, args?: unknown, input?: unknown}>} toolCalls
126
+ * @returns {Array<object>}
127
+ */
128
+ function buildToolCallOutputMessages (inputMessages, toolCalls) {
129
+ return [
130
+ ...inputMessages,
131
+ {
132
+ role: 'assistant',
133
+ tool_calls: toolCalls.map(tc => {
134
+ const args = tc.args ?? tc.input
135
+ return {
136
+ id: tc.toolCallId,
137
+ function: {
138
+ name: tc.toolName,
139
+ arguments: typeof args === 'string' ? args : JSON.stringify(args),
140
+ },
141
+ }
142
+ }),
143
+ },
144
+ ]
145
+ }
146
+
147
+ /**
148
+ * Builds OpenAI-style output messages for the assistant's text response.
149
+ *
150
+ * @param {Array<object>} inputMessages - The input messages already in AI guard style format
151
+ * @param {string} text - The assistant's text response
152
+ * @returns {Array<object>}
153
+ */
154
+ function buildTextOutputMessages (inputMessages, text) {
155
+ return [
156
+ ...inputMessages,
157
+ { role: 'assistant', content: text },
158
+ ]
159
+ }
160
+
161
+ /**
162
+ * Parses a Vercel AI content array and dispatches to the appropriate output message builder.
163
+ *
164
+ * @param {Array<object>} inputMessages - The input messages already in AI guard style format
165
+ * @param {Array<{type: string}>} content - Vercel AI content array from doGenerate/doStream result
166
+ * @returns {Array<object>}
167
+ */
168
+ function buildOutputMessages (inputMessages, content) {
169
+ const toolCalls = content.filter(c => c.type === 'tool-call')
170
+ const text = content.filter(c => c.type === 'text').map(c => c.text).join('\n')
171
+ if (toolCalls.length) return buildToolCallOutputMessages(inputMessages, toolCalls)
172
+ if (text) return buildTextOutputMessages(inputMessages, text)
173
+ return inputMessages
174
+ }
175
+
176
+ module.exports = {
177
+ convertVercelPromptToMessages,
178
+ convertFilePartToImageUrl,
179
+ buildToolCallOutputMessages,
180
+ buildTextOutputMessages,
181
+ buildOutputMessages,
182
+ }
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const log = require('../../../../../dd-trace/src/log')
3
+ const log = require('../../../../dd-trace/src/log')
4
4
 
5
5
  // eslint-disable-next-line camelcase, no-undef
6
6
  const runtimeRequire = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
@@ -25,7 +25,7 @@ const compiler = {
25
25
  log.error(e)
26
26
 
27
27
  // Fallback for when OXC is not available.
28
- const meriyah = require('../../../../../../vendor/dist/meriyah')
28
+ const meriyah = require('../../../../../vendor/dist/meriyah')
29
29
 
30
30
  compiler.parse = (sourceText, { range, sourceType } = {}) => {
31
31
  return meriyah.parse(sourceText.toString(), {
@@ -39,16 +39,8 @@ const compiler = {
39
39
  return compiler.parse(sourceText, options)
40
40
  },
41
41
 
42
- generate: (...args) => {
43
- const astring = require('../../../../../../vendor/dist/astring')
44
-
45
- compiler.generate = astring.generate
46
-
47
- return compiler.generate(...args)
48
- },
49
-
50
42
  traverse: (ast, query, visitor) => {
51
- const esquery = require('../../../../../../vendor/dist/esquery').default
43
+ const esquery = require('../../../../../vendor/dist/esquery')
52
44
 
53
45
  compiler.traverse = (ast, query, visitor) => {
54
46
  return esquery.traverse(ast, esquery.parse(query), visitor)
@@ -58,7 +50,7 @@ const compiler = {
58
50
  },
59
51
 
60
52
  query: (ast, query) => {
61
- const esquery = require('../../../../../../vendor/dist/esquery').default
53
+ const esquery = require('../../../../../vendor/dist/esquery')
62
54
 
63
55
  compiler.query = esquery.query
64
56
 
@@ -68,7 +60,6 @@ const compiler = {
68
60
 
69
61
  module.exports = {
70
62
  parse: (...args) => compiler.parse(...args),
71
- generate: (...args) => compiler.generate(...args),
72
63
  traverse: (...args) => compiler.traverse(...args),
73
64
  query: (...args) => compiler.query(...args),
74
65
  }
@@ -3,13 +3,27 @@
3
3
  const { readFileSync } = require('fs')
4
4
  const { join } = require('path')
5
5
  const log = require('../../../../dd-trace/src/log')
6
+ const { create } = require('../../../../../vendor/dist/@apm-js-collab/code-transformer')
7
+ const { traceAsyncIterator, traceIterator } = require('./transforms')
6
8
  const instrumentations = require('./instrumentations')
7
- const { create } = require('./orchestrion')
9
+
10
+ let dcPolyfill
11
+
12
+ try {
13
+ dcPolyfill = require.resolve('dc-polyfill').replaceAll('\\', '/')
14
+ } catch {
15
+ // The `dc-polyfill` module is unavailable for some reason (like bundling).
16
+ // Let's just keep the default of using `diagnostics-channel` as a fallback
17
+ // which works for most Node versions.
18
+ }
8
19
 
9
20
  /** @type {Record<string, string>} map of module base name to version */
10
21
  const moduleVersions = {}
11
22
  const disabled = new Set()
12
- const matcher = create(instrumentations, 'dc-polyfill')
23
+ const matcher = create(instrumentations, dcPolyfill)
24
+
25
+ matcher.addTransform('traceIterator', traceIterator)
26
+ matcher.addTransform('traceAsyncIterator', traceAsyncIterator)
13
27
 
14
28
  function rewrite (content, filename, format) {
15
29
  if (!content) return content
@@ -75,6 +75,31 @@ module.exports = [
75
75
  },
76
76
  channelName: 'selectTelemetryAttributes',
77
77
  },
78
+ // resolveLanguageModel called by all LLM entry points, its result is the resolved model instance.
79
+ {
80
+ module: {
81
+ name: 'ai',
82
+ versionRange: '>=6.0.0',
83
+ filePath: 'dist/index.js',
84
+ },
85
+ functionQuery: {
86
+ functionName: 'resolveLanguageModel',
87
+ kind: 'Sync',
88
+ },
89
+ channelName: 'resolveLanguageModel',
90
+ },
91
+ {
92
+ module: {
93
+ name: 'ai',
94
+ versionRange: '>=6.0.0',
95
+ filePath: 'dist/index.mjs',
96
+ },
97
+ functionQuery: {
98
+ functionName: 'resolveLanguageModel',
99
+ kind: 'Sync',
100
+ },
101
+ channelName: 'resolveLanguageModel',
102
+ },
78
103
  // tool
79
104
  {
80
105
  module: {
@@ -10,9 +10,9 @@ module.exports = [
10
10
  functionQuery: {
11
11
  methodName: 'stream',
12
12
  className: 'Pregel',
13
- kind: 'AsyncIterator',
14
13
  },
15
14
  channelName: 'Pregel_stream',
15
+ transform: 'traceAsyncIterator',
16
16
  },
17
17
  {
18
18
  module: {
@@ -23,8 +23,8 @@ module.exports = [
23
23
  functionQuery: {
24
24
  methodName: 'stream',
25
25
  className: 'Pregel',
26
- kind: 'AsyncIterator',
27
26
  },
28
27
  channelName: 'Pregel_stream',
28
+ transform: 'traceAsyncIterator',
29
29
  },
30
30
  ]
@@ -1,5 +1,7 @@
1
1
  'use strict'
2
2
 
3
+ // TODO: Move traceIterator to Orchestrion.
4
+
3
5
  const { parse, query, traverse } = require('./compiler')
4
6
 
5
7
  const tracingChannelPredicate = (node) => (
@@ -36,10 +38,7 @@ const transforms = module.exports = {
36
38
  },
37
39
 
38
40
  traceAsyncIterator: traceAny,
39
- traceCallback: traceAny,
40
41
  traceIterator: traceAny,
41
- tracePromise: traceAny,
42
- traceSync: traceAny,
43
42
  }
44
43
 
45
44
  function traceAny (state, node, _parent, ancestry) {
@@ -117,33 +116,10 @@ function traceInstanceMethod (state, node, program) {
117
116
  }
118
117
 
119
118
  function wrap (state, node, program) {
120
- const { channelName, operator } = state
119
+ const { operator } = state
121
120
 
122
121
  if (operator === 'traceAsyncIterator') return wrapIterator(state, node, program)
123
- if (operator === 'traceCallback') return wrapCallback(state, node)
124
122
  if (operator === 'traceIterator') return wrapIterator(state, node, program)
125
-
126
- const async = operator === 'tracePromise' ? 'async' : ''
127
- const channelVariable = 'tr_ch_apm$' + channelName.replaceAll(':', '_')
128
- const wrapper = parse(`
129
- function wrapper () {
130
- const __apm$traced = ${async} () => {
131
- const __apm$wrapped = () => {};
132
- return __apm$wrapped.apply(this, arguments);
133
- };
134
- if (!${channelVariable}.hasSubscribers) return __apm$traced();
135
- return ${channelVariable}.${operator}(__apm$traced, {
136
- arguments,
137
- self: this,
138
- moduleVersion: "1.0.0"
139
- });
140
- }
141
- `).body[0].body // Extract only block statement of function body.
142
-
143
- // Replace the right-hand side assignment of `const __apm$wrapped = () => {}`.
144
- query(wrapper, '[id.name=__apm$wrapped]')[0].init = node
145
-
146
- return wrapper
147
123
  }
148
124
 
149
125
  function wrapSuper (_state, node) {
@@ -195,68 +171,6 @@ function wrapSuper (_state, node) {
195
171
  }
196
172
  }
197
173
 
198
- function wrapCallback (state, node) {
199
- const { channelName, functionQuery: { callbackIndex = -1 } } = state
200
- const channelVariable = 'tr_ch_apm$' + channelName.replaceAll(':', '_')
201
- const wrapper = parse(`
202
- function wrapper () {
203
- const __apm$cb = Array.prototype.at.call(arguments, ${callbackIndex});
204
- const __apm$ctx = {
205
- arguments,
206
- self: this,
207
- moduleVersion: "1.0.0"
208
- };
209
- const __apm$traced = () => {
210
- const __apm$wrapped = () => {};
211
- return __apm$wrapped.apply(this, arguments);
212
- };
213
-
214
- if (!${channelVariable}.start.hasSubscribers) return __apm$traced();
215
-
216
- function __apm$wrappedCb(err, res) {
217
- if (err) {
218
- __apm$ctx.error = err;
219
- ${channelVariable}.error.publish(__apm$ctx);
220
- } else {
221
- __apm$ctx.result = res;
222
- }
223
-
224
- ${channelVariable}.asyncStart.runStores(__apm$ctx, () => {
225
- try {
226
- if (__apm$cb) {
227
- return __apm$cb.apply(this, arguments);
228
- }
229
- } finally {
230
- ${channelVariable}.asyncEnd.publish(__apm$ctx);
231
- }
232
- });
233
- }
234
-
235
- if (typeof __apm$cb !== 'function') {
236
- return __apm$traced();
237
- }
238
- Array.prototype.splice.call(arguments, ${callbackIndex}, 1, __apm$wrappedCb);
239
-
240
- return ${channelVariable}.start.runStores(__apm$ctx, () => {
241
- try {
242
- return __apm$traced();
243
- } catch (err) {
244
- __apm$ctx.error = err;
245
- ${channelVariable}.error.publish(__apm$ctx);
246
- throw err;
247
- } finally {
248
- ${channelVariable}.end.publish(__apm$ctx);
249
- }
250
- });
251
- }
252
- `).body[0].body // Extract only block statement of function body.
253
-
254
- // Replace the right-hand side assignment of `const __apm$wrapped = () => {}`.
255
- query(wrapper, '[id.name=__apm$wrapped]')[0].init = node
256
-
257
- return wrapper
258
- }
259
-
260
174
  function wrapIterator (state, node, program) {
261
175
  const { channelName, operator } = state
262
176
  const baseChannel = channelName.replaceAll(':', '_')
@@ -384,6 +384,11 @@ function getOnTestEndHandler (config) {
384
384
  })
385
385
  } else if (ctx) { // if there is an afterEach to run, let's store the finalStatus for getOnHookEndHandler
386
386
  ctx.finalStatus = finalStatus
387
+ ctx.hasFailedAllRetries = hasFailedAllRetries
388
+ ctx.attemptToFixPassed = attemptToFixPassed
389
+ ctx.attemptToFixFailed = attemptToFixFailed
390
+ ctx.isAttemptToFixRetry = isAttemptToFixRetry
391
+ ctx.isAtrRetry = isAtrRetry
387
392
  }
388
393
  }
389
394
  }
@@ -404,6 +409,11 @@ function getOnHookEndHandler () {
404
409
  status,
405
410
  hasBeenRetried: isMochaRetry(test),
406
411
  isLastRetry: getIsLastRetry(test),
412
+ hasFailedAllRetries: ctx.hasFailedAllRetries,
413
+ attemptToFixPassed: ctx.attemptToFixPassed,
414
+ attemptToFixFailed: ctx.attemptToFixFailed,
415
+ isAttemptToFixRetry: ctx.isAttemptToFixRetry,
416
+ isAtrRetry: ctx.isAtrRetry,
407
417
  ...ctx.currentStore,
408
418
  finalStatus: ctx.finalStatus,
409
419
  })
@@ -3,14 +3,10 @@
3
3
  const Plugin = require('../../dd-trace/src/plugins/plugin')
4
4
  const telemetryMetrics = require('../../dd-trace/src/telemetry/metrics')
5
5
  const apiMetrics = telemetryMetrics.manager.namespace('tracers')
6
- const { getValueFromEnvSources } = require('../../dd-trace/src/config/helper')
7
6
 
8
7
  // api ==> here
9
8
  const objectMap = new WeakMap()
10
9
 
11
- const injectionEnabledTag =
12
- `injection_enabled:${getValueFromEnvSources('DD_INJECTION_ENABLED') ? 'yes' : 'no'}`
13
-
14
10
  module.exports = class DdTraceApiPlugin extends Plugin {
15
11
  static id = 'dd-trace-api'
16
12
 
@@ -18,6 +14,7 @@ module.exports = class DdTraceApiPlugin extends Plugin {
18
14
  super(...args)
19
15
 
20
16
  const tracer = this._tracer
17
+ const injectionEnabledTag = `injection_enabled:${this._tracerConfig.injectionEnabled ? 'yes' : 'no'}`
21
18
 
22
19
  this.addSub('datadog-api:v1:tracerinit', ({ proxy }) => {
23
20
  const proxyVal = proxy()
@@ -0,0 +1,64 @@
1
+ 'use strict'
2
+
3
+ const { channel } = require('dc-polyfill')
4
+ const log = require('../log')
5
+ const AIGuard = require('./sdk')
6
+
7
+ const aiguardChannel = channel('dd-trace:ai:aiguard')
8
+
9
+ let isEnabled = false
10
+ let aiguard
11
+ let block
12
+
13
+ function enable (tracer, config) {
14
+ if (isEnabled) return
15
+
16
+ try {
17
+ aiguard = new AIGuard(tracer, config)
18
+ block = config.experimental?.aiguard?.block !== false
19
+
20
+ aiguardChannel.subscribe(onEvaluate)
21
+
22
+ isEnabled = true
23
+ } catch (err) {
24
+ log.error('AIGuard: unexpected error during initialization: %s', err.message)
25
+ disable()
26
+ }
27
+ }
28
+
29
+ function disable () {
30
+ if (!isEnabled) return
31
+
32
+ aiguardChannel.unsubscribe(onEvaluate)
33
+
34
+ aiguard = undefined
35
+ isEnabled = false
36
+ block = false
37
+ }
38
+
39
+ /**
40
+ * Handles channel messages with pre-converted messages.
41
+ *
42
+ * @param {{messages: Array<object>, resolve: Function, reject: Function}} ctx
43
+ */
44
+ function onEvaluate (ctx) {
45
+ if (!ctx.messages?.length) {
46
+ ctx.resolve()
47
+ return
48
+ }
49
+
50
+ aiguard.evaluate(ctx.messages, { block })
51
+ .then(() => {
52
+ ctx.resolve()
53
+ })
54
+ .catch(err => {
55
+ if (err.name === 'AIGuardAbortError') {
56
+ ctx.reject(err)
57
+ } else {
58
+ log.error('AIGuard: unexpected error during evaluation: %s', err.message)
59
+ ctx.resolve()
60
+ }
61
+ })
62
+ }
63
+
64
+ module.exports = { enable, disable }
@@ -5,10 +5,9 @@
5
5
  const os = require('os')
6
6
  const {
7
7
  getEnvironmentVariable,
8
- getEnvironmentVariables,
9
8
  getValueFromEnvSources,
10
9
  } = require('./config/helper')
11
- const { getIsAzureFunction, getIsFlexConsumptionAzureFunction } = require('./serverless')
10
+ const { getIsAzureFunction } = require('./serverless')
12
11
 
13
12
  function extractSubscriptionID (ownerName) {
14
13
  if (ownerName !== undefined) {
@@ -46,32 +45,33 @@ function trimObject (obj) {
46
45
  }
47
46
 
48
47
  function buildMetadata () {
49
- const {
50
- COMPUTERNAME,
51
- FUNCTIONS_EXTENSION_VERSION,
52
- FUNCTIONS_WORKER_RUNTIME,
53
- FUNCTIONS_WORKER_RUNTIME_VERSION,
54
- WEBSITE_INSTANCE_ID,
55
- WEBSITE_OWNER_NAME,
56
- WEBSITE_OS,
57
- WEBSITE_RESOURCE_GROUP,
58
- WEBSITE_SITE_NAME,
59
- } = getEnvironmentVariables()
48
+ const COMPUTERNAME = getEnvironmentVariable('COMPUTERNAME')
49
+ const FUNCTIONS_EXTENSION_VERSION = getEnvironmentVariable('FUNCTIONS_EXTENSION_VERSION')
50
+ const FUNCTIONS_WORKER_RUNTIME = getEnvironmentVariable('FUNCTIONS_WORKER_RUNTIME')
51
+ const FUNCTIONS_WORKER_RUNTIME_VERSION = getEnvironmentVariable('FUNCTIONS_WORKER_RUNTIME_VERSION')
52
+ const WEBSITE_INSTANCE_ID = getEnvironmentVariable('WEBSITE_INSTANCE_ID')
53
+ const WEBSITE_OWNER_NAME = getEnvironmentVariable('WEBSITE_OWNER_NAME')
54
+ const WEBSITE_OS = getEnvironmentVariable('WEBSITE_OS')
55
+ const WEBSITE_RESOURCE_GROUP = getEnvironmentVariable('WEBSITE_RESOURCE_GROUP')
56
+ const WEBSITE_SITE_NAME = getEnvironmentVariable('WEBSITE_SITE_NAME')
57
+ const WEBSITE_SKU = getEnvironmentVariable('WEBSITE_SKU')
60
58
 
61
59
  const DD_AZURE_RESOURCE_GROUP = getValueFromEnvSources('DD_AZURE_RESOURCE_GROUP')
60
+ const isAzureFunction = FUNCTIONS_EXTENSION_VERSION !== undefined && FUNCTIONS_WORKER_RUNTIME !== undefined
61
+ const isFlexConsumptionAzureFunction = isAzureFunction && WEBSITE_SKU === 'FlexConsumption'
62
62
 
63
63
  const subscriptionID = extractSubscriptionID(WEBSITE_OWNER_NAME)
64
64
 
65
65
  const siteName = WEBSITE_SITE_NAME
66
66
 
67
- const [siteKind, siteType] = getIsAzureFunction()
67
+ const [siteKind, siteType] = isAzureFunction
68
68
  ? ['functionapp', 'function']
69
69
  : ['app', 'app']
70
70
 
71
71
  // Azure Functions on Flex Consumption plans require the `DD_AZURE_RESOURCE_GROUP` env var.
72
72
  // If this logic ever changes, update the logic in `libdatadog`, `serverless-components/src/datadog-trace-agent`,
73
73
  // and the serverless compat layers accordingly.
74
- const resourceGroup = getIsFlexConsumptionAzureFunction()
74
+ const resourceGroup = isFlexConsumptionAzureFunction
75
75
  ? (DD_AZURE_RESOURCE_GROUP ?? WEBSITE_RESOURCE_GROUP ?? extractResourceGroup(WEBSITE_OWNER_NAME))
76
76
  : (WEBSITE_RESOURCE_GROUP ?? extractResourceGroup(WEBSITE_OWNER_NAME))
77
77