dd-trace 5.31.0 → 5.33.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/LICENSE-3rdparty.csv +1 -0
- package/README.md +17 -14
- package/index.d.ts +11 -1
- package/package.json +6 -5
- package/packages/datadog-instrumentations/src/aws-sdk.js +4 -1
- package/packages/datadog-instrumentations/src/cucumber.js +31 -14
- package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -0
- package/packages/datadog-instrumentations/src/jest.js +105 -56
- package/packages/datadog-instrumentations/src/mocha/main.js +9 -4
- package/packages/datadog-instrumentations/src/mocha/utils.js +27 -9
- package/packages/datadog-instrumentations/src/mocha/worker.js +4 -2
- package/packages/datadog-instrumentations/src/node-serialize.js +22 -0
- package/packages/datadog-instrumentations/src/openai.js +2 -0
- package/packages/datadog-instrumentations/src/playwright.js +8 -3
- package/packages/datadog-instrumentations/src/vitest.js +134 -62
- package/packages/datadog-instrumentations/src/vm.js +49 -0
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/index.js +16 -0
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/tracing.js +63 -0
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +287 -0
- package/packages/datadog-plugin-aws-sdk/src/services/index.js +1 -0
- package/packages/datadog-plugin-cucumber/src/index.js +31 -31
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +19 -8
- package/packages/datadog-plugin-cypress/src/support.js +6 -2
- package/packages/datadog-plugin-fetch/src/index.js +3 -3
- package/packages/datadog-plugin-http/src/client.js +5 -33
- package/packages/datadog-plugin-jest/src/index.js +37 -37
- package/packages/datadog-plugin-langchain/src/index.js +12 -80
- package/packages/datadog-plugin-langchain/src/tracing.js +89 -0
- package/packages/datadog-plugin-mocha/src/index.js +19 -35
- package/packages/datadog-plugin-playwright/src/index.js +3 -1
- package/packages/datadog-plugin-vitest/src/index.js +33 -35
- package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/code-injection-analyzer.js +2 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/cookie-analyzer.js +3 -3
- package/packages/dd-trace/src/appsec/iast/analyzers/untrusted-deserialization-analyzer.js +16 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +41 -24
- package/packages/dd-trace/src/appsec/iast/iast-context.js +12 -0
- package/packages/dd-trace/src/appsec/iast/path-line.js +19 -23
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +9 -8
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +1 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +75 -24
- package/packages/dd-trace/src/appsec/rasp/utils.js +10 -5
- package/packages/dd-trace/src/appsec/stack_trace.js +38 -28
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +37 -0
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +65 -28
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +57 -17
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +5 -4
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +18 -3
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +5 -3
- package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +20 -3
- package/packages/dd-trace/src/config.js +43 -3
- package/packages/dd-trace/src/crashtracking/crashtracker.js +9 -0
- package/packages/dd-trace/src/crashtracking/noop.js +3 -0
- package/packages/dd-trace/src/datastreams/fnv.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +2 -2
- package/packages/dd-trace/src/debugger/devtools_client/config.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/defaults.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/index.js +30 -13
- package/packages/dd-trace/src/debugger/devtools_client/send.js +4 -8
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +35 -1
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/redaction.js +112 -0
- package/packages/dd-trace/src/debugger/devtools_client/status.js +12 -10
- package/packages/dd-trace/src/debugger/index.js +2 -13
- package/packages/dd-trace/src/llmobs/plugins/base.js +40 -11
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +59 -0
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chain.js +24 -0
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chat_model.js +111 -0
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/embedding.js +42 -0
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +102 -0
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/llm.js +32 -0
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +131 -0
- package/packages/dd-trace/src/llmobs/plugins/openai.js +1 -1
- package/packages/dd-trace/src/llmobs/tagger.js +11 -3
- package/packages/dd-trace/src/llmobs/util.js +7 -1
- package/packages/dd-trace/src/llmobs/writers/spans/agentProxy.js +3 -3
- package/packages/dd-trace/src/opentelemetry/context_manager.js +43 -3
- package/packages/dd-trace/src/plugins/ci_plugin.js +58 -27
- package/packages/dd-trace/src/plugins/util/inferred_proxy.js +0 -2
- package/packages/dd-trace/src/plugins/util/test.js +44 -12
- package/packages/dd-trace/src/priority_sampler.js +4 -1
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +21 -0
- package/packages/dd-trace/src/profiling/profiler.js +11 -8
- package/packages/dd-trace/src/profiling/profilers/events.js +17 -1
- package/packages/dd-trace/src/proxy.js +6 -3
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const BaseAwsSdkPlugin = require('../../base')
|
|
4
|
+
const { parseModelId, extractRequestParams, extractTextAndResponseReason } = require('./utils')
|
|
5
|
+
|
|
6
|
+
const enabledOperations = ['invokeModel']
|
|
7
|
+
|
|
8
|
+
class BedrockRuntime extends BaseAwsSdkPlugin {
|
|
9
|
+
static get id () { return 'bedrockruntime' }
|
|
10
|
+
|
|
11
|
+
isEnabled (request) {
|
|
12
|
+
const operation = request.operation
|
|
13
|
+
if (!enabledOperations.includes(operation)) {
|
|
14
|
+
return false
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return super.isEnabled(request)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
generateTags (params, operation, response) {
|
|
21
|
+
const { modelProvider, modelName } = parseModelId(params.modelId)
|
|
22
|
+
|
|
23
|
+
const requestParams = extractRequestParams(params, modelProvider)
|
|
24
|
+
const textAndResponseReason = extractTextAndResponseReason(response, modelProvider, modelName)
|
|
25
|
+
|
|
26
|
+
const tags = buildTagsFromParams(requestParams, textAndResponseReason, modelProvider, modelName, operation)
|
|
27
|
+
|
|
28
|
+
return tags
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function buildTagsFromParams (requestParams, textAndResponseReason, modelProvider, modelName, operation) {
|
|
33
|
+
const tags = {}
|
|
34
|
+
|
|
35
|
+
// add request tags
|
|
36
|
+
tags['resource.name'] = operation
|
|
37
|
+
tags['aws.bedrock.request.model'] = modelName
|
|
38
|
+
tags['aws.bedrock.request.model_provider'] = modelProvider.toLowerCase()
|
|
39
|
+
tags['aws.bedrock.request.prompt'] = requestParams.prompt
|
|
40
|
+
tags['aws.bedrock.request.temperature'] = requestParams.temperature
|
|
41
|
+
tags['aws.bedrock.request.top_p'] = requestParams.topP
|
|
42
|
+
tags['aws.bedrock.request.top_k'] = requestParams.topK
|
|
43
|
+
tags['aws.bedrock.request.max_tokens'] = requestParams.maxTokens
|
|
44
|
+
tags['aws.bedrock.request.stop_sequences'] = requestParams.stopSequences
|
|
45
|
+
tags['aws.bedrock.request.input_type'] = requestParams.inputType
|
|
46
|
+
tags['aws.bedrock.request.truncate'] = requestParams.truncate
|
|
47
|
+
tags['aws.bedrock.request.stream'] = requestParams.stream
|
|
48
|
+
tags['aws.bedrock.request.n'] = requestParams.n
|
|
49
|
+
|
|
50
|
+
// add response tags
|
|
51
|
+
if (modelName.includes('embed')) {
|
|
52
|
+
tags['aws.bedrock.response.embedding_length'] = textAndResponseReason.message.length
|
|
53
|
+
}
|
|
54
|
+
if (textAndResponseReason.choiceId) {
|
|
55
|
+
tags['aws.bedrock.response.choices.id'] = textAndResponseReason.choiceId
|
|
56
|
+
}
|
|
57
|
+
tags['aws.bedrock.response.choices.text'] = textAndResponseReason.message
|
|
58
|
+
tags['aws.bedrock.response.choices.finish_reason'] = textAndResponseReason.finishReason
|
|
59
|
+
|
|
60
|
+
return tags
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
module.exports = BedrockRuntime
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const log = require('../../../../dd-trace/src/log')
|
|
4
|
+
|
|
5
|
+
const MODEL_TYPE_IDENTIFIERS = [
|
|
6
|
+
'foundation-model/',
|
|
7
|
+
'custom-model/',
|
|
8
|
+
'provisioned-model/',
|
|
9
|
+
'imported-module/',
|
|
10
|
+
'prompt/',
|
|
11
|
+
'endpoint/',
|
|
12
|
+
'inference-profile/',
|
|
13
|
+
'default-prompt-router/'
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
const PROVIDER = {
|
|
17
|
+
AI21: 'AI21',
|
|
18
|
+
AMAZON: 'AMAZON',
|
|
19
|
+
ANTHROPIC: 'ANTHROPIC',
|
|
20
|
+
COHERE: 'COHERE',
|
|
21
|
+
META: 'META',
|
|
22
|
+
STABILITY: 'STABILITY',
|
|
23
|
+
MISTRAL: 'MISTRAL'
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
class Generation {
|
|
27
|
+
constructor ({ message = '', finishReason = '', choiceId = '' } = {}) {
|
|
28
|
+
// stringify message as it could be a single generated message as well as a list of embeddings
|
|
29
|
+
this.message = typeof message === 'string' ? message : JSON.stringify(message) || ''
|
|
30
|
+
this.finishReason = finishReason || ''
|
|
31
|
+
this.choiceId = choiceId || undefined
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
class RequestParams {
|
|
36
|
+
constructor ({
|
|
37
|
+
prompt = '',
|
|
38
|
+
temperature = undefined,
|
|
39
|
+
topP = undefined,
|
|
40
|
+
topK = undefined,
|
|
41
|
+
maxTokens = undefined,
|
|
42
|
+
stopSequences = [],
|
|
43
|
+
inputType = '',
|
|
44
|
+
truncate = '',
|
|
45
|
+
stream = '',
|
|
46
|
+
n = undefined
|
|
47
|
+
} = {}) {
|
|
48
|
+
// stringify prompt as it could be a single prompt as well as a list of message objects
|
|
49
|
+
this.prompt = typeof prompt === 'string' ? prompt : JSON.stringify(prompt) || ''
|
|
50
|
+
this.temperature = temperature !== undefined ? temperature : undefined
|
|
51
|
+
this.topP = topP !== undefined ? topP : undefined
|
|
52
|
+
this.topK = topK !== undefined ? topK : undefined
|
|
53
|
+
this.maxTokens = maxTokens !== undefined ? maxTokens : undefined
|
|
54
|
+
this.stopSequences = stopSequences || []
|
|
55
|
+
this.inputType = inputType || ''
|
|
56
|
+
this.truncate = truncate || ''
|
|
57
|
+
this.stream = stream || ''
|
|
58
|
+
this.n = n !== undefined ? n : undefined
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function parseModelId (modelId) {
|
|
63
|
+
// Best effort to extract the model provider and model name from the bedrock model ID.
|
|
64
|
+
// modelId can be a 1/2 period-separated string or a full AWS ARN, based on the following formats:
|
|
65
|
+
// 1. Base model: "{model_provider}.{model_name}"
|
|
66
|
+
// 2. Cross-region model: "{region}.{model_provider}.{model_name}"
|
|
67
|
+
// 3. Other: Prefixed by AWS ARN "arn:aws{+region?}:bedrock:{region}:{account-id}:"
|
|
68
|
+
// a. Foundation model: ARN prefix + "foundation-model/{region?}.{model_provider}.{model_name}"
|
|
69
|
+
// b. Custom model: ARN prefix + "custom-model/{model_provider}.{model_name}"
|
|
70
|
+
// c. Provisioned model: ARN prefix + "provisioned-model/{model-id}"
|
|
71
|
+
// d. Imported model: ARN prefix + "imported-module/{model-id}"
|
|
72
|
+
// e. Prompt management: ARN prefix + "prompt/{prompt-id}"
|
|
73
|
+
// f. Sagemaker: ARN prefix + "endpoint/{model-id}"
|
|
74
|
+
// g. Inference profile: ARN prefix + "{application-?}inference-profile/{model-id}"
|
|
75
|
+
// h. Default prompt router: ARN prefix + "default-prompt-router/{prompt-id}"
|
|
76
|
+
// If model provider cannot be inferred from the modelId formatting, then default to "custom"
|
|
77
|
+
modelId = modelId.toLowerCase()
|
|
78
|
+
if (!modelId.startsWith('arn:aws')) {
|
|
79
|
+
const modelMeta = modelId.split('.')
|
|
80
|
+
if (modelMeta.length < 2) {
|
|
81
|
+
return { modelProvider: 'custom', modelName: modelMeta[0] }
|
|
82
|
+
}
|
|
83
|
+
return { modelProvider: modelMeta[modelMeta.length - 2], modelName: modelMeta[modelMeta.length - 1] }
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
for (const identifier of MODEL_TYPE_IDENTIFIERS) {
|
|
87
|
+
if (!modelId.includes(identifier)) {
|
|
88
|
+
continue
|
|
89
|
+
}
|
|
90
|
+
modelId = modelId.split(identifier).pop()
|
|
91
|
+
if (['foundation-model/', 'custom-model/'].includes(identifier)) {
|
|
92
|
+
const modelMeta = modelId.split('.')
|
|
93
|
+
if (modelMeta.length < 2) {
|
|
94
|
+
return { modelProvider: 'custom', modelName: modelId }
|
|
95
|
+
}
|
|
96
|
+
return { modelProvider: modelMeta[modelMeta.length - 2], modelName: modelMeta[modelMeta.length - 1] }
|
|
97
|
+
}
|
|
98
|
+
return { modelProvider: 'custom', modelName: modelId }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return { modelProvider: 'custom', modelName: 'custom' }
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function extractRequestParams (params, provider) {
|
|
105
|
+
const requestBody = JSON.parse(params.body)
|
|
106
|
+
const modelId = params.modelId
|
|
107
|
+
|
|
108
|
+
switch (provider.toUpperCase()) {
|
|
109
|
+
case PROVIDER.AI21: {
|
|
110
|
+
let userPrompt = requestBody.prompt
|
|
111
|
+
if (modelId.includes('jamba')) {
|
|
112
|
+
for (const message of requestBody.messages) {
|
|
113
|
+
if (message.role === 'user') {
|
|
114
|
+
userPrompt = message.content // Return the content of the most recent user message
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return new RequestParams({
|
|
119
|
+
prompt: userPrompt,
|
|
120
|
+
temperature: requestBody.temperature,
|
|
121
|
+
topP: requestBody.top_p,
|
|
122
|
+
maxTokens: requestBody.max_tokens,
|
|
123
|
+
stopSequences: requestBody.stop_sequences
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
case PROVIDER.AMAZON: {
|
|
127
|
+
if (modelId.includes('embed')) {
|
|
128
|
+
return new RequestParams({ prompt: requestBody.inputText })
|
|
129
|
+
}
|
|
130
|
+
const textGenerationConfig = requestBody.textGenerationConfig || {}
|
|
131
|
+
return new RequestParams({
|
|
132
|
+
prompt: requestBody.inputText,
|
|
133
|
+
temperature: textGenerationConfig.temperature,
|
|
134
|
+
topP: textGenerationConfig.topP,
|
|
135
|
+
maxTokens: textGenerationConfig.maxTokenCount,
|
|
136
|
+
stopSequences: textGenerationConfig.stopSequences
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
case PROVIDER.ANTHROPIC: {
|
|
140
|
+
const prompt = requestBody.prompt || requestBody.messages
|
|
141
|
+
return new RequestParams({
|
|
142
|
+
prompt,
|
|
143
|
+
temperature: requestBody.temperature,
|
|
144
|
+
topP: requestBody.top_p,
|
|
145
|
+
maxTokens: requestBody.max_tokens_to_sample,
|
|
146
|
+
stopSequences: requestBody.stop_sequences
|
|
147
|
+
})
|
|
148
|
+
}
|
|
149
|
+
case PROVIDER.COHERE: {
|
|
150
|
+
if (modelId.includes('embed')) {
|
|
151
|
+
return new RequestParams({
|
|
152
|
+
prompt: requestBody.texts,
|
|
153
|
+
inputType: requestBody.input_type,
|
|
154
|
+
truncate: requestBody.truncate
|
|
155
|
+
})
|
|
156
|
+
}
|
|
157
|
+
return new RequestParams({
|
|
158
|
+
prompt: requestBody.prompt,
|
|
159
|
+
temperature: requestBody.temperature,
|
|
160
|
+
topP: requestBody.p,
|
|
161
|
+
maxTokens: requestBody.max_tokens,
|
|
162
|
+
stopSequences: requestBody.stop_sequences,
|
|
163
|
+
stream: requestBody.stream,
|
|
164
|
+
n: requestBody.num_generations
|
|
165
|
+
})
|
|
166
|
+
}
|
|
167
|
+
case PROVIDER.META: {
|
|
168
|
+
return new RequestParams({
|
|
169
|
+
prompt: requestBody.prompt,
|
|
170
|
+
temperature: requestBody.temperature,
|
|
171
|
+
topP: requestBody.top_p,
|
|
172
|
+
maxTokens: requestBody.max_gen_len
|
|
173
|
+
})
|
|
174
|
+
}
|
|
175
|
+
case PROVIDER.MISTRAL: {
|
|
176
|
+
return new RequestParams({
|
|
177
|
+
prompt: requestBody.prompt,
|
|
178
|
+
temperature: requestBody.temperature,
|
|
179
|
+
topP: requestBody.top_p,
|
|
180
|
+
maxTokens: requestBody.max_tokens,
|
|
181
|
+
stopSequences: requestBody.stop,
|
|
182
|
+
topK: requestBody.top_k
|
|
183
|
+
})
|
|
184
|
+
}
|
|
185
|
+
case PROVIDER.STABILITY: {
|
|
186
|
+
return new RequestParams()
|
|
187
|
+
}
|
|
188
|
+
default: {
|
|
189
|
+
return new RequestParams()
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function extractTextAndResponseReason (response, provider, modelName) {
|
|
195
|
+
const body = JSON.parse(Buffer.from(response.body).toString('utf8'))
|
|
196
|
+
const shouldSetChoiceIds = provider.toUpperCase() === PROVIDER.COHERE && !modelName.includes('embed')
|
|
197
|
+
try {
|
|
198
|
+
switch (provider.toUpperCase()) {
|
|
199
|
+
case PROVIDER.AI21: {
|
|
200
|
+
if (modelName.includes('jamba')) {
|
|
201
|
+
const generations = body.choices || []
|
|
202
|
+
if (generations.length > 0) {
|
|
203
|
+
const generation = generations[0]
|
|
204
|
+
return new Generation({
|
|
205
|
+
message: generation.message,
|
|
206
|
+
finishReason: generation.finish_reason,
|
|
207
|
+
choiceId: shouldSetChoiceIds ? generation.id : undefined
|
|
208
|
+
})
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
const completions = body.completions || []
|
|
212
|
+
if (completions.length > 0) {
|
|
213
|
+
const completion = completions[0]
|
|
214
|
+
return new Generation({
|
|
215
|
+
message: completion.data?.text,
|
|
216
|
+
finishReason: completion?.finishReason,
|
|
217
|
+
choiceId: shouldSetChoiceIds ? completion?.id : undefined
|
|
218
|
+
})
|
|
219
|
+
}
|
|
220
|
+
return new Generation()
|
|
221
|
+
}
|
|
222
|
+
case PROVIDER.AMAZON: {
|
|
223
|
+
if (modelName.includes('embed')) {
|
|
224
|
+
return new Generation({ message: body.embedding })
|
|
225
|
+
}
|
|
226
|
+
const results = body.results || []
|
|
227
|
+
if (results.length > 0) {
|
|
228
|
+
const result = results[0]
|
|
229
|
+
return new Generation({ message: result.outputText, finishReason: result.completionReason })
|
|
230
|
+
}
|
|
231
|
+
break
|
|
232
|
+
}
|
|
233
|
+
case PROVIDER.ANTHROPIC: {
|
|
234
|
+
return new Generation({ message: body.completion || body.content, finishReason: body.stop_reason })
|
|
235
|
+
}
|
|
236
|
+
case PROVIDER.COHERE: {
|
|
237
|
+
if (modelName.includes('embed')) {
|
|
238
|
+
const embeddings = body.embeddings || [[]]
|
|
239
|
+
if (embeddings.length > 0) {
|
|
240
|
+
return new Generation({ message: embeddings[0] })
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
const generations = body.generations || []
|
|
244
|
+
if (generations.length > 0) {
|
|
245
|
+
const generation = generations[0]
|
|
246
|
+
return new Generation({
|
|
247
|
+
message: generation.text,
|
|
248
|
+
finishReason: generation.finish_reason,
|
|
249
|
+
choiceId: shouldSetChoiceIds ? generation.id : undefined
|
|
250
|
+
})
|
|
251
|
+
}
|
|
252
|
+
break
|
|
253
|
+
}
|
|
254
|
+
case PROVIDER.META: {
|
|
255
|
+
return new Generation({ message: body.generation, finishReason: body.stop_reason })
|
|
256
|
+
}
|
|
257
|
+
case PROVIDER.MISTRAL: {
|
|
258
|
+
const mistralGenerations = body.outputs || []
|
|
259
|
+
if (mistralGenerations.length > 0) {
|
|
260
|
+
const generation = mistralGenerations[0]
|
|
261
|
+
return new Generation({ message: generation.text, finishReason: generation.stop_reason })
|
|
262
|
+
}
|
|
263
|
+
break
|
|
264
|
+
}
|
|
265
|
+
case PROVIDER.STABILITY: {
|
|
266
|
+
return new Generation()
|
|
267
|
+
}
|
|
268
|
+
default: {
|
|
269
|
+
return new Generation()
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
} catch (error) {
|
|
273
|
+
log.warn('Unable to extract text/finishReason from response body. Defaulting to empty text/finishReason.')
|
|
274
|
+
return new Generation()
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return new Generation()
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
module.exports = {
|
|
281
|
+
Generation,
|
|
282
|
+
RequestParams,
|
|
283
|
+
parseModelId,
|
|
284
|
+
extractRequestParams,
|
|
285
|
+
extractTextAndResponseReason,
|
|
286
|
+
PROVIDER
|
|
287
|
+
}
|
|
@@ -27,11 +27,7 @@ const {
|
|
|
27
27
|
TEST_MODULE_ID,
|
|
28
28
|
TEST_SUITE,
|
|
29
29
|
CUCUMBER_IS_PARALLEL,
|
|
30
|
-
|
|
31
|
-
DI_ERROR_DEBUG_INFO_CAPTURED,
|
|
32
|
-
DI_DEBUG_ERROR_SNAPSHOT_ID,
|
|
33
|
-
DI_DEBUG_ERROR_FILE,
|
|
34
|
-
DI_DEBUG_ERROR_LINE
|
|
30
|
+
TEST_RETRY_REASON
|
|
35
31
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
36
32
|
const { RESOURCE_NAME } = require('../../../ext/tags')
|
|
37
33
|
const { COMPONENT, ERROR_MESSAGE } = require('../../dd-trace/src/constants')
|
|
@@ -50,8 +46,8 @@ const {
|
|
|
50
46
|
} = require('../../dd-trace/src/ci-visibility/telemetry')
|
|
51
47
|
const id = require('../../dd-trace/src/id')
|
|
52
48
|
|
|
49
|
+
const BREAKPOINT_HIT_GRACE_PERIOD_MS = 200
|
|
53
50
|
const isCucumberWorker = !!process.env.CUCUMBER_WORKER_ID
|
|
54
|
-
const debuggerParameterPerTest = new Map()
|
|
55
51
|
|
|
56
52
|
function getTestSuiteTags (testSuiteSpan) {
|
|
57
53
|
const suiteTags = {
|
|
@@ -210,7 +206,13 @@ class CucumberPlugin extends CiPlugin {
|
|
|
210
206
|
this.telemetry.ciVisEvent(TELEMETRY_CODE_COVERAGE_FINISHED, 'suite', { library: 'istanbul' })
|
|
211
207
|
})
|
|
212
208
|
|
|
213
|
-
this.addSub('ci:cucumber:test:start', ({
|
|
209
|
+
this.addSub('ci:cucumber:test:start', ({
|
|
210
|
+
testName,
|
|
211
|
+
testFileAbsolutePath,
|
|
212
|
+
testSourceLine,
|
|
213
|
+
isParallel,
|
|
214
|
+
promises
|
|
215
|
+
}) => {
|
|
214
216
|
const store = storage.getStore()
|
|
215
217
|
const testSuite = getTestSuitePath(testFileAbsolutePath, this.sourceRoot)
|
|
216
218
|
const testSourceFile = getTestSuitePath(testFileAbsolutePath, this.repositoryRoot)
|
|
@@ -227,38 +229,30 @@ class CucumberPlugin extends CiPlugin {
|
|
|
227
229
|
|
|
228
230
|
this.enter(testSpan, store)
|
|
229
231
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
if (
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
this.retriedTestIds = {
|
|
237
|
-
spanId: spanContext.toSpanId(),
|
|
238
|
-
traceId: spanContext.toTraceId()
|
|
239
|
-
}
|
|
240
|
-
const { snapshotId, file, line } = debuggerParameters
|
|
241
|
-
|
|
242
|
-
// TODO: should these be added on test:end if and only if the probe is hit?
|
|
243
|
-
// Sync issues: `hitProbePromise` might be resolved after the test ends
|
|
244
|
-
testSpan.setTag(DI_ERROR_DEBUG_INFO_CAPTURED, 'true')
|
|
245
|
-
testSpan.setTag(DI_DEBUG_ERROR_SNAPSHOT_ID, snapshotId)
|
|
246
|
-
testSpan.setTag(DI_DEBUG_ERROR_FILE, file)
|
|
247
|
-
testSpan.setTag(DI_DEBUG_ERROR_LINE, line)
|
|
232
|
+
this.activeTestSpan = testSpan
|
|
233
|
+
// Time we give the breakpoint to be hit
|
|
234
|
+
if (promises && this.runningTestProbeId) {
|
|
235
|
+
promises.hitBreakpointPromise = new Promise((resolve) => {
|
|
236
|
+
setTimeout(resolve, BREAKPOINT_HIT_GRACE_PERIOD_MS)
|
|
237
|
+
})
|
|
248
238
|
}
|
|
249
239
|
})
|
|
250
240
|
|
|
251
|
-
this.addSub('ci:cucumber:test:retry', ({
|
|
241
|
+
this.addSub('ci:cucumber:test:retry', ({ isFirstAttempt, error }) => {
|
|
252
242
|
const store = storage.getStore()
|
|
253
243
|
const span = store.span
|
|
254
|
-
if (
|
|
244
|
+
if (!isFirstAttempt) {
|
|
255
245
|
span.setTag(TEST_IS_RETRY, 'true')
|
|
256
246
|
}
|
|
257
247
|
span.setTag('error', error)
|
|
258
|
-
if (this.di && error && this.libraryConfig?.isDiEnabled) {
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
248
|
+
if (isFirstAttempt && this.di && error && this.libraryConfig?.isDiEnabled) {
|
|
249
|
+
const probeInformation = this.addDiProbe(error)
|
|
250
|
+
if (probeInformation) {
|
|
251
|
+
const { probeId, stackIndex } = probeInformation
|
|
252
|
+
this.runningTestProbeId = probeId
|
|
253
|
+
this.testErrorStackIndex = stackIndex
|
|
254
|
+
// TODO: we're not waiting for setProbePromise to be resolved, so there might be race conditions
|
|
255
|
+
}
|
|
262
256
|
}
|
|
263
257
|
span.setTag(TEST_STATUS, 'fail')
|
|
264
258
|
span.finish()
|
|
@@ -328,6 +322,7 @@ class CucumberPlugin extends CiPlugin {
|
|
|
328
322
|
span.setTag(TEST_IS_NEW, 'true')
|
|
329
323
|
if (isEfdRetry) {
|
|
330
324
|
span.setTag(TEST_IS_RETRY, 'true')
|
|
325
|
+
span.setTag(TEST_RETRY_REASON, 'efd')
|
|
331
326
|
}
|
|
332
327
|
}
|
|
333
328
|
|
|
@@ -363,6 +358,11 @@ class CucumberPlugin extends CiPlugin {
|
|
|
363
358
|
if (isCucumberWorker) {
|
|
364
359
|
this.tracer._exporter.flush()
|
|
365
360
|
}
|
|
361
|
+
this.activeTestSpan = null
|
|
362
|
+
if (this.runningTestProbeId) {
|
|
363
|
+
this.removeDiProbe(this.runningTestProbeId)
|
|
364
|
+
this.runningTestProbeId = null
|
|
365
|
+
}
|
|
366
366
|
}
|
|
367
367
|
})
|
|
368
368
|
|
|
@@ -31,7 +31,8 @@ const {
|
|
|
31
31
|
TEST_EARLY_FLAKE_ENABLED,
|
|
32
32
|
getTestSessionName,
|
|
33
33
|
TEST_SESSION_NAME,
|
|
34
|
-
TEST_LEVEL_EVENT_TYPES
|
|
34
|
+
TEST_LEVEL_EVENT_TYPES,
|
|
35
|
+
TEST_RETRY_REASON
|
|
35
36
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
36
37
|
const { isMarkedAsUnskippable } = require('../../datadog-plugin-jest/src/util')
|
|
37
38
|
const { ORIGIN_KEY, COMPONENT } = require('../../dd-trace/src/constants')
|
|
@@ -112,7 +113,7 @@ function getCypressCommand (details) {
|
|
|
112
113
|
function getLibraryConfiguration (tracer, testConfiguration) {
|
|
113
114
|
return new Promise(resolve => {
|
|
114
115
|
if (!tracer._tracer._exporter?.getLibraryConfiguration) {
|
|
115
|
-
return resolve({ err: new Error('
|
|
116
|
+
return resolve({ err: new Error('Test Optimization was not initialized correctly') })
|
|
116
117
|
}
|
|
117
118
|
|
|
118
119
|
tracer._tracer._exporter.getLibraryConfiguration(testConfiguration, (err, libraryConfig) => {
|
|
@@ -124,7 +125,7 @@ function getLibraryConfiguration (tracer, testConfiguration) {
|
|
|
124
125
|
function getSkippableTests (tracer, testConfiguration) {
|
|
125
126
|
return new Promise(resolve => {
|
|
126
127
|
if (!tracer._tracer._exporter?.getSkippableSuites) {
|
|
127
|
-
return resolve({ err: new Error('
|
|
128
|
+
return resolve({ err: new Error('Test Optimization was not initialized correctly') })
|
|
128
129
|
}
|
|
129
130
|
tracer._tracer._exporter.getSkippableSuites(testConfiguration, (err, skippableTests, correlationId) => {
|
|
130
131
|
resolve({
|
|
@@ -139,7 +140,7 @@ function getSkippableTests (tracer, testConfiguration) {
|
|
|
139
140
|
function getKnownTests (tracer, testConfiguration) {
|
|
140
141
|
return new Promise(resolve => {
|
|
141
142
|
if (!tracer._tracer._exporter?.getKnownTests) {
|
|
142
|
-
return resolve({ err: new Error('
|
|
143
|
+
return resolve({ err: new Error('Test Optimization was not initialized correctly') })
|
|
143
144
|
}
|
|
144
145
|
tracer._tracer._exporter.getKnownTests(testConfiguration, (err, knownTests) => {
|
|
145
146
|
resolve({
|
|
@@ -203,6 +204,7 @@ class CypressPlugin {
|
|
|
203
204
|
this.isSuitesSkippingEnabled = false
|
|
204
205
|
this.isCodeCoverageEnabled = false
|
|
205
206
|
this.isEarlyFlakeDetectionEnabled = false
|
|
207
|
+
this.isKnownTestsEnabled = false
|
|
206
208
|
this.earlyFlakeDetectionNumRetries = 0
|
|
207
209
|
this.testsToSkip = []
|
|
208
210
|
this.skippedTests = []
|
|
@@ -232,13 +234,15 @@ class CypressPlugin {
|
|
|
232
234
|
isEarlyFlakeDetectionEnabled,
|
|
233
235
|
earlyFlakeDetectionNumRetries,
|
|
234
236
|
isFlakyTestRetriesEnabled,
|
|
235
|
-
flakyTestRetriesCount
|
|
237
|
+
flakyTestRetriesCount,
|
|
238
|
+
isKnownTestsEnabled
|
|
236
239
|
}
|
|
237
240
|
} = libraryConfigurationResponse
|
|
238
241
|
this.isSuitesSkippingEnabled = isSuitesSkippingEnabled
|
|
239
242
|
this.isCodeCoverageEnabled = isCodeCoverageEnabled
|
|
240
243
|
this.isEarlyFlakeDetectionEnabled = isEarlyFlakeDetectionEnabled
|
|
241
244
|
this.earlyFlakeDetectionNumRetries = earlyFlakeDetectionNumRetries
|
|
245
|
+
this.isKnownTestsEnabled = isKnownTestsEnabled
|
|
242
246
|
if (isFlakyTestRetriesEnabled) {
|
|
243
247
|
this.cypressConfig.retries.runMode = flakyTestRetriesCount
|
|
244
248
|
}
|
|
@@ -354,7 +358,7 @@ class CypressPlugin {
|
|
|
354
358
|
this.frameworkVersion = getCypressVersion(details)
|
|
355
359
|
this.rootDir = getRootDir(details)
|
|
356
360
|
|
|
357
|
-
if (this.
|
|
361
|
+
if (this.isKnownTestsEnabled) {
|
|
358
362
|
const knownTestsResponse = await getKnownTests(
|
|
359
363
|
this.tracer,
|
|
360
364
|
this.testConfiguration
|
|
@@ -362,6 +366,7 @@ class CypressPlugin {
|
|
|
362
366
|
if (knownTestsResponse.err) {
|
|
363
367
|
log.error('Cypress known tests response error', knownTestsResponse.err)
|
|
364
368
|
this.isEarlyFlakeDetectionEnabled = false
|
|
369
|
+
this.isKnownTestsEnabled = false
|
|
365
370
|
} else {
|
|
366
371
|
// We use TEST_FRAMEWORK_NAME for the name of the module
|
|
367
372
|
this.knownTestsByTestSuite = knownTestsResponse.knownTests[TEST_FRAMEWORK_NAME]
|
|
@@ -567,6 +572,9 @@ class CypressPlugin {
|
|
|
567
572
|
cypressTestStatus = CYPRESS_STATUS_TO_TEST_STATUS[cypressTest.attempts[attemptIndex].state]
|
|
568
573
|
if (attemptIndex > 0) {
|
|
569
574
|
finishedTest.testSpan.setTag(TEST_IS_RETRY, 'true')
|
|
575
|
+
if (finishedTest.isEfdRetry) {
|
|
576
|
+
finishedTest.testSpan.setTag(TEST_RETRY_REASON, 'efd')
|
|
577
|
+
}
|
|
570
578
|
}
|
|
571
579
|
}
|
|
572
580
|
if (cypressTest.displayError) {
|
|
@@ -618,7 +626,8 @@ class CypressPlugin {
|
|
|
618
626
|
const suitePayload = {
|
|
619
627
|
isEarlyFlakeDetectionEnabled: this.isEarlyFlakeDetectionEnabled,
|
|
620
628
|
knownTestsForSuite: this.knownTestsByTestSuite?.[testSuite] || [],
|
|
621
|
-
earlyFlakeDetectionNumRetries: this.earlyFlakeDetectionNumRetries
|
|
629
|
+
earlyFlakeDetectionNumRetries: this.earlyFlakeDetectionNumRetries,
|
|
630
|
+
isKnownTestsEnabled: this.isKnownTestsEnabled
|
|
622
631
|
}
|
|
623
632
|
|
|
624
633
|
if (this.testSuiteSpan) {
|
|
@@ -703,13 +712,15 @@ class CypressPlugin {
|
|
|
703
712
|
this.activeTestSpan.setTag(TEST_IS_NEW, 'true')
|
|
704
713
|
if (isEfdRetry) {
|
|
705
714
|
this.activeTestSpan.setTag(TEST_IS_RETRY, 'true')
|
|
715
|
+
this.activeTestSpan.setTag(TEST_RETRY_REASON, 'efd')
|
|
706
716
|
}
|
|
707
717
|
}
|
|
708
718
|
const finishedTest = {
|
|
709
719
|
testName,
|
|
710
720
|
testStatus,
|
|
711
721
|
finishTime: this.activeTestSpan._getTime(), // we store the finish time here
|
|
712
|
-
testSpan: this.activeTestSpan
|
|
722
|
+
testSpan: this.activeTestSpan,
|
|
723
|
+
isEfdRetry
|
|
713
724
|
}
|
|
714
725
|
if (this.finishedTestsByFile[testSuite]) {
|
|
715
726
|
this.finishedTestsByFile[testSuite].push(finishedTest)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* eslint-disable */
|
|
2
2
|
let isEarlyFlakeDetectionEnabled = false
|
|
3
|
+
let isKnownTestsEnabled = false
|
|
3
4
|
let knownTestsForSuite = []
|
|
4
5
|
let suiteTests = []
|
|
5
6
|
let earlyFlakeDetectionNumRetries = 0
|
|
@@ -33,7 +34,7 @@ function retryTest (test, suiteTests) {
|
|
|
33
34
|
|
|
34
35
|
const oldRunTests = Cypress.mocha.getRunner().runTests
|
|
35
36
|
Cypress.mocha.getRunner().runTests = function (suite, fn) {
|
|
36
|
-
if (!
|
|
37
|
+
if (!isKnownTestsEnabled) {
|
|
37
38
|
return oldRunTests.apply(this, arguments)
|
|
38
39
|
}
|
|
39
40
|
// We copy the new tests at the beginning of the suite run (runTests), so that they're run
|
|
@@ -41,7 +42,9 @@ Cypress.mocha.getRunner().runTests = function (suite, fn) {
|
|
|
41
42
|
suite.tests.forEach(test => {
|
|
42
43
|
if (!test._ddIsNew && !test.isPending() && isNewTest(test)) {
|
|
43
44
|
test._ddIsNew = true
|
|
44
|
-
|
|
45
|
+
if (isEarlyFlakeDetectionEnabled) {
|
|
46
|
+
retryTest(test, suite.tests)
|
|
47
|
+
}
|
|
45
48
|
}
|
|
46
49
|
})
|
|
47
50
|
|
|
@@ -67,6 +70,7 @@ before(function () {
|
|
|
67
70
|
}).then((suiteConfig) => {
|
|
68
71
|
if (suiteConfig) {
|
|
69
72
|
isEarlyFlakeDetectionEnabled = suiteConfig.isEarlyFlakeDetectionEnabled
|
|
73
|
+
isKnownTestsEnabled = suiteConfig.isKnownTestsEnabled
|
|
70
74
|
knownTestsForSuite = suiteConfig.knownTestsForSuite
|
|
71
75
|
earlyFlakeDetectionNumRetries = suiteConfig.earlyFlakeDetectionNumRetries
|
|
72
76
|
}
|
|
@@ -9,7 +9,7 @@ class FetchPlugin extends HttpClientPlugin {
|
|
|
9
9
|
bindStart (ctx) {
|
|
10
10
|
const req = ctx.req
|
|
11
11
|
const options = new URL(req.url)
|
|
12
|
-
|
|
12
|
+
options.headers = Object.fromEntries(req.headers.entries())
|
|
13
13
|
|
|
14
14
|
options.method = req.method
|
|
15
15
|
|
|
@@ -17,9 +17,9 @@ class FetchPlugin extends HttpClientPlugin {
|
|
|
17
17
|
|
|
18
18
|
const store = super.bindStart(ctx)
|
|
19
19
|
|
|
20
|
-
for (const name in headers) {
|
|
20
|
+
for (const name in options.headers) {
|
|
21
21
|
if (!req.headers.has(name)) {
|
|
22
|
-
req.headers.set(name, headers[name])
|
|
22
|
+
req.headers.set(name, options.headers[name])
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
|