dd-trace 5.117.0 → 5.118.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.
- package/ci/diagnose.js +162 -21
- package/ci/init.js +0 -1
- package/ci/runbook.md +187 -180
- package/ci/test-optimization-validation/approval-artifacts.js +3 -1
- package/ci/test-optimization-validation/approval.js +52 -15
- package/ci/test-optimization-validation/ci-command-candidate.js +26 -32
- package/ci/test-optimization-validation/ci-discovery.js +1 -1
- package/ci/test-optimization-validation/ci-package-scripts.js +141 -0
- package/ci/test-optimization-validation/ci-remediation.js +112 -39
- package/ci/test-optimization-validation/cli.js +516 -681
- package/ci/test-optimization-validation/command-blocker.js +188 -33
- package/ci/test-optimization-validation/command-output-policy.js +3 -26
- package/ci/test-optimization-validation/command-runner.js +189 -245
- package/ci/test-optimization-validation/environment.js +90 -0
- package/ci/test-optimization-validation/executable.js +159 -388
- package/ci/test-optimization-validation/framework-adapters/cucumber.js +119 -0
- package/ci/test-optimization-validation/framework-adapters/cypress.js +107 -0
- package/ci/test-optimization-validation/framework-adapters/playwright.js +181 -0
- package/ci/test-optimization-validation/generated-files.js +75 -13
- package/ci/test-optimization-validation/generated-test-contract.js +283 -0
- package/ci/test-optimization-validation/generated-verifier.js +49 -16
- package/ci/test-optimization-validation/literal-environment.js +57 -0
- package/ci/test-optimization-validation/manifest-loader.js +2 -22
- package/ci/test-optimization-validation/manifest-scaffold.js +816 -515
- package/ci/test-optimization-validation/manifest-schema.js +494 -702
- package/ci/test-optimization-validation/offline-fixtures.js +4 -1
- package/ci/test-optimization-validation/plan-writer.js +243 -984
- package/ci/test-optimization-validation/preflight-runner.js +49 -55
- package/ci/test-optimization-validation/redaction.js +2 -1
- package/ci/test-optimization-validation/report-writer.js +391 -1357
- package/ci/test-optimization-validation/result-semantics.js +86 -0
- package/ci/test-optimization-validation/runner-command.js +249 -0
- package/ci/test-optimization-validation/runner-contract.js +664 -0
- package/ci/test-optimization-validation/scenarios/auto-test-retries.js +29 -3
- package/ci/test-optimization-validation/scenarios/basic-reporting.js +240 -543
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +450 -670
- package/ci/test-optimization-validation/scenarios/early-flake-detection.js +4 -3
- package/ci/test-optimization-validation/scenarios/helpers.js +67 -9
- package/ci/test-optimization-validation/scenarios/test-management.js +4 -3
- package/ci/test-optimization-validation/source-text.js +87 -0
- package/ci/test-optimization-validation/test-output.js +12 -22
- package/ext/tags.d.ts +1 -0
- package/index.d.ts +7 -0
- package/package.json +2 -2
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/openai-agents.js +31 -0
- package/packages/datadog-instrumentations/src/openai-agents.js +159 -0
- package/packages/datadog-plugin-openai-agents/src/index.js +74 -0
- package/packages/datadog-plugin-openai-agents/src/integration.js +503 -0
- package/packages/datadog-plugin-openai-agents/src/processor.js +108 -0
- package/packages/datadog-plugin-openai-agents/src/util.js +60 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +48 -49
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +161 -33
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +6 -4
- package/packages/dd-trace/src/config/generated-config-types.d.ts +2 -0
- package/packages/dd-trace/src/config/supported-configurations.json +7 -0
- package/packages/dd-trace/src/encode/span-stats.js +7 -1
- package/packages/dd-trace/src/exporter.js +16 -9
- package/packages/dd-trace/src/llmobs/index.js +9 -1
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +104 -22
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +5 -9
- package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +20 -0
- package/packages/dd-trace/src/llmobs/plugins/openai-agents/utils.js +321 -0
- package/packages/dd-trace/src/llmobs/sdk.js +2 -1
- package/packages/dd-trace/src/llmobs/span_processor.js +7 -2
- package/packages/dd-trace/src/llmobs/tagger.js +23 -3
- package/packages/dd-trace/src/llmobs/util.js +56 -3
- package/packages/dd-trace/src/openfeature/agentless_configuration_source.js +3 -6
- package/packages/dd-trace/src/openfeature/configuration_source.js +5 -8
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +5 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +9 -3
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/outbound.js +4 -1
- package/packages/dd-trace/src/span_processor.js +1 -1
- package/packages/dd-trace/src/span_stats.js +22 -4
- package/ci/test-optimization-validation/command-suitability.js +0 -471
- package/ci/test-optimization-validation/init-probe-preload.js +0 -163
- package/ci/test-optimization-validation/init-probe.js +0 -246
- package/ci/test-optimization-validation/late-initialization.js +0 -63
- package/ci/test-optimization-validation/local-command.js +0 -163
- package/ci/test-optimization-validation/setup-runner.js +0 -97
- package/ci/test-optimization-validation-manifest.schema.json +0 -1
|
@@ -20,6 +20,7 @@ const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('../../plugins/util/tags'
|
|
|
20
20
|
const { sendGitMetadata: sendGitMetadataRequest } = require('./git/git_metadata')
|
|
21
21
|
|
|
22
22
|
const hostname = getHostname()
|
|
23
|
+
const EMPTY_SETTINGS = Object.freeze({})
|
|
23
24
|
|
|
24
25
|
function getTestConfigurationTags (tags) {
|
|
25
26
|
if (!tags) {
|
|
@@ -308,60 +309,58 @@ class CiVisibilityExporter extends BufferingExporter {
|
|
|
308
309
|
}
|
|
309
310
|
|
|
310
311
|
// Takes into account potential kill switches
|
|
311
|
-
filterConfiguration (remoteConfiguration) {
|
|
312
|
-
|
|
313
|
-
return {}
|
|
314
|
-
}
|
|
312
|
+
filterConfiguration (remoteConfiguration = EMPTY_SETTINGS) {
|
|
313
|
+
const { testOptimization } = this._config
|
|
315
314
|
const {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
315
|
+
DD_CIVISIBILITY_EARLY_FLAKE_DETECTION_ENABLED: isEarlyFlakeDetectionAllowed,
|
|
316
|
+
DD_CIVISIBILITY_FLAKY_RETRY_COUNT: flakyTestRetriesCount = 0,
|
|
317
|
+
DD_CIVISIBILITY_FLAKY_RETRY_ENABLED: isFlakyTestRetriesAllowed,
|
|
318
|
+
DD_CIVISIBILITY_IMPACTED_TESTS_DETECTION_ENABLED: isImpactedTestsAllowed,
|
|
319
|
+
DD_TEST_EARLY_FLAKE_DETECTION_RETRY_COUNT: earlyFlakeDetectionRetryCount,
|
|
320
|
+
DD_TEST_FAILED_TEST_REPLAY_ENABLED: isFailedTestReplayAllowed,
|
|
321
|
+
DD_TEST_MANAGEMENT_ATTEMPT_TO_FIX_RETRIES: configuredAttemptToFixRetries = 0,
|
|
322
|
+
DD_TEST_MANAGEMENT_ENABLED: isTestManagementAllowed,
|
|
323
|
+
} = testOptimization
|
|
324
|
+
const hasEarlyFlakeDetectionRetryCount = earlyFlakeDetectionRetryCount !== undefined
|
|
325
|
+
const configuredSlowTestRetries = remoteConfiguration.earlyFlakeDetectionSlowTestRetries ?? EMPTY_SETTINGS
|
|
326
|
+
const earlyFlakeDetectionSlowTestRetries = hasEarlyFlakeDetectionRetryCount
|
|
327
|
+
? Object.freeze({
|
|
328
|
+
'5s': earlyFlakeDetectionRetryCount,
|
|
329
|
+
'10s': earlyFlakeDetectionRetryCount,
|
|
330
|
+
'30s': earlyFlakeDetectionRetryCount,
|
|
331
|
+
'5m': earlyFlakeDetectionRetryCount,
|
|
332
|
+
})
|
|
333
|
+
: Object.isFrozen(configuredSlowTestRetries)
|
|
334
|
+
? configuredSlowTestRetries
|
|
335
|
+
: Object.freeze({ ...configuredSlowTestRetries })
|
|
336
|
+
const earlyFlakeDetectionNumRetries = hasEarlyFlakeDetectionRetryCount
|
|
337
|
+
? earlyFlakeDetectionRetryCount
|
|
338
|
+
: remoteConfiguration.earlyFlakeDetectionNumRetries ?? 0
|
|
339
|
+
const testManagementAttemptToFixRetries =
|
|
340
|
+
remoteConfiguration.testManagementAttemptToFixRetries ?? configuredAttemptToFixRetries
|
|
341
|
+
|
|
342
|
+
return Object.freeze({
|
|
343
|
+
isCodeCoverageEnabled: remoteConfiguration.isCodeCoverageEnabled === true,
|
|
344
|
+
isSuitesSkippingEnabled: remoteConfiguration.isSuitesSkippingEnabled === true,
|
|
345
|
+
isItrEnabled: remoteConfiguration.isItrEnabled === true,
|
|
346
|
+
requireGit: remoteConfiguration.requireGit === true,
|
|
347
|
+
isEarlyFlakeDetectionEnabled:
|
|
348
|
+
remoteConfiguration.isEarlyFlakeDetectionEnabled === true && isEarlyFlakeDetectionAllowed === true,
|
|
321
349
|
earlyFlakeDetectionNumRetries,
|
|
322
350
|
earlyFlakeDetectionSlowTestRetries,
|
|
323
|
-
earlyFlakeDetectionFaultyThreshold,
|
|
324
|
-
isFlakyTestRetriesEnabled
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
351
|
+
earlyFlakeDetectionFaultyThreshold: remoteConfiguration.earlyFlakeDetectionFaultyThreshold ?? 30,
|
|
352
|
+
isFlakyTestRetriesEnabled:
|
|
353
|
+
remoteConfiguration.isFlakyTestRetriesEnabled === true && isFlakyTestRetriesAllowed === true,
|
|
354
|
+
flakyTestRetriesCount,
|
|
355
|
+
isDiEnabled: remoteConfiguration.isDiEnabled === true && isFailedTestReplayAllowed === true,
|
|
356
|
+
isKnownTestsEnabled: remoteConfiguration.isKnownTestsEnabled === true,
|
|
357
|
+
isTestManagementEnabled:
|
|
358
|
+
remoteConfiguration.isTestManagementEnabled === true && isTestManagementAllowed === true,
|
|
328
359
|
testManagementAttemptToFixRetries,
|
|
329
|
-
isImpactedTestsEnabled,
|
|
330
|
-
isCoverageReportUploadEnabled,
|
|
331
|
-
} = remoteConfiguration
|
|
332
|
-
const { testOptimization } = this._config
|
|
333
|
-
const earlyFlakeDetectionRetryCount =
|
|
334
|
-
testOptimization.DD_TEST_EARLY_FLAKE_DETECTION_RETRY_COUNT
|
|
335
|
-
const hasEarlyFlakeDetectionRetryCount = earlyFlakeDetectionRetryCount !== undefined
|
|
336
|
-
return {
|
|
337
|
-
isCodeCoverageEnabled,
|
|
338
|
-
isSuitesSkippingEnabled,
|
|
339
|
-
isItrEnabled,
|
|
340
|
-
requireGit,
|
|
341
|
-
isEarlyFlakeDetectionEnabled:
|
|
342
|
-
isEarlyFlakeDetectionEnabled && testOptimization.DD_CIVISIBILITY_EARLY_FLAKE_DETECTION_ENABLED,
|
|
343
|
-
earlyFlakeDetectionNumRetries:
|
|
344
|
-
hasEarlyFlakeDetectionRetryCount ? earlyFlakeDetectionRetryCount : earlyFlakeDetectionNumRetries,
|
|
345
|
-
earlyFlakeDetectionSlowTestRetries: hasEarlyFlakeDetectionRetryCount
|
|
346
|
-
? {
|
|
347
|
-
'5s': earlyFlakeDetectionRetryCount,
|
|
348
|
-
'10s': earlyFlakeDetectionRetryCount,
|
|
349
|
-
'30s': earlyFlakeDetectionRetryCount,
|
|
350
|
-
'5m': earlyFlakeDetectionRetryCount,
|
|
351
|
-
}
|
|
352
|
-
: earlyFlakeDetectionSlowTestRetries,
|
|
353
|
-
earlyFlakeDetectionFaultyThreshold,
|
|
354
|
-
isFlakyTestRetriesEnabled: isFlakyTestRetriesEnabled && testOptimization.DD_CIVISIBILITY_FLAKY_RETRY_ENABLED,
|
|
355
|
-
flakyTestRetriesCount: testOptimization.DD_CIVISIBILITY_FLAKY_RETRY_COUNT,
|
|
356
|
-
isDiEnabled: isDiEnabled && testOptimization.DD_TEST_FAILED_TEST_REPLAY_ENABLED,
|
|
357
|
-
isKnownTestsEnabled,
|
|
358
|
-
isTestManagementEnabled: isTestManagementEnabled && testOptimization.DD_TEST_MANAGEMENT_ENABLED,
|
|
359
|
-
testManagementAttemptToFixRetries:
|
|
360
|
-
testManagementAttemptToFixRetries ?? testOptimization.DD_TEST_MANAGEMENT_ATTEMPT_TO_FIX_RETRIES,
|
|
361
360
|
isImpactedTestsEnabled:
|
|
362
|
-
isImpactedTestsEnabled &&
|
|
363
|
-
isCoverageReportUploadEnabled,
|
|
364
|
-
}
|
|
361
|
+
remoteConfiguration.isImpactedTestsEnabled === true && isImpactedTestsAllowed === true,
|
|
362
|
+
isCoverageReportUploadEnabled: remoteConfiguration.isCoverageReportUploadEnabled === true,
|
|
363
|
+
})
|
|
365
364
|
}
|
|
366
365
|
|
|
367
366
|
sendGitMetadata (repositoryUrl) {
|
|
@@ -12,12 +12,145 @@ const {
|
|
|
12
12
|
TELEMETRY_GIT_REQUESTS_SETTINGS_RESPONSE,
|
|
13
13
|
} = require('../telemetry')
|
|
14
14
|
const { writeSettingsToCache } = require('../test-optimization-cache')
|
|
15
|
-
const { validateSettingsResponse } = require('../test-optimization-http-cache-schema')
|
|
15
|
+
const { MAX_RETRIES, validateSettingsResponse } = require('../test-optimization-http-cache-schema')
|
|
16
16
|
const request = require('./request')
|
|
17
17
|
|
|
18
18
|
const DEFAULT_EARLY_FLAKE_DETECTION_NUM_RETRIES = 2
|
|
19
|
-
const DEFAULT_EARLY_FLAKE_DETECTION_SLOW_TEST_RETRIES = {
|
|
19
|
+
const DEFAULT_EARLY_FLAKE_DETECTION_SLOW_TEST_RETRIES = Object.freeze({
|
|
20
|
+
'5s': 10,
|
|
21
|
+
'10s': 5,
|
|
22
|
+
'30s': 3,
|
|
23
|
+
'5m': 2,
|
|
24
|
+
})
|
|
20
25
|
const DEFAULT_EARLY_FLAKE_DETECTION_ERROR_THRESHOLD = 30
|
|
26
|
+
const EARLY_FLAKE_DETECTION_RETRY_BUCKETS = Object.keys(DEFAULT_EARLY_FLAKE_DETECTION_SLOW_TEST_RETRIES)
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @typedef {object} EarlyFlakeDetectionSettings
|
|
30
|
+
* @property {boolean} enabled
|
|
31
|
+
* @property {number} numRetries
|
|
32
|
+
* @property {Readonly<Record<string, number>>} slowTestRetries
|
|
33
|
+
* @property {number} faultyThreshold
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @typedef {object} TestManagementSettings
|
|
38
|
+
* @property {boolean} enabled
|
|
39
|
+
* @property {number|undefined} attemptToFixRetries
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param {unknown} value
|
|
44
|
+
* @returns {value is Record<string, unknown>}
|
|
45
|
+
*/
|
|
46
|
+
function isRecord (value) {
|
|
47
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @param {unknown} value
|
|
52
|
+
* @returns {value is number}
|
|
53
|
+
*/
|
|
54
|
+
function isNonNegativeInteger (value) {
|
|
55
|
+
return Number.isInteger(value) && value >= 0
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @param {unknown} value
|
|
60
|
+
* @returns {value is number}
|
|
61
|
+
*/
|
|
62
|
+
function isRetryCount (value) {
|
|
63
|
+
return isNonNegativeInteger(value) && value <= MAX_RETRIES
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @param {unknown} value
|
|
68
|
+
* @returns {Readonly<Record<string, number>>|undefined}
|
|
69
|
+
*/
|
|
70
|
+
function parseSlowTestRetries (value) {
|
|
71
|
+
if (!isRecord(value)) return
|
|
72
|
+
|
|
73
|
+
const slowTestRetries = {}
|
|
74
|
+
for (const bucket of EARLY_FLAKE_DETECTION_RETRY_BUCKETS) {
|
|
75
|
+
if (!Object.hasOwn(value, bucket)) continue
|
|
76
|
+
|
|
77
|
+
const retries = value[bucket]
|
|
78
|
+
if (!isRetryCount(retries)) return
|
|
79
|
+
slowTestRetries[bucket] = retries
|
|
80
|
+
}
|
|
81
|
+
return Object.freeze(slowTestRetries)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @param {unknown} value
|
|
86
|
+
* @param {boolean} isKnownTestsEnabled
|
|
87
|
+
* @returns {EarlyFlakeDetectionSettings}
|
|
88
|
+
*/
|
|
89
|
+
function parseEarlyFlakeDetectionSettings (value, isKnownTestsEnabled) {
|
|
90
|
+
if (!isRecord(value) || value.enabled !== true) {
|
|
91
|
+
return {
|
|
92
|
+
enabled: false,
|
|
93
|
+
numRetries: DEFAULT_EARLY_FLAKE_DETECTION_NUM_RETRIES,
|
|
94
|
+
slowTestRetries: DEFAULT_EARLY_FLAKE_DETECTION_SLOW_TEST_RETRIES,
|
|
95
|
+
faultyThreshold: DEFAULT_EARLY_FLAKE_DETECTION_ERROR_THRESHOLD,
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
let isValid = true
|
|
100
|
+
let numRetries = DEFAULT_EARLY_FLAKE_DETECTION_NUM_RETRIES
|
|
101
|
+
let slowTestRetries = DEFAULT_EARLY_FLAKE_DETECTION_SLOW_TEST_RETRIES
|
|
102
|
+
if (Object.hasOwn(value, 'slow_test_retries')) {
|
|
103
|
+
const parsedSlowTestRetries = parseSlowTestRetries(value.slow_test_retries)
|
|
104
|
+
if (parsedSlowTestRetries) {
|
|
105
|
+
slowTestRetries = parsedSlowTestRetries
|
|
106
|
+
numRetries = parsedSlowTestRetries['5s'] ?? DEFAULT_EARLY_FLAKE_DETECTION_NUM_RETRIES
|
|
107
|
+
} else {
|
|
108
|
+
isValid = false
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let faultyThreshold = DEFAULT_EARLY_FLAKE_DETECTION_ERROR_THRESHOLD
|
|
113
|
+
if (Object.hasOwn(value, 'faulty_session_threshold')) {
|
|
114
|
+
if (isNonNegativeInteger(value.faulty_session_threshold) && value.faulty_session_threshold <= 100) {
|
|
115
|
+
faultyThreshold = value.faulty_session_threshold
|
|
116
|
+
} else {
|
|
117
|
+
isValid = false
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
enabled: isKnownTestsEnabled && isValid,
|
|
123
|
+
numRetries,
|
|
124
|
+
slowTestRetries,
|
|
125
|
+
faultyThreshold,
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* @param {unknown} value
|
|
131
|
+
* @returns {TestManagementSettings}
|
|
132
|
+
*/
|
|
133
|
+
function parseTestManagementSettings (value) {
|
|
134
|
+
if (!isRecord(value) || value.enabled !== true) {
|
|
135
|
+
return {
|
|
136
|
+
enabled: false,
|
|
137
|
+
attemptToFixRetries: undefined,
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const attemptToFixRetries = value.attempt_to_fix_retries
|
|
142
|
+
if (attemptToFixRetries !== undefined && !isRetryCount(attemptToFixRetries)) {
|
|
143
|
+
return {
|
|
144
|
+
enabled: false,
|
|
145
|
+
attemptToFixRetries: undefined,
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
enabled: true,
|
|
151
|
+
attemptToFixRetries,
|
|
152
|
+
}
|
|
153
|
+
}
|
|
21
154
|
|
|
22
155
|
function parseJsonResponse (rawJson) {
|
|
23
156
|
return typeof rawJson === 'string' ? JSON.parse(rawJson) : rawJson
|
|
@@ -28,40 +161,35 @@ function parseLibraryConfigurationResponse (rawJson, config = getConfig(), optio
|
|
|
28
161
|
if (options.validateRequiredFields) {
|
|
29
162
|
validateSettingsResponse(parsedResponse, options)
|
|
30
163
|
}
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
} = parsedResponse?.data?.attributes ?? parsedResponse
|
|
164
|
+
const parsedAttributes = parsedResponse?.data?.attributes ?? parsedResponse
|
|
165
|
+
if (!isRecord(parsedAttributes)) {
|
|
166
|
+
throw new TypeError('Invalid settings response: attributes must be an object')
|
|
167
|
+
}
|
|
168
|
+
const attributes = parsedAttributes
|
|
169
|
+
const isKnownTestsEnabled = attributes.known_tests_enabled === true
|
|
170
|
+
const isFlakyTestRetriesEnabled = attributes.flaky_test_retries_enabled === true
|
|
171
|
+
const earlyFlakeDetection = parseEarlyFlakeDetectionSettings(
|
|
172
|
+
attributes.early_flake_detection,
|
|
173
|
+
isKnownTestsEnabled
|
|
174
|
+
)
|
|
175
|
+
const testManagement = parseTestManagementSettings(attributes.test_management)
|
|
44
176
|
|
|
45
177
|
const settings = {
|
|
46
|
-
isCodeCoverageEnabled,
|
|
47
|
-
isSuitesSkippingEnabled,
|
|
48
|
-
isItrEnabled,
|
|
49
|
-
requireGit,
|
|
50
|
-
isEarlyFlakeDetectionEnabled:
|
|
51
|
-
earlyFlakeDetectionNumRetries:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
earlyFlakeDetectionConfig?.slow_test_retries ?? DEFAULT_EARLY_FLAKE_DETECTION_SLOW_TEST_RETRIES,
|
|
55
|
-
earlyFlakeDetectionFaultyThreshold:
|
|
56
|
-
earlyFlakeDetectionConfig?.faulty_session_threshold ?? DEFAULT_EARLY_FLAKE_DETECTION_ERROR_THRESHOLD,
|
|
178
|
+
isCodeCoverageEnabled: attributes.code_coverage === true,
|
|
179
|
+
isSuitesSkippingEnabled: attributes.tests_skipping === true,
|
|
180
|
+
isItrEnabled: attributes.itr_enabled === true,
|
|
181
|
+
requireGit: attributes.require_git === true,
|
|
182
|
+
isEarlyFlakeDetectionEnabled: earlyFlakeDetection.enabled,
|
|
183
|
+
earlyFlakeDetectionNumRetries: earlyFlakeDetection.numRetries,
|
|
184
|
+
earlyFlakeDetectionSlowTestRetries: earlyFlakeDetection.slowTestRetries,
|
|
185
|
+
earlyFlakeDetectionFaultyThreshold: earlyFlakeDetection.faultyThreshold,
|
|
57
186
|
isFlakyTestRetriesEnabled,
|
|
58
|
-
isDiEnabled:
|
|
187
|
+
isDiEnabled: attributes.di_enabled === true && isFlakyTestRetriesEnabled,
|
|
59
188
|
isKnownTestsEnabled,
|
|
60
|
-
isTestManagementEnabled:
|
|
61
|
-
testManagementAttemptToFixRetries:
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
isCoverageReportUploadEnabled: isCoverageReportUploadEnabled ?? false,
|
|
189
|
+
isTestManagementEnabled: testManagement.enabled,
|
|
190
|
+
testManagementAttemptToFixRetries: testManagement.attemptToFixRetries,
|
|
191
|
+
isImpactedTestsEnabled: attributes.impacted_tests_enabled === true,
|
|
192
|
+
isCoverageReportUploadEnabled: attributes.coverage_report_upload_enabled === true,
|
|
65
193
|
}
|
|
66
194
|
|
|
67
195
|
log.debug('Remote settings: %j', settings)
|
|
@@ -82,7 +210,7 @@ function parseLibraryConfigurationResponse (rawJson, config = getConfig(), optio
|
|
|
82
210
|
log.debug('Code coverage report upload was disabled by the environment variable')
|
|
83
211
|
}
|
|
84
212
|
|
|
85
|
-
return settings
|
|
213
|
+
return Object.freeze(settings)
|
|
86
214
|
}
|
|
87
215
|
|
|
88
216
|
function getLibraryConfiguration ({
|
|
@@ -30,7 +30,7 @@ const RETRY_THRESHOLD_PATTERN = /^\d+(?:ms|s|m|h)$/
|
|
|
30
30
|
const MAX_VALIDATION_MODULES = 1000
|
|
31
31
|
const MAX_VALIDATION_SUITES = 10_000
|
|
32
32
|
const MAX_VALIDATION_TESTS = 100_000
|
|
33
|
-
const
|
|
33
|
+
const MAX_RETRIES = 100
|
|
34
34
|
const MAX_VALIDATION_STRING_BYTES = 4096
|
|
35
35
|
|
|
36
36
|
function isObject (value) {
|
|
@@ -75,7 +75,8 @@ function validateSettingsResponse (response, options = {}) {
|
|
|
75
75
|
assertBooleanFields(attributes.test_management, ['enabled'], 'settings test_management')
|
|
76
76
|
assertOptionalBoundedNumber(
|
|
77
77
|
attributes.early_flake_detection.faulty_session_threshold,
|
|
78
|
-
'settings early_flake_detection faulty_session_threshold'
|
|
78
|
+
'settings early_flake_detection faulty_session_threshold',
|
|
79
|
+
{ integer: true }
|
|
79
80
|
)
|
|
80
81
|
assertRetryMap(attributes.early_flake_detection.slow_test_retries)
|
|
81
82
|
assertOptionalBoundedNumber(
|
|
@@ -216,8 +217,8 @@ function assertBooleanFields (object, keys, endpoint) {
|
|
|
216
217
|
function assertOptionalBoundedNumber (value, endpoint, { integer = false } = {}) {
|
|
217
218
|
if (value === undefined) return
|
|
218
219
|
if (!Number.isFinite(value) || (integer && !Number.isInteger(value)) ||
|
|
219
|
-
value < 0 || value >
|
|
220
|
-
throw new TypeError(`Invalid ${endpoint}: value must be between 0 and ${
|
|
220
|
+
value < 0 || value > MAX_RETRIES) {
|
|
221
|
+
throw new TypeError(`Invalid ${endpoint}: value must be between 0 and ${MAX_RETRIES}`)
|
|
221
222
|
}
|
|
222
223
|
}
|
|
223
224
|
|
|
@@ -249,6 +250,7 @@ function assertValidationString (value, field) {
|
|
|
249
250
|
}
|
|
250
251
|
|
|
251
252
|
module.exports = {
|
|
253
|
+
MAX_RETRIES,
|
|
252
254
|
validateKnownTestsResponse,
|
|
253
255
|
validateSettingsResponse,
|
|
254
256
|
validateSkippableTestsResponse,
|
|
@@ -333,6 +333,7 @@ export interface GeneratedConfig {
|
|
|
333
333
|
DD_TRACE_NODE_SERIALIZE_ENABLED: boolean;
|
|
334
334
|
DD_TRACE_NYC_ENABLED: boolean;
|
|
335
335
|
DD_TRACE_OBFUSCATION_QUERY_STRING_REGEXP: string;
|
|
336
|
+
DD_TRACE_OPENAI_AGENTS_ENABLED: boolean;
|
|
336
337
|
DD_TRACE_OPENAI_ENABLED: boolean;
|
|
337
338
|
DD_TRACE_OPENSEARCH_ENABLED: boolean;
|
|
338
339
|
DD_TRACE_OPENSEARCH_PROJECT_OPENSEARCH_ENABLED: boolean;
|
|
@@ -1028,6 +1029,7 @@ export interface GeneratedEnvVarConfig {
|
|
|
1028
1029
|
DD_TRACE_NODE_SERIALIZE_ENABLED: boolean;
|
|
1029
1030
|
DD_TRACE_NYC_ENABLED: boolean;
|
|
1030
1031
|
DD_TRACE_OBFUSCATION_QUERY_STRING_REGEXP: string;
|
|
1032
|
+
DD_TRACE_OPENAI_AGENTS_ENABLED: boolean;
|
|
1031
1033
|
DD_TRACE_OPENAI_ENABLED: boolean;
|
|
1032
1034
|
DD_TRACE_OPENSEARCH_ENABLED: boolean;
|
|
1033
1035
|
DD_TRACE_OPENSEARCH_PROJECT_OPENSEARCH_ENABLED: boolean;
|
|
@@ -3452,6 +3452,13 @@
|
|
|
3452
3452
|
"default": "true"
|
|
3453
3453
|
}
|
|
3454
3454
|
],
|
|
3455
|
+
"DD_TRACE_OPENAI_AGENTS_ENABLED": [
|
|
3456
|
+
{
|
|
3457
|
+
"implementation": "A",
|
|
3458
|
+
"type": "boolean",
|
|
3459
|
+
"default": "true"
|
|
3460
|
+
}
|
|
3461
|
+
],
|
|
3455
3462
|
"DD_TRACE_OPENSEARCH_ENABLED": [
|
|
3456
3463
|
{
|
|
3457
3464
|
"implementation": "A",
|
|
@@ -31,7 +31,7 @@ class SpanStatsEncoder extends AgentEncoder {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
_encodeStat (bytes, stat) {
|
|
34
|
-
bytes.writeMapPrefix(
|
|
34
|
+
bytes.writeMapPrefix(17)
|
|
35
35
|
|
|
36
36
|
this._encodeString(bytes, 'Service')
|
|
37
37
|
const service = stat.Service || DEFAULT_SERVICE_NAME
|
|
@@ -79,6 +79,12 @@ class SpanStatsEncoder extends AgentEncoder {
|
|
|
79
79
|
|
|
80
80
|
this._encodeString(bytes, 'srv_src')
|
|
81
81
|
this._encodeString(bytes, stat.srv_src || '')
|
|
82
|
+
|
|
83
|
+
this._encodeString(bytes, 'SpanKind')
|
|
84
|
+
this._encodeString(bytes, stat.SpanKind || '')
|
|
85
|
+
|
|
86
|
+
this._encodeString(bytes, 'GRPCStatusCode')
|
|
87
|
+
this._encodeString(bytes, stat.GRPCStatusCode || '')
|
|
82
88
|
}
|
|
83
89
|
|
|
84
90
|
_encodeBucket (bytes, bucket) {
|
|
@@ -4,6 +4,7 @@ const fs = require('fs')
|
|
|
4
4
|
const exporters = require('../../../ext/exporters')
|
|
5
5
|
const { getEnvironmentVariable } = require('../../dd-trace/src/config/helper')
|
|
6
6
|
const constants = require('./constants')
|
|
7
|
+
const { isTrue } = require('./util')
|
|
7
8
|
|
|
8
9
|
module.exports = function getExporter (name) {
|
|
9
10
|
switch (name) {
|
|
@@ -20,20 +21,26 @@ module.exports = function getExporter (name) {
|
|
|
20
21
|
case exporters.AGENT_PROXY:
|
|
21
22
|
return require('./ci-visibility/exporters/agent-proxy')
|
|
22
23
|
case exporters.CI_VALIDATION:
|
|
23
|
-
return require('./ci-visibility/exporters/ci-validation')
|
|
24
|
+
if (hasCiValidationEnvironment()) return require('./ci-visibility/exporters/ci-validation')
|
|
25
|
+
break
|
|
24
26
|
case exporters.JEST_WORKER:
|
|
25
27
|
case exporters.CUCUMBER_WORKER:
|
|
26
28
|
case exporters.MOCHA_WORKER:
|
|
27
29
|
case exporters.PLAYWRIGHT_WORKER:
|
|
28
30
|
case exporters.VITEST_WORKER:
|
|
29
31
|
return require('./ci-visibility/exporters/test-worker')
|
|
30
|
-
default: {
|
|
31
|
-
const inAWSLambda = getEnvironmentVariable('AWS_LAMBDA_FUNCTION_NAME') !== undefined
|
|
32
|
-
const usingAgent = inAWSLambda && (
|
|
33
|
-
fs.existsSync(constants.DATADOG_LAMBDA_EXTENSION_PATH) ||
|
|
34
|
-
fs.existsSync(constants.DATADOG_MINI_AGENT_PATH)
|
|
35
|
-
)
|
|
36
|
-
return inAWSLambda && !usingAgent ? require('./exporters/log') : require('./exporters/agent')
|
|
37
|
-
}
|
|
38
32
|
}
|
|
33
|
+
|
|
34
|
+
const inAWSLambda = getEnvironmentVariable('AWS_LAMBDA_FUNCTION_NAME') !== undefined
|
|
35
|
+
const usingAgent = inAWSLambda && (
|
|
36
|
+
fs.existsSync(constants.DATADOG_LAMBDA_EXTENSION_PATH) ||
|
|
37
|
+
fs.existsSync(constants.DATADOG_MINI_AGENT_PATH)
|
|
38
|
+
)
|
|
39
|
+
return inAWSLambda && !usingAgent ? require('./exporters/log') : require('./exporters/agent')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function hasCiValidationEnvironment () {
|
|
43
|
+
return isTrue(getEnvironmentVariable('_DD_TEST_OPTIMIZATION_VALIDATION_MODE')) &&
|
|
44
|
+
getEnvironmentVariable('_DD_TEST_OPTIMIZATION_VALIDATION_MANIFEST_FILE') &&
|
|
45
|
+
getEnvironmentVariable('_DD_TEST_OPTIMIZATION_VALIDATION_OUTPUT_DIR')
|
|
39
46
|
}
|
|
@@ -16,6 +16,8 @@ const {
|
|
|
16
16
|
SAMPLING_DECISION,
|
|
17
17
|
PROPAGATED_SAMPLE_RATE_KEY,
|
|
18
18
|
PROPAGATED_SAMPLING_DECISION_KEY,
|
|
19
|
+
TRACE_ID,
|
|
20
|
+
PROPAGATED_TRACE_ID_KEY,
|
|
19
21
|
} = require('./constants/tags')
|
|
20
22
|
const { storage } = require('./storage')
|
|
21
23
|
const telemetry = require('./telemetry')
|
|
@@ -25,6 +27,7 @@ const LLMObsTagger = require('./tagger')
|
|
|
25
27
|
const LLMObsSpanWriter = require('./writers/spans')
|
|
26
28
|
const { setAgentStrategy } = require('./writers/util')
|
|
27
29
|
const { INCOMPATIBLE_INITIALIZATION } = require('./constants/text')
|
|
30
|
+
const { llmObsTraceIdToWire } = require('./util')
|
|
28
31
|
|
|
29
32
|
const spanFinishCh = channel('dd-trace:span:finish')
|
|
30
33
|
const evalMetricAppendCh = channel('llmobs:eval-metric:append')
|
|
@@ -137,8 +140,12 @@ function handleLLMObsInjection ({ carrier }) {
|
|
|
137
140
|
mlObsSpanTags?.[SESSION_ID] ??
|
|
138
141
|
parentContext?._trace?.tags?.[SESSION_ID_TRACE_DEFAULT_KEY] ??
|
|
139
142
|
parentContext?._trace?.tags?.[PROPAGATED_SESSION_ID_KEY]
|
|
143
|
+
const llmobsTraceId = mlObsSpanTags?.[TRACE_ID]
|
|
144
|
+
const propagatedTraceId = llmobsTraceId === undefined
|
|
145
|
+
? parentContext?._trace?.tags?.[PROPAGATED_TRACE_ID_KEY]
|
|
146
|
+
: llmObsTraceIdToWire(llmobsTraceId)
|
|
140
147
|
|
|
141
|
-
if (!parentId && !mlApp && samplingDecision == null && !sessionId) return
|
|
148
|
+
if (!parentId && !mlApp && samplingDecision == null && !sessionId && !propagatedTraceId) return
|
|
142
149
|
|
|
143
150
|
// `_injectTags` only writes `x-datadog-tags` when the trace has `_dd.p.*`
|
|
144
151
|
// tags, so it may be undefined here — coalesce before appending.
|
|
@@ -149,6 +156,7 @@ function handleLLMObsInjection ({ carrier }) {
|
|
|
149
156
|
if (sessionId) tags += `${tags ? ',' : ''}${PROPAGATED_SESSION_ID_KEY}=${sessionId}`
|
|
150
157
|
if (sampleRate != null) tags += `${tags ? ',' : ''}${PROPAGATED_SAMPLE_RATE_KEY}=${sampleRate}`
|
|
151
158
|
if (samplingDecision != null) tags += `${tags ? ',' : ''}${PROPAGATED_SAMPLING_DECISION_KEY}=${samplingDecision}`
|
|
159
|
+
if (propagatedTraceId != null) tags += `${tags ? ',' : ''}${PROPAGATED_TRACE_ID_KEY}=${propagatedTraceId}`
|
|
152
160
|
if (tags !== existing) carrier['x-datadog-tags'] = tags
|
|
153
161
|
}
|
|
154
162
|
|
|
@@ -13,6 +13,8 @@ const MODEL_METADATA_KEYS = new Set([
|
|
|
13
13
|
const VERCEL_AI_TELEMETRY_METADATA_PREFIX = 'ai.telemetry.metadata.'
|
|
14
14
|
const VERCEL_AI_MODEL_METADATA_PREFIX = 'gen_ai.request.'
|
|
15
15
|
const VERCEL_AI_GENERATION_METADATA_PREFIX = 'ai.settings.'
|
|
16
|
+
const UNPARSABLE_TOOL_RESULT = '[Unparsable Tool Result]'
|
|
17
|
+
const UNSUPPORTED_TOOL_RESULT = '[Unsupported Tool Result]'
|
|
16
18
|
|
|
17
19
|
/**
|
|
18
20
|
* @typedef {import('../../../opentracing/span')} Span
|
|
@@ -26,8 +28,31 @@ const VERCEL_AI_GENERATION_METADATA_PREFIX = 'ai.settings.'
|
|
|
26
28
|
|
|
27
29
|
/**
|
|
28
30
|
* @typedef {{
|
|
29
|
-
* type:
|
|
30
|
-
*
|
|
31
|
+
* type: 'text',
|
|
32
|
+
* text: string
|
|
33
|
+
* } | {
|
|
34
|
+
* type: 'media' | 'file',
|
|
35
|
+
* mediaType: string
|
|
36
|
+
* } | {
|
|
37
|
+
* type: 'file-data' | 'file-url' | 'file-id' | 'file-reference'
|
|
38
|
+
* } | {
|
|
39
|
+
* type: 'image-data' | 'image-url' | 'image-file-id' | 'image-file-reference'
|
|
40
|
+
* } | {
|
|
41
|
+
* type: 'custom'
|
|
42
|
+
* }} ToolCallContentPart
|
|
43
|
+
*
|
|
44
|
+
* @typedef {{
|
|
45
|
+
* type: 'text' | 'error-text',
|
|
46
|
+
* value: string
|
|
47
|
+
* } | {
|
|
48
|
+
* type: 'json' | 'error-json',
|
|
49
|
+
* value: unknown
|
|
50
|
+
* } | {
|
|
51
|
+
* type: 'content',
|
|
52
|
+
* value: ToolCallContentPart[]
|
|
53
|
+
* } | {
|
|
54
|
+
* type: 'execution-denied',
|
|
55
|
+
* reason?: string
|
|
31
56
|
* }} ToolCallOutput
|
|
32
57
|
*
|
|
33
58
|
* @typedef {{ output?: ToolCallOutput, result?: unknown } & Record<string, unknown>} ToolCallResultContent
|
|
@@ -304,32 +329,89 @@ function getToolNameFromTags (tags) {
|
|
|
304
329
|
}
|
|
305
330
|
|
|
306
331
|
/**
|
|
307
|
-
*
|
|
308
|
-
*
|
|
309
|
-
|
|
332
|
+
* @param {unknown} value
|
|
333
|
+
* @returns {string}
|
|
334
|
+
*/
|
|
335
|
+
function stringifyToolCallResult (value) {
|
|
336
|
+
return JSON.stringify(value) ?? UNPARSABLE_TOOL_RESULT
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* @param {ToolCallContentPart[]} value
|
|
341
|
+
* @returns {string}
|
|
342
|
+
*/
|
|
343
|
+
function formatToolCallContent (value) {
|
|
344
|
+
if (!Array.isArray(value)) return UNPARSABLE_TOOL_RESULT
|
|
345
|
+
|
|
346
|
+
let result = ''
|
|
347
|
+
for (const part of value) {
|
|
348
|
+
if (typeof part !== 'object' || part === null) return UNPARSABLE_TOOL_RESULT
|
|
349
|
+
|
|
350
|
+
const { type } = part
|
|
351
|
+
if (type === 'text') {
|
|
352
|
+
if (typeof part.text !== 'string') return UNPARSABLE_TOOL_RESULT
|
|
353
|
+
result += part.text
|
|
354
|
+
} else if (type === 'media' || type === 'file') {
|
|
355
|
+
const { mediaType } = part
|
|
356
|
+
if (typeof mediaType !== 'string') return UNPARSABLE_TOOL_RESULT
|
|
357
|
+
result += mediaType === 'image' || mediaType.startsWith('image/') ? '[Image]' : '[File]'
|
|
358
|
+
} else if (
|
|
359
|
+
type === 'file-data' ||
|
|
360
|
+
type === 'file-url' ||
|
|
361
|
+
type === 'file-id' ||
|
|
362
|
+
type === 'file-reference'
|
|
363
|
+
) {
|
|
364
|
+
result += '[File]'
|
|
365
|
+
} else if (
|
|
366
|
+
type === 'image-data' ||
|
|
367
|
+
type === 'image-url' ||
|
|
368
|
+
type === 'image-file-id' ||
|
|
369
|
+
type === 'image-file-reference'
|
|
370
|
+
) {
|
|
371
|
+
result += '[Image]'
|
|
372
|
+
} else if (type === 'custom') {
|
|
373
|
+
result += '[Custom Content]'
|
|
374
|
+
} else {
|
|
375
|
+
return UNPARSABLE_TOOL_RESULT
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return result
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* @param {ToolCallResultContent | null | undefined} content
|
|
310
384
|
* @returns {string}
|
|
311
385
|
*/
|
|
312
386
|
function getToolCallResultContent (content) {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
387
|
+
try {
|
|
388
|
+
if (typeof content !== 'object' || content === null) return UNPARSABLE_TOOL_RESULT
|
|
389
|
+
|
|
390
|
+
const { output, result } = content
|
|
391
|
+
if (output !== undefined) {
|
|
392
|
+
if (typeof output !== 'object' || output === null) return UNPARSABLE_TOOL_RESULT
|
|
393
|
+
|
|
394
|
+
const { type, value } = output
|
|
395
|
+
if (type === 'text' || type === 'error-text') {
|
|
396
|
+
return typeof value === 'string' ? value : UNPARSABLE_TOOL_RESULT
|
|
397
|
+
} else if (type === 'json' || type === 'error-json') {
|
|
398
|
+
return stringifyToolCallResult(value)
|
|
399
|
+
} else if (type === 'content') {
|
|
400
|
+
return formatToolCallContent(value)
|
|
401
|
+
} else if (type === 'execution-denied') {
|
|
402
|
+
const { reason } = output
|
|
403
|
+
if (reason === undefined) return '[Tool Execution Denied]'
|
|
404
|
+
return typeof reason === 'string' ? reason : UNPARSABLE_TOOL_RESULT
|
|
405
|
+
}
|
|
406
|
+
return UNPARSABLE_TOOL_RESULT
|
|
319
407
|
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
if (typeof result === 'string') {
|
|
323
|
-
return result
|
|
408
|
+
if (result !== undefined) {
|
|
409
|
+
return typeof result === 'string' ? result : stringifyToolCallResult(result)
|
|
324
410
|
}
|
|
325
411
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
return '[Unparsable Tool Result]'
|
|
330
|
-
}
|
|
331
|
-
} else {
|
|
332
|
-
return '[Unsupported Tool Result]'
|
|
412
|
+
return UNSUPPORTED_TOOL_RESULT
|
|
413
|
+
} catch {
|
|
414
|
+
return UNPARSABLE_TOOL_RESULT
|
|
333
415
|
}
|
|
334
416
|
}
|
|
335
417
|
|