dd-trace 5.105.0 → 5.107.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/index.d.ts +20 -1
- package/package.json +5 -7
- package/packages/datadog-core/src/storage.js +47 -48
- package/packages/datadog-esbuild/index.js +6 -1
- package/packages/datadog-instrumentations/src/ai.js +12 -3
- package/packages/datadog-instrumentations/src/aws-sdk.js +3 -2
- package/packages/datadog-instrumentations/src/body-parser.js +5 -2
- package/packages/datadog-instrumentations/src/connect.js +3 -2
- package/packages/datadog-instrumentations/src/cookie-parser.js +3 -2
- package/packages/datadog-instrumentations/src/cucumber-worker-threads.js +19 -0
- package/packages/datadog-instrumentations/src/cucumber.js +319 -152
- package/packages/datadog-instrumentations/src/express-mongo-sanitize.js +7 -5
- package/packages/datadog-instrumentations/src/express-session.js +12 -11
- package/packages/datadog-instrumentations/src/express.js +24 -20
- package/packages/datadog-instrumentations/src/fastify.js +18 -6
- package/packages/datadog-instrumentations/src/helpers/openai-ai-guard.js +27 -12
- package/packages/datadog-instrumentations/src/http/client.js +9 -12
- package/packages/datadog-instrumentations/src/http/server.js +30 -16
- package/packages/datadog-instrumentations/src/http2/client.js +15 -12
- package/packages/datadog-instrumentations/src/http2/server.js +15 -8
- package/packages/datadog-instrumentations/src/jest/bail-reporter.js +42 -0
- package/packages/datadog-instrumentations/src/jest.js +143 -73
- package/packages/datadog-instrumentations/src/mocha/main.js +43 -8
- package/packages/datadog-instrumentations/src/mocha/utils.js +128 -17
- package/packages/datadog-instrumentations/src/multer.js +3 -2
- package/packages/datadog-instrumentations/src/mysql2.js +34 -0
- package/packages/datadog-instrumentations/src/net.js +8 -6
- package/packages/datadog-instrumentations/src/openai.js +19 -7
- package/packages/datadog-instrumentations/src/pg.js +19 -0
- package/packages/datadog-instrumentations/src/router.js +12 -10
- package/packages/datadog-instrumentations/src/vitest.js +29 -4
- package/packages/datadog-plugin-aws-sdk/src/base.js +0 -3
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/tracing.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +218 -4
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +62 -11
- package/packages/datadog-plugin-cucumber/src/index.js +2 -0
- package/packages/datadog-plugin-cypress/src/support.js +31 -1
- package/packages/datadog-plugin-http/src/client.js +0 -3
- package/packages/datadog-plugin-http/src/server.js +11 -1
- package/packages/datadog-plugin-mocha/src/index.js +2 -0
- package/packages/datadog-plugin-pg/src/index.js +10 -0
- package/packages/dd-trace/src/aiguard/index.js +34 -15
- package/packages/dd-trace/src/aiguard/sdk.js +34 -3
- package/packages/dd-trace/src/aiguard/tags.js +6 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +1 -1
- package/packages/dd-trace/src/config/defaults.js +14 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +1 -1
- package/packages/dd-trace/src/config/helper.js +1 -0
- package/packages/dd-trace/src/config/index.js +5 -9
- package/packages/dd-trace/src/config/parsers.js +8 -0
- package/packages/dd-trace/src/config/supported-configurations.json +13 -6
- package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -2
- package/packages/dd-trace/src/datastreams/writer.js +1 -2
- package/packages/dd-trace/src/debugger/config.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/config.js +3 -2
- package/packages/dd-trace/src/debugger/index.js +1 -2
- package/packages/dd-trace/src/dogstatsd.js +2 -3
- package/packages/dd-trace/src/encode/0.4.js +49 -41
- package/packages/dd-trace/src/encode/agentless-json.js +5 -1
- package/packages/dd-trace/src/encode/tags-processors.js +14 -0
- package/packages/dd-trace/src/exporters/agent/index.js +1 -2
- package/packages/dd-trace/src/exporters/agentless/index.js +6 -10
- package/packages/dd-trace/src/exporters/common/buffering-exporter.js +1 -2
- package/packages/dd-trace/src/exporters/common/request.js +26 -0
- package/packages/dd-trace/src/exporters/span-stats/index.js +1 -2
- package/packages/dd-trace/src/id.js +15 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +91 -5
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +43 -21
- package/packages/dd-trace/src/llmobs/plugins/genai/index.js +4 -0
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +45 -0
- package/packages/dd-trace/src/llmobs/sdk.js +4 -1
- package/packages/dd-trace/src/llmobs/span_processor.js +17 -1
- package/packages/dd-trace/src/llmobs/tagger.js +5 -3
- package/packages/dd-trace/src/llmobs/util.js +54 -0
- package/packages/dd-trace/src/llmobs/writers/base.js +1 -2
- package/packages/dd-trace/src/llmobs/writers/util.js +1 -2
- package/packages/dd-trace/src/openfeature/writers/base.js +1 -10
- package/packages/dd-trace/src/openfeature/writers/util.js +1 -2
- package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +26 -13
- package/packages/dd-trace/src/opentelemetry/metrics/meter.js +7 -10
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +92 -0
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +25 -5
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +2 -10
- package/packages/dd-trace/src/opentracing/span.js +23 -18
- package/packages/dd-trace/src/opentracing/span_context.js +1 -3
- package/packages/dd-trace/src/opentracing/tracer.js +16 -12
- package/packages/dd-trace/src/plugins/ci_plugin.js +131 -46
- package/packages/dd-trace/src/priority_sampler.js +6 -5
- package/packages/dd-trace/src/profiling/config.js +11 -25
- package/packages/dd-trace/src/profiling/exporters/agent.js +11 -10
- package/packages/dd-trace/src/profiling/profiler.js +19 -9
- package/packages/dd-trace/src/profiling/profilers/wall.js +2 -3
- package/packages/dd-trace/src/proxy.js +13 -10
- package/packages/dd-trace/src/remote_config/index.js +1 -2
- package/packages/dd-trace/src/runtime_metrics/client.js +30 -0
- package/packages/dd-trace/src/runtime_metrics/index.js +12 -2
- package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +284 -0
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +2 -11
- package/packages/dd-trace/src/service-naming/source-resolver.js +5 -1
- package/packages/dd-trace/src/span_format.js +33 -25
- package/packages/dd-trace/src/span_stats.js +1 -1
- package/packages/dd-trace/src/startup-log.js +1 -2
- package/packages/dd-trace/src/telemetry/send-data.js +1 -1
- package/packages/dd-trace/src/tracer.js +1 -1
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +2 -2
- package/vendor/dist/shell-quote/index.js +1 -1
- package/packages/dd-trace/src/agent/url.js +0 -28
- package/scripts/preinstall.js +0 -34
|
@@ -206,6 +206,32 @@ function wrapConnection (Connection, version) {
|
|
|
206
206
|
})
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* mysql2 defers a busy pool's `getConnection` callback and runs it in the releasing connection's
|
|
211
|
+
* async context; capture the caller's context and restore it around the callback so spans created
|
|
212
|
+
* by the queued query attach to the caller rather than the previous query that freed the connection.
|
|
213
|
+
*
|
|
214
|
+
* @param {Function} Pool
|
|
215
|
+
* @returns {Function}
|
|
216
|
+
*/
|
|
217
|
+
function wrapGetConnection (Pool) {
|
|
218
|
+
const connectionStartCh = channel('apm:mysql2:connection:start')
|
|
219
|
+
const connectionFinishCh = channel('apm:mysql2:connection:finish')
|
|
220
|
+
|
|
221
|
+
shimmer.wrap(Pool.prototype, 'getConnection', getConnection => function (cb) {
|
|
222
|
+
const ctx = {}
|
|
223
|
+
arguments[0] = function (...args) {
|
|
224
|
+
return connectionFinishCh.runStores(ctx, cb, this, ...args)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
connectionStartCh.publish(ctx)
|
|
228
|
+
|
|
229
|
+
return getConnection.apply(this, arguments)
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
return Pool
|
|
233
|
+
}
|
|
234
|
+
|
|
209
235
|
/**
|
|
210
236
|
* @param {Function} Pool
|
|
211
237
|
* @param {string} version
|
|
@@ -215,6 +241,8 @@ function wrapPool (Pool, version) {
|
|
|
215
241
|
const startOuterQueryCh = channel('datadog:mysql2:outerquery:start')
|
|
216
242
|
const shouldEmitEndAfterQueryAbort = satisfies(version, '>=1.3.3')
|
|
217
243
|
|
|
244
|
+
wrapGetConnection(Pool)
|
|
245
|
+
|
|
218
246
|
shimmer.wrap(Pool.prototype, 'query', query => function (sql, values, cb) {
|
|
219
247
|
if (!startOuterQueryCh.hasSubscribers) return query.apply(this, arguments)
|
|
220
248
|
|
|
@@ -373,6 +401,12 @@ addHook(
|
|
|
373
401
|
/** @type {(moduleExports: unknown, version: string) => unknown} */ (wrapPool)
|
|
374
402
|
)
|
|
375
403
|
|
|
404
|
+
// mysql2 >=3.11.5 moved the pool onto BasePool in lib/base/pool.js.
|
|
405
|
+
addHook(
|
|
406
|
+
{ name: 'mysql2', file: 'lib/base/pool.js', versions: ['>=3.11.5'] },
|
|
407
|
+
/** @type {(moduleExports: unknown, version: string) => unknown} */ (wrapGetConnection)
|
|
408
|
+
)
|
|
409
|
+
|
|
376
410
|
// PoolNamespace.prototype.query does not exist in mysql2<2.3.0
|
|
377
411
|
addHook(
|
|
378
412
|
{ name: 'mysql2', file: 'lib/pool_cluster.js', versions: ['2.3.0 - 3.11.4'] },
|
|
@@ -49,20 +49,22 @@ addHook({ name: 'net' }, (net) => {
|
|
|
49
49
|
|
|
50
50
|
const emit = this.emit
|
|
51
51
|
let pendingReadyEvents = 2
|
|
52
|
-
|
|
52
|
+
// Named `emit`/arity-1 mirrors the socket method so the per-socket wrap
|
|
53
|
+
// skips its name/length rewrite.
|
|
54
|
+
this.emit = shimmer.wrapFunction(emit, originalEmit => function emit (eventName) {
|
|
53
55
|
switch (eventName) {
|
|
54
56
|
case 'ready':
|
|
55
57
|
case 'connect':
|
|
56
|
-
if (--pendingReadyEvents === 0) this.emit =
|
|
58
|
+
if (--pendingReadyEvents === 0) this.emit = originalEmit
|
|
57
59
|
return readyCh.runStores(ctx, () => {
|
|
58
|
-
return
|
|
60
|
+
return Reflect.apply(originalEmit, this, arguments)
|
|
59
61
|
})
|
|
60
62
|
case 'error':
|
|
61
63
|
case 'close':
|
|
62
|
-
this.emit =
|
|
63
|
-
return
|
|
64
|
+
this.emit = originalEmit
|
|
65
|
+
return Reflect.apply(originalEmit, this, arguments)
|
|
64
66
|
default:
|
|
65
|
-
return
|
|
67
|
+
return Reflect.apply(originalEmit, this, arguments)
|
|
66
68
|
}
|
|
67
69
|
})
|
|
68
70
|
|
|
@@ -240,6 +240,10 @@ for (const extension of extensions) {
|
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
return ch.start.runStores(ctx, () => {
|
|
243
|
+
// Explicit childOf rather than async-context: the _thenUnwrap/parse path
|
|
244
|
+
// decouples the lazy evaluation from the active scope at call time.
|
|
245
|
+
if (guard) guard.parentSpan = ctx.currentStore?.span
|
|
246
|
+
|
|
243
247
|
const apiProm = methodFn.apply(this, args)
|
|
244
248
|
|
|
245
249
|
if (baseResource === 'chat.completions' && typeof apiProm._thenUnwrap === 'function') {
|
|
@@ -300,23 +304,31 @@ function handleUnwrappedAPIPromise (apiProm, ctx, stream, guard) {
|
|
|
300
304
|
return body
|
|
301
305
|
}
|
|
302
306
|
|
|
303
|
-
|
|
307
|
+
const responseData = {
|
|
304
308
|
headers: response.headers,
|
|
305
309
|
data: body,
|
|
306
310
|
request: {
|
|
307
311
|
path: response.url,
|
|
308
312
|
method: options.method,
|
|
309
313
|
},
|
|
310
|
-
}
|
|
314
|
+
}
|
|
311
315
|
|
|
312
|
-
if (!guard)
|
|
316
|
+
if (!guard) {
|
|
317
|
+
finish(ctx, responseData)
|
|
318
|
+
return body
|
|
319
|
+
}
|
|
313
320
|
|
|
314
|
-
|
|
321
|
+
// Finish after evaluation so a block propagates the error to openai.request
|
|
322
|
+
// and the span wraps its ai_guard child instead of closing before it.
|
|
323
|
+
return aiGuard.evaluateOutput(guard, body).then(() => {
|
|
324
|
+
finish(ctx, responseData)
|
|
325
|
+
return body
|
|
326
|
+
})
|
|
315
327
|
})
|
|
316
328
|
.catch(error => {
|
|
317
|
-
// ctx.result is set inside finish(); if absent, finish never ran (sync throw in
|
|
318
|
-
//
|
|
319
|
-
// If finish already ran
|
|
329
|
+
// ctx.result is set inside finish(); if absent, finish never ran (sync throw in the success
|
|
330
|
+
// branch, Before Model block, After Model block, or openai error) — record the error now so
|
|
331
|
+
// the openai.request span is marked errored. If finish already ran, don't double-publish.
|
|
320
332
|
if (!ctx.result) finish(ctx, undefined, error)
|
|
321
333
|
throw error
|
|
322
334
|
})
|
|
@@ -15,6 +15,9 @@ const errorCh = channel('apm:pg:query:error')
|
|
|
15
15
|
const startPoolQueryCh = channel('datadog:pg:pool:query:start')
|
|
16
16
|
const finishPoolQueryCh = channel('datadog:pg:pool:query:finish')
|
|
17
17
|
|
|
18
|
+
const poolConnectStartCh = channel('apm:pg:pool:connect:start')
|
|
19
|
+
const poolConnectFinishCh = channel('apm:pg:pool:connect:finish')
|
|
20
|
+
|
|
18
21
|
// Drivers like pg-promise reuse the same prepared-statement query object across executions; cache
|
|
19
22
|
// the un-injected `text` so the wrap doesn't capture a previous DBM injection as the new original.
|
|
20
23
|
const originalTextCache = new WeakMap()
|
|
@@ -30,6 +33,22 @@ addHook({ name: 'pg', versions: ['>=8.0.3'], file: 'lib/client.js' }, Client =>
|
|
|
30
33
|
})
|
|
31
34
|
|
|
32
35
|
addHook({ name: 'pg', versions: ['>=8.0.3'] }, pg => {
|
|
36
|
+
// pg defers a busy pool's connect callback and runs it in the releasing query's async context;
|
|
37
|
+
// capture the caller's context and restore it around the callback so spans attach to the caller.
|
|
38
|
+
shimmer.wrap(pg.Pool.prototype, 'connect', connect => function (cb) {
|
|
39
|
+
if (typeof cb !== 'function' || !poolConnectStartCh.hasSubscribers) {
|
|
40
|
+
return connect.apply(this, arguments)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const ctx = {}
|
|
44
|
+
arguments[0] = function (...args) {
|
|
45
|
+
return poolConnectFinishCh.runStores(ctx, cb, this, ...args)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
poolConnectStartCh.publish(ctx)
|
|
49
|
+
|
|
50
|
+
return connect.apply(this, arguments)
|
|
51
|
+
})
|
|
33
52
|
shimmer.wrap(pg.Pool.prototype, 'query', query => wrapPoolQuery(query))
|
|
34
53
|
return pg
|
|
35
54
|
})
|
|
@@ -157,9 +157,9 @@ function createWrapRouterMethod (name, compile) {
|
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
function wrapNext (req, originalNext) {
|
|
160
|
-
// Per layer dispatch, N per request. `
|
|
161
|
-
//
|
|
162
|
-
return shimmer.wrapCallback(originalNext,
|
|
160
|
+
// Per layer dispatch, N per request. Named `next`/arity-1 mirrors the
|
|
161
|
+
// router continuation so wrapCallback skips its name/length rewrite.
|
|
162
|
+
return shimmer.wrapCallback(originalNext, original => function next (error) {
|
|
163
163
|
if (error && error !== 'route' && error !== 'router') {
|
|
164
164
|
errorChannel.publish({ req, error })
|
|
165
165
|
}
|
|
@@ -167,7 +167,7 @@ function createWrapRouterMethod (name, compile) {
|
|
|
167
167
|
nextChannel.publish({ req })
|
|
168
168
|
finishChannel.publish({ req })
|
|
169
169
|
|
|
170
|
-
|
|
170
|
+
original.apply(this, arguments)
|
|
171
171
|
})
|
|
172
172
|
}
|
|
173
173
|
|
|
@@ -328,7 +328,8 @@ const routerParamStartCh = channel('datadog:router:param:start')
|
|
|
328
328
|
const visitedParams = new WeakSet()
|
|
329
329
|
|
|
330
330
|
function wrapHandleRequest (original) {
|
|
331
|
-
return function wrappedHandleRequest (
|
|
331
|
+
return function wrappedHandleRequest (...args) {
|
|
332
|
+
const req = args[0]
|
|
332
333
|
if (routerParamStartCh.hasSubscribers && !visitedParams.has(req.params) && Object.keys(req.params).length) {
|
|
333
334
|
visitedParams.add(req.params)
|
|
334
335
|
|
|
@@ -336,7 +337,7 @@ function wrapHandleRequest (original) {
|
|
|
336
337
|
|
|
337
338
|
routerParamStartCh.publish({
|
|
338
339
|
req,
|
|
339
|
-
res,
|
|
340
|
+
res: args[1],
|
|
340
341
|
params: req?.params,
|
|
341
342
|
abortController,
|
|
342
343
|
})
|
|
@@ -344,7 +345,7 @@ function wrapHandleRequest (original) {
|
|
|
344
345
|
if (abortController.signal.aborted) return
|
|
345
346
|
}
|
|
346
347
|
|
|
347
|
-
return
|
|
348
|
+
return Reflect.apply(original, this, args)
|
|
348
349
|
}
|
|
349
350
|
}
|
|
350
351
|
|
|
@@ -358,7 +359,8 @@ addHook({
|
|
|
358
359
|
function wrapParam (original) {
|
|
359
360
|
return function wrappedProcessParams (...args) {
|
|
360
361
|
args[1] = shimmer.wrapFunction(args[1], (originalFn) => {
|
|
361
|
-
return function wrappedFn (
|
|
362
|
+
return function wrappedFn (...fnArgs) {
|
|
363
|
+
const req = fnArgs[0]
|
|
362
364
|
if (routerParamStartCh.hasSubscribers && Object.keys(req.params).length && !visitedParams.has(req.params)) {
|
|
363
365
|
visitedParams.add(req.params)
|
|
364
366
|
|
|
@@ -366,7 +368,7 @@ function wrapParam (original) {
|
|
|
366
368
|
|
|
367
369
|
routerParamStartCh.publish({
|
|
368
370
|
req,
|
|
369
|
-
res,
|
|
371
|
+
res: fnArgs[1],
|
|
370
372
|
params: req?.params,
|
|
371
373
|
abortController,
|
|
372
374
|
})
|
|
@@ -374,7 +376,7 @@ function wrapParam (original) {
|
|
|
374
376
|
if (abortController.signal.aborted) return
|
|
375
377
|
}
|
|
376
378
|
|
|
377
|
-
return
|
|
379
|
+
return Reflect.apply(originalFn, this, fnArgs)
|
|
378
380
|
}
|
|
379
381
|
})
|
|
380
382
|
|
|
@@ -57,6 +57,7 @@ const codeCoverageReportCh = channel('ci:vitest:coverage-report')
|
|
|
57
57
|
|
|
58
58
|
const taskToCtx = new WeakMap()
|
|
59
59
|
const taskToStatuses = new WeakMap()
|
|
60
|
+
const taskToReportedErrorCount = new WeakMap()
|
|
60
61
|
const attemptToFixTaskToStatuses = new WeakMap()
|
|
61
62
|
const originalHookFns = new WeakMap()
|
|
62
63
|
const newTasks = new WeakSet()
|
|
@@ -316,6 +317,27 @@ function recordFinalAttemptToFixExecution (task, status, providedContext) {
|
|
|
316
317
|
})
|
|
317
318
|
}
|
|
318
319
|
|
|
320
|
+
function disableFrameworkRetries (task) {
|
|
321
|
+
task.retry = 0
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Vitest accumulates retry and repeat errors on one task result. The first error added since
|
|
326
|
+
* the last reported attempt is the primary error for the failed attempt currently being reported.
|
|
327
|
+
*
|
|
328
|
+
* @param {object} task
|
|
329
|
+
* @param {Array<object> | undefined} errors
|
|
330
|
+
* @returns {object | undefined}
|
|
331
|
+
*/
|
|
332
|
+
function getCurrentAttemptTestError (task, errors) {
|
|
333
|
+
if (!errors?.length) return
|
|
334
|
+
|
|
335
|
+
const previousErrorCount = taskToReportedErrorCount.get(task) ?? 0
|
|
336
|
+
const testError = errors[previousErrorCount] ?? errors[0]
|
|
337
|
+
taskToReportedErrorCount.set(task, errors.length)
|
|
338
|
+
return testError
|
|
339
|
+
}
|
|
340
|
+
|
|
319
341
|
/**
|
|
320
342
|
* Wraps a function so it runs inside the current test span context.
|
|
321
343
|
* @param {object} task
|
|
@@ -801,6 +823,7 @@ function wrapVitestTestRunner (VitestTestRunner) {
|
|
|
801
823
|
onDone: (isAttemptToFix) => {
|
|
802
824
|
if (isAttemptToFix) {
|
|
803
825
|
isRetryReasonAttemptToFix = task.repeats !== testManagementAttemptToFixRetries
|
|
826
|
+
disableFrameworkRetries(task)
|
|
804
827
|
task.repeats = testManagementAttemptToFixRetries
|
|
805
828
|
attemptToFixTasks.add(task)
|
|
806
829
|
attemptToFixTaskToStatuses.set(task, [])
|
|
@@ -831,6 +854,7 @@ function wrapVitestTestRunner (VitestTestRunner) {
|
|
|
831
854
|
if (isImpacted) {
|
|
832
855
|
if (isEarlyFlakeDetectionEnabled) {
|
|
833
856
|
isRetryReasonEfd = true
|
|
857
|
+
disableFrameworkRetries(task)
|
|
834
858
|
task.repeats = numRepeats
|
|
835
859
|
}
|
|
836
860
|
modifiedTasks.add(task)
|
|
@@ -849,6 +873,7 @@ function wrapVitestTestRunner (VitestTestRunner) {
|
|
|
849
873
|
if (isNew && !attemptToFixTasks.has(task)) {
|
|
850
874
|
if (isEarlyFlakeDetectionEnabled && !modifiedTasks.has(task)) {
|
|
851
875
|
isRetryReasonEfd = true
|
|
876
|
+
disableFrameworkRetries(task)
|
|
852
877
|
task.repeats = numRepeats
|
|
853
878
|
}
|
|
854
879
|
newTasks.add(task)
|
|
@@ -986,7 +1011,7 @@ function wrapVitestTestRunner (VitestTestRunner) {
|
|
|
986
1011
|
const promises = {}
|
|
987
1012
|
const shouldSetProbe = isDiEnabled && numAttempt === 1
|
|
988
1013
|
const ctx = taskToCtx.get(task)
|
|
989
|
-
const testError = task.result?.errors
|
|
1014
|
+
const testError = getCurrentAttemptTestError(task, task.result?.errors)
|
|
990
1015
|
if (ctx) {
|
|
991
1016
|
testErrorCh.publish({
|
|
992
1017
|
error: testError,
|
|
@@ -1016,7 +1041,7 @@ function wrapVitestTestRunner (VitestTestRunner) {
|
|
|
1016
1041
|
const ctx = taskToCtx.get(task)
|
|
1017
1042
|
if (ctx) {
|
|
1018
1043
|
if (lastExecutionStatus === 'fail') {
|
|
1019
|
-
const testError = task.result?.errors
|
|
1044
|
+
const testError = getCurrentAttemptTestError(task, task.result?.errors)
|
|
1020
1045
|
testErrorCh.publish({ error: testError, ...ctx.currentStore })
|
|
1021
1046
|
} else {
|
|
1022
1047
|
testPassCh.publish({ task, ...ctx.currentStore })
|
|
@@ -1040,7 +1065,7 @@ function wrapVitestTestRunner (VitestTestRunner) {
|
|
|
1040
1065
|
|
|
1041
1066
|
const ctx = taskToCtx.get(task)
|
|
1042
1067
|
if (lastExecutionStatus === 'fail') {
|
|
1043
|
-
const testError = task.result?.errors
|
|
1068
|
+
const testError = getCurrentAttemptTestError(task, task.result?.errors)
|
|
1044
1069
|
testErrorCh.publish({ error: testError, ...ctx.currentStore })
|
|
1045
1070
|
} else {
|
|
1046
1071
|
testPassCh.publish({ task, ...ctx.currentStore })
|
|
@@ -1389,7 +1414,7 @@ addHook({
|
|
|
1389
1414
|
|
|
1390
1415
|
if (result) {
|
|
1391
1416
|
const { state, duration, errors } = result
|
|
1392
|
-
const testError = errors
|
|
1417
|
+
const testError = getCurrentAttemptTestError(task, errors)
|
|
1393
1418
|
if (attemptToFixTasks.has(task)) {
|
|
1394
1419
|
const status = getFinalAttemptToFixStatus(task, state, isSwitchedStatus, testCtx)
|
|
1395
1420
|
recordFinalAttemptToFixExecution(task, status, providedContext)
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
|
|
4
3
|
const ClientPlugin = require('../../dd-trace/src/plugins/client')
|
|
5
4
|
const { storage } = require('../../datadog-core')
|
|
6
5
|
const { tagsFromRequest, tagsFromResponse } = require('../../dd-trace/src/payload-tagging')
|
|
@@ -113,8 +112,6 @@ class BaseAwsSdkPlugin extends ClientPlugin {
|
|
|
113
112
|
integrationName: 'aws-sdk',
|
|
114
113
|
}, ctx)
|
|
115
114
|
|
|
116
|
-
analyticsSampler.sample(span, this.config.measured)
|
|
117
|
-
|
|
118
115
|
storage('legacy').run(ctx.currentStore, () => {
|
|
119
116
|
this.requestInject(span, request)
|
|
120
117
|
})
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const BaseAwsSdkPlugin = require('../../base')
|
|
4
4
|
const { parseModelId } = require('./utils')
|
|
5
5
|
|
|
6
|
-
const enabledOperations = new Set(['invokeModel', 'invokeModelWithResponseStream'])
|
|
6
|
+
const enabledOperations = new Set(['invokeModel', 'invokeModelWithResponseStream', 'converse', 'converseStream'])
|
|
7
7
|
|
|
8
8
|
class BedrockRuntime extends BaseAwsSdkPlugin {
|
|
9
9
|
static id = 'bedrockruntime'
|
|
@@ -131,6 +131,7 @@ class Generation {
|
|
|
131
131
|
outputTokens,
|
|
132
132
|
cacheReadTokens,
|
|
133
133
|
cacheWriteTokens,
|
|
134
|
+
messages,
|
|
134
135
|
} = {}) {
|
|
135
136
|
// stringify message as it could be a single generated message as well as a list of embeddings
|
|
136
137
|
this.message = typeof message === 'string' ? message : JSON.stringify(message) || ''
|
|
@@ -143,6 +144,7 @@ class Generation {
|
|
|
143
144
|
cacheReadTokens,
|
|
144
145
|
cacheWriteTokens,
|
|
145
146
|
}
|
|
147
|
+
this.messages = messages ?? [{ content: this.message, role: this.role }]
|
|
146
148
|
}
|
|
147
149
|
}
|
|
148
150
|
|
|
@@ -401,10 +403,7 @@ function extractTextAndResponseReason (response, provider, modelName) {
|
|
|
401
403
|
message: output.message?.content[0]?.text ?? 'Unsupported content type',
|
|
402
404
|
finishReason: body.stopReason,
|
|
403
405
|
role: output.message?.role,
|
|
404
|
-
|
|
405
|
-
outputTokens: body.usage?.outputTokens,
|
|
406
|
-
cacheReadInputTokenCount: body.usage?.cacheReadInputTokenCount,
|
|
407
|
-
cacheWriteInputTokenCount: body.usage?.cacheWriteInputTokenCount,
|
|
406
|
+
...buildUsage(body.usage),
|
|
408
407
|
})
|
|
409
408
|
}
|
|
410
409
|
break
|
|
@@ -476,6 +475,216 @@ function extractTextAndResponseReason (response, provider, modelName) {
|
|
|
476
475
|
return new Generation()
|
|
477
476
|
}
|
|
478
477
|
|
|
478
|
+
/**
|
|
479
|
+
* Convert a Converse content-block array to an LLMObs message array.
|
|
480
|
+
*
|
|
481
|
+
* @param {string} role
|
|
482
|
+
* @param {Array<object>} contentBlocks
|
|
483
|
+
* @returns {{ content?: string, role: string, toolCalls?: Array, toolResults?: Array } | undefined}
|
|
484
|
+
*/
|
|
485
|
+
function extractMessagesFromConverseContent (role, contentBlocks) {
|
|
486
|
+
let content = ''
|
|
487
|
+
const toolCalls = []
|
|
488
|
+
const toolResults = []
|
|
489
|
+
|
|
490
|
+
for (const block of contentBlocks || []) {
|
|
491
|
+
if (block == null || typeof block !== 'object') continue
|
|
492
|
+
if (typeof block.text === 'string') {
|
|
493
|
+
content += block.text
|
|
494
|
+
} else if (block.toolUse) {
|
|
495
|
+
toolCalls.push(buildToolCall(block.toolUse))
|
|
496
|
+
} else if (block.toolResult) {
|
|
497
|
+
toolResults.push(buildToolResult(block.toolResult))
|
|
498
|
+
} else {
|
|
499
|
+
content += `[Unsupported content type: ${getContentBlockType(block)}]`
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if (!content && toolCalls.length === 0 && toolResults.length === 0) return
|
|
504
|
+
|
|
505
|
+
const message = { role }
|
|
506
|
+
if (content) message.content = content
|
|
507
|
+
if (toolCalls.length > 0) message.toolCalls = toolCalls
|
|
508
|
+
if (toolResults.length > 0) message.toolResults = toolResults
|
|
509
|
+
return message
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Resolve a Converse `ContentBlock`'s member type. The block is a key-presence
|
|
514
|
+
* tagged union (no `type` discriminator), so the active member is its sole own
|
|
515
|
+
* key. For forward-compat `$unknown` members the real type is the first element
|
|
516
|
+
* of the `[name, value]` tuple.
|
|
517
|
+
*
|
|
518
|
+
* @param {object} block
|
|
519
|
+
* @returns {string}
|
|
520
|
+
*/
|
|
521
|
+
function getContentBlockType (block) {
|
|
522
|
+
const key = Object.keys(block)[0]
|
|
523
|
+
if (key === '$unknown') return block.$unknown?.[0] ?? 'unknown'
|
|
524
|
+
return key ?? 'unknown'
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Always emit at least one output message so downstream tagging has a role to attach to.
|
|
528
|
+
function toOutputMessages (role, contentBlocks) {
|
|
529
|
+
const message = extractMessagesFromConverseContent(role, contentBlocks)
|
|
530
|
+
return message ? [message] : [{ role, content: '' }]
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
function buildToolCall ({ name, input, toolUseId }) {
|
|
534
|
+
return { name: name ?? '', arguments: input ?? {}, toolId: toolUseId ?? '', type: 'toolUse' }
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
function parseToolInput (inputStr) {
|
|
538
|
+
try {
|
|
539
|
+
return JSON.parse(inputStr)
|
|
540
|
+
} catch {
|
|
541
|
+
log.warn('Failed to parse Converse stream toolUse.input JSON; emitting empty arguments')
|
|
542
|
+
return {}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function buildToolResult ({ toolUseId, content }) {
|
|
547
|
+
const result = (content || []).map(resolveToolResultItem).join('')
|
|
548
|
+
return { name: '', result, toolId: toolUseId ?? '', type: 'tool_result' }
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
function resolveToolResultItem (item) {
|
|
552
|
+
if (typeof item.text === 'string') return item.text
|
|
553
|
+
if (item.json != null) return JSON.stringify(item.json)
|
|
554
|
+
return `[Unsupported content type(s): ${getContentBlockType(item)}]`
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function buildUsage (usage = {}) {
|
|
558
|
+
return {
|
|
559
|
+
inputTokens: usage.inputTokens,
|
|
560
|
+
outputTokens: usage.outputTokens,
|
|
561
|
+
cacheReadTokens: usage.cacheReadInputTokens ?? usage.cacheReadInputTokenCount,
|
|
562
|
+
cacheWriteTokens: usage.cacheWriteInputTokens ?? usage.cacheWriteInputTokenCount,
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Extract tool definitions from a Converse request's `toolConfig.tools`,
|
|
568
|
+
* mapping Bedrock's `toolSpec` shape to LLMObs `ToolDefinition` shape.
|
|
569
|
+
*
|
|
570
|
+
* @param {object} params - Converse request params with optional `toolConfig.tools[].toolSpec`.
|
|
571
|
+
* @returns {Array<{ name: string, description: string, schema: object }>}
|
|
572
|
+
*/
|
|
573
|
+
function extractConverseToolDefinitions (params) {
|
|
574
|
+
const toolDefinitions = []
|
|
575
|
+
for (const tool of params.toolConfig?.tools || []) {
|
|
576
|
+
const toolSpec = tool?.toolSpec
|
|
577
|
+
if (!toolSpec?.name) continue
|
|
578
|
+
toolDefinitions.push({
|
|
579
|
+
name: toolSpec.name,
|
|
580
|
+
description: toolSpec.description ?? '',
|
|
581
|
+
schema: toolSpec.inputSchema ?? {},
|
|
582
|
+
})
|
|
583
|
+
}
|
|
584
|
+
return toolDefinitions
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Extract request metadata + rendered input messages from a Converse /
|
|
589
|
+
* ConverseStream request.
|
|
590
|
+
*
|
|
591
|
+
* @param {{ modelId?: string, messages?: Array, system?: Array, inferenceConfig?: object, toolConfig?: object }} params
|
|
592
|
+
* @returns {RequestParams}
|
|
593
|
+
*/
|
|
594
|
+
function extractRequestParamsConverse (params) {
|
|
595
|
+
const prompt = []
|
|
596
|
+
for (const block of params.system || []) {
|
|
597
|
+
if (typeof block?.text === 'string') prompt.push({ content: block.text, role: 'system' })
|
|
598
|
+
}
|
|
599
|
+
for (const msg of params.messages || []) {
|
|
600
|
+
if (msg == null || typeof msg !== 'object') continue
|
|
601
|
+
const message = extractMessagesFromConverseContent(msg.role || 'user', msg.content)
|
|
602
|
+
if (message) prompt.push(message)
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
const { temperature, topP, maxTokens, stopSequences } = params.inferenceConfig || {}
|
|
606
|
+
return new RequestParams({ prompt, temperature, topP, maxTokens, stopSequences })
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Extract output messages + usage from a non-stream Converse response.
|
|
611
|
+
*
|
|
612
|
+
* @param {{ output?: { message?: { role?: string, content?: Array } }, stopReason?: string, usage?: object }} response
|
|
613
|
+
* @returns {Generation}
|
|
614
|
+
*/
|
|
615
|
+
function extractTextAndResponseReasonConverse (response) {
|
|
616
|
+
const outputMessage = response?.output?.message
|
|
617
|
+
const role = outputMessage?.role || 'assistant'
|
|
618
|
+
|
|
619
|
+
return new Generation({
|
|
620
|
+
role,
|
|
621
|
+
finishReason: response?.stopReason || '',
|
|
622
|
+
...buildUsage(response?.usage),
|
|
623
|
+
messages: toOutputMessages(role, outputMessage?.content),
|
|
624
|
+
})
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Aggregate Converse stream events into a single output message + usage.
|
|
629
|
+
* One messageStart / messageStop pair per response, so one message out.
|
|
630
|
+
*
|
|
631
|
+
* Stream events describe the same content-block structure as the non-stream
|
|
632
|
+
* response, spread across start/delta chunks. We reassemble those chunks
|
|
633
|
+
* into a normalized content-block array and reuse the non-stream extractor.
|
|
634
|
+
*
|
|
635
|
+
* @param {Array<object>} chunks - Ordered ConverseStreamOutput events.
|
|
636
|
+
* @returns {Generation}
|
|
637
|
+
*/
|
|
638
|
+
function extractTextAndResponseReasonConverseFromStream (chunks) {
|
|
639
|
+
let role = 'assistant'
|
|
640
|
+
let stopReason = ''
|
|
641
|
+
let usage = {}
|
|
642
|
+
const blocksByIdx = new Map()
|
|
643
|
+
|
|
644
|
+
for (const chunk of chunks || []) {
|
|
645
|
+
if (chunk.messageStart?.role) {
|
|
646
|
+
role = chunk.messageStart.role
|
|
647
|
+
} else if (chunk.messageStop?.stopReason) {
|
|
648
|
+
stopReason = chunk.messageStop.stopReason
|
|
649
|
+
} else if (chunk.metadata?.usage) {
|
|
650
|
+
usage = chunk.metadata.usage
|
|
651
|
+
} else if (chunk.contentBlockStart?.start?.toolUse) {
|
|
652
|
+
const { contentBlockIndex, start: { toolUse } } = chunk.contentBlockStart
|
|
653
|
+
blocksByIdx.set(contentBlockIndex, {
|
|
654
|
+
toolUse: { toolUseId: toolUse.toolUseId, name: toolUse.name, inputStr: '' },
|
|
655
|
+
})
|
|
656
|
+
} else if (chunk.contentBlockDelta) {
|
|
657
|
+
const { contentBlockIndex, delta } = chunk.contentBlockDelta
|
|
658
|
+
if (typeof delta?.text === 'string') {
|
|
659
|
+
const block = blocksByIdx.get(contentBlockIndex) ?? {}
|
|
660
|
+
block.text = (block.text ?? '') + delta.text
|
|
661
|
+
blocksByIdx.set(contentBlockIndex, block)
|
|
662
|
+
} else if (typeof delta?.toolUse?.input === 'string') {
|
|
663
|
+
const block = blocksByIdx.get(contentBlockIndex) ?? { toolUse: { inputStr: '' } }
|
|
664
|
+
block.toolUse ??= { inputStr: '' }
|
|
665
|
+
block.toolUse.inputStr += delta.toolUse.input
|
|
666
|
+
blocksByIdx.set(contentBlockIndex, block)
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
const contentBlocks = [...blocksByIdx.keys()].sort((a, b) => a - b).map(i => {
|
|
672
|
+
const block = blocksByIdx.get(i)
|
|
673
|
+
if (block.toolUse) {
|
|
674
|
+
const { toolUseId, name, inputStr } = block.toolUse
|
|
675
|
+
block.toolUse = { toolUseId, name, input: parseToolInput(inputStr) }
|
|
676
|
+
}
|
|
677
|
+
return block
|
|
678
|
+
})
|
|
679
|
+
|
|
680
|
+
return new Generation({
|
|
681
|
+
role,
|
|
682
|
+
finishReason: stopReason,
|
|
683
|
+
...buildUsage(usage),
|
|
684
|
+
messages: toOutputMessages(role, contentBlocks),
|
|
685
|
+
})
|
|
686
|
+
}
|
|
687
|
+
|
|
479
688
|
module.exports = {
|
|
480
689
|
Generation,
|
|
481
690
|
RequestParams,
|
|
@@ -483,5 +692,10 @@ module.exports = {
|
|
|
483
692
|
parseModelId,
|
|
484
693
|
extractRequestParams,
|
|
485
694
|
extractTextAndResponseReason,
|
|
695
|
+
extractMessagesFromConverseContent,
|
|
696
|
+
extractConverseToolDefinitions,
|
|
697
|
+
extractRequestParamsConverse,
|
|
698
|
+
extractTextAndResponseReasonConverse,
|
|
699
|
+
extractTextAndResponseReasonConverseFromStream,
|
|
486
700
|
PROVIDER,
|
|
487
701
|
}
|