dd-trace 5.109.0 → 5.110.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/init.js +4 -3
- package/index.d.ts +66 -24
- package/loader-hook.mjs +88 -2
- package/package.json +18 -14
- package/packages/datadog-esbuild/index.js +26 -0
- package/packages/datadog-esbuild/src/utils.js +46 -3
- package/packages/datadog-instrumentations/src/connect.js +4 -3
- package/packages/datadog-instrumentations/src/fastify.js +4 -12
- package/packages/datadog-instrumentations/src/graphql.js +17 -6
- package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +81 -8
- package/packages/datadog-instrumentations/src/helpers/instrument.js +27 -2
- package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +65 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +6 -4
- package/packages/datadog-instrumentations/src/helpers/require-optional-peer.js +17 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +18 -2
- package/packages/datadog-instrumentations/src/koa.js +3 -2
- package/packages/datadog-instrumentations/src/mariadb.js +4 -2
- package/packages/datadog-instrumentations/src/mocha/main.js +3 -3
- package/packages/datadog-instrumentations/src/next.js +10 -10
- package/packages/datadog-instrumentations/src/otel-sdk-trace.js +7 -6
- package/packages/datadog-instrumentations/src/playwright.js +1 -1
- package/packages/datadog-instrumentations/src/restify.js +4 -3
- package/packages/datadog-instrumentations/src/router.js +18 -5
- package/packages/datadog-instrumentations/src/selenium.js +1 -2
- package/packages/datadog-instrumentations/src/tedious.js +28 -0
- package/packages/datadog-instrumentations/src/vitest.js +264 -7
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +2 -2
- package/packages/datadog-plugin-aws-sdk/src/base.js +13 -9
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +43 -6
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +83 -91
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +9 -1
- package/packages/datadog-plugin-http/src/client.js +8 -3
- package/packages/datadog-plugin-http2/src/client.js +5 -2
- package/packages/datadog-plugin-mysql/src/index.js +7 -2
- package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +2 -2
- package/packages/datadog-plugin-undici/src/index.js +5 -2
- package/packages/datadog-plugin-vitest/src/index.js +7 -1
- package/packages/datadog-webpack/index.js +17 -1
- package/packages/datadog-webpack/src/optional-peer-loader.js +17 -0
- package/packages/dd-trace/src/aiguard/sdk.js +13 -1
- package/packages/dd-trace/src/appsec/api_security/index.js +55 -0
- package/packages/dd-trace/src/appsec/api_security/sampler.js +147 -0
- package/packages/dd-trace/src/appsec/index.js +10 -6
- package/packages/dd-trace/src/appsec/reporter.js +1 -0
- package/packages/dd-trace/src/appsec/telemetry/api_security.js +34 -0
- package/packages/dd-trace/src/appsec/telemetry/index.js +26 -0
- package/packages/dd-trace/src/ci-visibility/requests/request.js +1 -17
- package/packages/dd-trace/src/config/defaults.js +22 -3
- package/packages/dd-trace/src/config/generated-config-types.d.ts +535 -1
- package/packages/dd-trace/src/config/helper.js +133 -33
- package/packages/dd-trace/src/config/index.js +4 -1
- package/packages/dd-trace/src/config/parsers.js +19 -1
- package/packages/dd-trace/src/config/supported-configurations.json +21 -3
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +47 -35
- package/packages/dd-trace/src/debugger/devtools_client/index.js +31 -66
- package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +83 -0
- package/packages/dd-trace/src/debugger/devtools_client/state.js +1 -0
- package/packages/dd-trace/src/debugger/index.js +5 -0
- package/packages/dd-trace/src/debugger/probe_sampler.js +112 -0
- package/packages/dd-trace/src/debugger/probe_sampler_constants.js +19 -0
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +77 -26
- package/packages/dd-trace/src/exporters/common/request.js +1 -20
- package/packages/dd-trace/src/exporters/common/url.js +34 -0
- package/packages/dd-trace/src/exporters/span-stats/writer.js +1 -3
- package/packages/dd-trace/src/guardrails/index.js +8 -5
- package/packages/dd-trace/src/index.js +6 -4
- package/packages/dd-trace/src/llmobs/constants/tags.js +6 -0
- package/packages/dd-trace/src/llmobs/index.js +16 -5
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +3 -3
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +5 -1
- package/packages/dd-trace/src/llmobs/sdk.js +5 -3
- package/packages/dd-trace/src/llmobs/span_processor.js +4 -0
- package/packages/dd-trace/src/llmobs/tagger.js +55 -0
- package/packages/dd-trace/src/llmobs/telemetry.js +1 -0
- package/packages/dd-trace/src/log/index.js +18 -20
- package/packages/dd-trace/src/openfeature/flagging_provider.js +2 -7
- package/packages/dd-trace/src/opentelemetry/bridge-span-base.js +5 -4
- package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +2 -1
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +4 -3
- package/packages/dd-trace/src/opentelemetry/metrics/time.js +19 -0
- package/packages/dd-trace/src/opentelemetry/otlp/common.proto +2 -2
- package/packages/dd-trace/src/opentelemetry/otlp/logs.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/logs_service.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/metrics.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/metrics_service.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +11 -4
- package/packages/dd-trace/src/opentelemetry/span-helpers.js +29 -13
- package/packages/dd-trace/src/opentelemetry/span.js +14 -3
- package/packages/dd-trace/src/opentelemetry/span_context.js +1 -0
- package/packages/dd-trace/src/opentelemetry/trace/index.js +2 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +3 -2
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +26 -13
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +3 -3
- package/packages/dd-trace/src/opentracing/span_context.js +17 -0
- package/packages/dd-trace/src/plugin_manager.js +7 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +9 -1
- package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +276 -0
- package/packages/dd-trace/src/plugins/util/test.js +80 -0
- package/packages/dd-trace/src/plugins/util/url.js +68 -1
- package/packages/dd-trace/src/plugins/util/web.js +19 -36
- package/packages/dd-trace/src/priority_sampler.js +1 -13
- package/packages/dd-trace/src/profiling/config.js +129 -198
- package/packages/dd-trace/src/profiling/constants.js +4 -7
- package/packages/dd-trace/src/profiling/exporter_cli.js +10 -10
- package/packages/dd-trace/src/profiling/exporters/agent.js +23 -16
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +45 -14
- package/packages/dd-trace/src/profiling/exporters/file.js +16 -5
- package/packages/dd-trace/src/profiling/oom.js +71 -0
- package/packages/dd-trace/src/profiling/profiler.js +35 -23
- package/packages/dd-trace/src/profiling/profilers/events.js +14 -7
- package/packages/dd-trace/src/profiling/profilers/space.js +27 -18
- package/packages/dd-trace/src/profiling/profilers/wall.js +35 -25
- package/packages/dd-trace/src/ritm.js +4 -2
- package/packages/dd-trace/src/serverless.js +1 -2
- package/packages/dd-trace/src/service-naming/schemas/util.js +36 -5
- package/packages/dd-trace/src/service-naming/schemas/v1/web.js +5 -3
- package/packages/dd-trace/src/span_format.js +2 -1
- package/packages/dd-trace/src/span_processor.js +6 -0
- package/packages/dd-trace/src/telemetry/send-data.js +12 -10
- package/packages/dd-trace/src/tracer.js +15 -1
- package/packages/dd-trace/src/util.js +14 -0
- package/register.js +58 -1
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +1 -1
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/pprof-format/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/packages/dd-trace/src/appsec/api_security_sampler.js +0 -117
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { DD_TRACE_SYMBOL, PROBE_SAMPLER_SYMBOL } = require('../probe_sampler_constants')
|
|
4
|
+
|
|
5
|
+
const SAMPLER_EXPRESSION = `globalThis[Symbol.for(${JSON.stringify(DD_TRACE_SYMBOL)})]?.` +
|
|
6
|
+
`[Symbol.for(${JSON.stringify(PROBE_SAMPLER_SYMBOL)})]`
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
compileBreakpointCondition,
|
|
10
|
+
getRemoveProbeExpression,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Build the expression that removes a probe from runtime sampler state. Called by the devtools worker and evaluated on
|
|
15
|
+
* the debuggee.
|
|
16
|
+
*
|
|
17
|
+
* @param {string} id - The probe id.
|
|
18
|
+
* @returns {string}
|
|
19
|
+
*/
|
|
20
|
+
function getRemoveProbeExpression (id) {
|
|
21
|
+
return `${SAMPLER_EXPRESSION}?.remove(${JSON.stringify(id)})`
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Build a Chrome DevTools breakpoint condition that samples all matching probes at a location. Called by the devtools
|
|
26
|
+
* worker.
|
|
27
|
+
*
|
|
28
|
+
* @param {{
|
|
29
|
+
* id: string,
|
|
30
|
+
* samplingIndex: number,
|
|
31
|
+
* nsBetweenSampling: bigint,
|
|
32
|
+
* condition?: string,
|
|
33
|
+
* captureSnapshot?: boolean,
|
|
34
|
+
* compiledCaptureExpressions?: object[]
|
|
35
|
+
* }[]} probes - The probes at the breakpoint location.
|
|
36
|
+
* @returns {string}
|
|
37
|
+
*/
|
|
38
|
+
function compileBreakpointCondition (probes) {
|
|
39
|
+
const probeConditions = []
|
|
40
|
+
for (const probe of probes) {
|
|
41
|
+
probeConditions.push(compileProbeCondition(probe))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// NOTE: $dd_sampler is read from the realm-local `globalThis` where it was installed (the main
|
|
45
|
+
// realm). A probe whose code runs in a different V8 realm (e.g. a `vm.createContext` script with a
|
|
46
|
+
// file-path filename) won't see it and will silently never fire. Known limitation: a breakpoint
|
|
47
|
+
// condition has no realm-independent handle to reach, so we degrade rather than crash.
|
|
48
|
+
return `(() => {
|
|
49
|
+
const $dd_sampler = ${SAMPLER_EXPRESSION}
|
|
50
|
+
if ($dd_sampler === undefined) return false
|
|
51
|
+
let $dd_sampled = false
|
|
52
|
+
${probeConditions.join('\n ')}
|
|
53
|
+
return $dd_sampled
|
|
54
|
+
})()`
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Build the condition fragment for a single probe. Called by the devtools worker while building breakpoint conditions.
|
|
59
|
+
*
|
|
60
|
+
* @param {{
|
|
61
|
+
* id: string,
|
|
62
|
+
* samplingIndex: number,
|
|
63
|
+
* nsBetweenSampling: bigint,
|
|
64
|
+
* condition?: string,
|
|
65
|
+
* captureSnapshot?: boolean,
|
|
66
|
+
* compiledCaptureExpressions?: object[]
|
|
67
|
+
* }} probe - The probe to sample.
|
|
68
|
+
* @returns {string}
|
|
69
|
+
*/
|
|
70
|
+
function compileProbeCondition (probe) {
|
|
71
|
+
const sample = `$dd_sampler.makeSampleDecision(${probe.samplingIndex}, ${JSON.stringify(probe.id)}, ` +
|
|
72
|
+
`${probe.nsBetweenSampling}n, ${probe.captureSnapshot === true || probe.compiledCaptureExpressions !== undefined})`
|
|
73
|
+
|
|
74
|
+
if (probe.condition === undefined) {
|
|
75
|
+
return `$dd_sampled = ${sample} || $dd_sampled`
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return `try {
|
|
79
|
+
if ((${probe.condition}) === true) {
|
|
80
|
+
$dd_sampled = ${sample} || $dd_sampled
|
|
81
|
+
}
|
|
82
|
+
} catch {}`
|
|
83
|
+
}
|
|
@@ -33,6 +33,7 @@ module.exports = {
|
|
|
33
33
|
locationToBreakpoint: new Map(),
|
|
34
34
|
breakpointToProbes: new Map(),
|
|
35
35
|
probeToLocation: new Map(),
|
|
36
|
+
samplingIndexToProbe: new Map(),
|
|
36
37
|
|
|
37
38
|
_loadedScripts: loadedScripts, // Only exposed for testing
|
|
38
39
|
_scriptUrls: scriptUrls, // Only exposed for testing
|
|
@@ -8,6 +8,7 @@ const log = require('../log')
|
|
|
8
8
|
const { fetchAgentInfo } = require('../agent/info')
|
|
9
9
|
const getDebuggerConfig = require('./config')
|
|
10
10
|
const { DEBUGGER_DIAGNOSTICS_V1, DEBUGGER_INPUT_V2 } = require('./constants')
|
|
11
|
+
const { installProbeSampler, uninstallProbeSampler } = require('./probe_sampler')
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* @typedef {ReturnType<import('../config')>} Config
|
|
@@ -65,6 +66,8 @@ function start (config, rcInstance) {
|
|
|
65
66
|
|
|
66
67
|
globalThis[Symbol.for('dd-trace')].utilTypes = types
|
|
67
68
|
|
|
69
|
+
const probeSamplerBuffer = installProbeSampler()
|
|
70
|
+
|
|
68
71
|
readProbeFile(config.dynamicInstrumentation.probeFile, (probes) => {
|
|
69
72
|
const action = 'apply'
|
|
70
73
|
for (const probe of probes) {
|
|
@@ -110,6 +113,7 @@ function start (config, rcInstance) {
|
|
|
110
113
|
probePort: probeChannel.port1,
|
|
111
114
|
logPort: logChannel.port1,
|
|
112
115
|
configPort: configChannel.port1,
|
|
116
|
+
probeSamplerBuffer,
|
|
113
117
|
},
|
|
114
118
|
transferList: [probeChannel.port1, logChannel.port1, configChannel.port1],
|
|
115
119
|
}
|
|
@@ -187,6 +191,7 @@ function cleanup (error) {
|
|
|
187
191
|
worker.removeAllListeners()
|
|
188
192
|
worker = null
|
|
189
193
|
}
|
|
194
|
+
uninstallProbeSampler()
|
|
190
195
|
configChannel = null
|
|
191
196
|
inputPath = null
|
|
192
197
|
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { MAX_SNAPSHOTS_PER_SECOND_GLOBALLY } = require('./devtools_client/defaults')
|
|
4
|
+
const {
|
|
5
|
+
DD_TRACE_SYMBOL,
|
|
6
|
+
MAX_SAMPLED_PROBES_PER_PAUSE,
|
|
7
|
+
PROBE_SAMPLER_SYMBOL,
|
|
8
|
+
SAMPLED_PROBE_COUNT_INDEX,
|
|
9
|
+
SAMPLED_PROBE_INDEXES_START,
|
|
10
|
+
SAMPLED_PROBE_OVERFLOW_INDEX,
|
|
11
|
+
} = require('./probe_sampler_constants')
|
|
12
|
+
|
|
13
|
+
const ddTraceGlobal = /** @type {Record<symbol, SharedArrayBuffer | object | undefined>} */ (
|
|
14
|
+
/** @type {Record<symbol, unknown>} */ (globalThis)[Symbol.for(DD_TRACE_SYMBOL)]
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
module.exports = {
|
|
18
|
+
installProbeSampler,
|
|
19
|
+
uninstallProbeSampler,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Install the runtime sampler in the debuggee context.
|
|
24
|
+
*
|
|
25
|
+
* @returns {SharedArrayBuffer} The shared sampler buffer to pass to the debugger worker.
|
|
26
|
+
*/
|
|
27
|
+
function installProbeSampler () {
|
|
28
|
+
const buffer = createProbeSamplerBuffer()
|
|
29
|
+
|
|
30
|
+
const lastCaptureNsByProbeId = new Map()
|
|
31
|
+
const sampledProbeIndexes = new Int32Array(buffer)
|
|
32
|
+
Atomics.store(sampledProbeIndexes, SAMPLED_PROBE_COUNT_INDEX, 0)
|
|
33
|
+
Atomics.store(sampledProbeIndexes, SAMPLED_PROBE_OVERFLOW_INDEX, 0)
|
|
34
|
+
|
|
35
|
+
const oneSecondNs = 1_000_000_000n
|
|
36
|
+
let globalSnapshotSamplingRateWindowStart = 0n
|
|
37
|
+
let snapshotsSampledWithinTheLastSecond = 0
|
|
38
|
+
|
|
39
|
+
ddTraceGlobal[Symbol.for(PROBE_SAMPLER_SYMBOL)] = {
|
|
40
|
+
/**
|
|
41
|
+
* Decide if a probe should be sampled and store sampled probe indexes for the debugger worker.
|
|
42
|
+
*
|
|
43
|
+
* @param {number} probeIndex - The worker-side probe sampling index.
|
|
44
|
+
* @param {string} probeId - The probe id.
|
|
45
|
+
* @param {bigint} nsBetweenSampling - Minimum nanoseconds between samples for this probe.
|
|
46
|
+
* @param {boolean} isSnapshotProducingProbe - Whether this probe counts toward the global snapshot sample limit.
|
|
47
|
+
* @returns {boolean} Whether this probe should make the breakpoint condition pause.
|
|
48
|
+
*/
|
|
49
|
+
makeSampleDecision (probeIndex, probeId, nsBetweenSampling, isSnapshotProducingProbe) {
|
|
50
|
+
const now = process.hrtime.bigint()
|
|
51
|
+
const lastCaptureNs = lastCaptureNsByProbeId.get(probeId)
|
|
52
|
+
if (lastCaptureNs !== undefined && now - lastCaptureNs < nsBetweenSampling) return false
|
|
53
|
+
|
|
54
|
+
let shouldResetGlobalSnapshotRateWindow = false
|
|
55
|
+
if (isSnapshotProducingProbe === true) {
|
|
56
|
+
if (now - globalSnapshotSamplingRateWindowStart > oneSecondNs) {
|
|
57
|
+
shouldResetGlobalSnapshotRateWindow = true
|
|
58
|
+
} else if (snapshotsSampledWithinTheLastSecond >= MAX_SNAPSHOTS_PER_SECOND_GLOBALLY) {
|
|
59
|
+
return false
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const sampledProbeCount = Atomics.add(sampledProbeIndexes, SAMPLED_PROBE_COUNT_INDEX, 1)
|
|
64
|
+
if (sampledProbeCount >= MAX_SAMPLED_PROBES_PER_PAUSE) {
|
|
65
|
+
Atomics.store(sampledProbeIndexes, SAMPLED_PROBE_OVERFLOW_INDEX, 1)
|
|
66
|
+
return false
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (isSnapshotProducingProbe === true) {
|
|
70
|
+
if (shouldResetGlobalSnapshotRateWindow === true) {
|
|
71
|
+
snapshotsSampledWithinTheLastSecond = 1
|
|
72
|
+
globalSnapshotSamplingRateWindowStart = now
|
|
73
|
+
} else {
|
|
74
|
+
snapshotsSampledWithinTheLastSecond++
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
lastCaptureNsByProbeId.set(probeId, now)
|
|
79
|
+
Atomics.store(sampledProbeIndexes, SAMPLED_PROBE_INDEXES_START + sampledProbeCount, probeIndex)
|
|
80
|
+
return true
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Remove cached sampling state for a probe.
|
|
85
|
+
*
|
|
86
|
+
* @param {string} probeId - The probe id.
|
|
87
|
+
*/
|
|
88
|
+
remove (probeId) {
|
|
89
|
+
lastCaptureNsByProbeId.delete(probeId)
|
|
90
|
+
},
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return buffer
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Remove the runtime sampler from the debuggee context.
|
|
98
|
+
*/
|
|
99
|
+
function uninstallProbeSampler () {
|
|
100
|
+
delete ddTraceGlobal[Symbol.for(PROBE_SAMPLER_SYMBOL)]
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Create the shared buffer used to hand sampled probe indexes from breakpoint conditions to the debugger worker.
|
|
105
|
+
*
|
|
106
|
+
* @returns {SharedArrayBuffer}
|
|
107
|
+
*/
|
|
108
|
+
function createProbeSamplerBuffer () {
|
|
109
|
+
return new SharedArrayBuffer(
|
|
110
|
+
(SAMPLED_PROBE_INDEXES_START + MAX_SAMPLED_PROBES_PER_PAUSE) * Int32Array.BYTES_PER_ELEMENT
|
|
111
|
+
)
|
|
112
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const DD_TRACE_SYMBOL = 'dd-trace'
|
|
4
|
+
const PROBE_SAMPLER_SYMBOL = 'dd-trace.debugger.probeSampler'
|
|
5
|
+
|
|
6
|
+
// Shared sampler contract used by the main debugger bootstrap and the devtools worker.
|
|
7
|
+
const MAX_SAMPLED_PROBES_PER_PAUSE = 256
|
|
8
|
+
const SAMPLED_PROBE_COUNT_INDEX = 0
|
|
9
|
+
const SAMPLED_PROBE_OVERFLOW_INDEX = 1
|
|
10
|
+
const SAMPLED_PROBE_INDEXES_START = 2
|
|
11
|
+
|
|
12
|
+
module.exports = {
|
|
13
|
+
DD_TRACE_SYMBOL,
|
|
14
|
+
MAX_SAMPLED_PROBES_PER_PAUSE,
|
|
15
|
+
PROBE_SAMPLER_SYMBOL,
|
|
16
|
+
SAMPLED_PROBE_COUNT_INDEX,
|
|
17
|
+
SAMPLED_PROBE_INDEXES_START,
|
|
18
|
+
SAMPLED_PROBE_OVERFLOW_INDEX,
|
|
19
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
const { version: ddTraceVersion } = require('../../../../package.json')
|
|
3
|
-
const { ITR_CORRELATION_ID } = require('../../src/plugins/util/test')
|
|
3
|
+
const { ITR_CORRELATION_ID, TEST_LEVELS_METADATA } = require('../../src/plugins/util/test')
|
|
4
4
|
const id = require('../../src/id')
|
|
5
5
|
const {
|
|
6
6
|
distributionMetric,
|
|
@@ -9,10 +9,15 @@ const {
|
|
|
9
9
|
} = require('../ci-visibility/telemetry')
|
|
10
10
|
const { MsgpackChunk } = require('../msgpack')
|
|
11
11
|
const { AgentEncoder } = require('./0.4')
|
|
12
|
-
const {
|
|
12
|
+
const {
|
|
13
|
+
truncateSpanTestOpt,
|
|
14
|
+
normalizeSpan,
|
|
15
|
+
MAX_META_VALUE_LENGTH_TEST_OPTIMIZATION,
|
|
16
|
+
} = require('./tags-processors')
|
|
13
17
|
|
|
14
18
|
const ENCODING_VERSION = 1
|
|
15
19
|
const ALLOWED_CONTENT_TYPES = new Set(['test_session_end', 'test_module_end', 'test_suite_end', 'test'])
|
|
20
|
+
const ALLOWED_METADATA_TARGETS = new Set([...ALLOWED_CONTENT_TYPES, TEST_LEVELS_METADATA])
|
|
16
21
|
|
|
17
22
|
const TEST_SUITE_KEYS_LENGTH = 12
|
|
18
23
|
const TEST_MODULE_KEYS_LENGTH = 11
|
|
@@ -36,6 +41,33 @@ function formatSpan (span) {
|
|
|
36
41
|
}
|
|
37
42
|
}
|
|
38
43
|
|
|
44
|
+
function isAllowedMetadataTarget (target) {
|
|
45
|
+
return ALLOWED_METADATA_TARGETS.has(target)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isEncodableMetadataValue (value) {
|
|
49
|
+
return typeof value === 'string' || typeof value === 'number'
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function truncateTestLevelMetadataValue (value) {
|
|
53
|
+
if (typeof value !== 'string' || value.length <= MAX_META_VALUE_LENGTH_TEST_OPTIMIZATION) {
|
|
54
|
+
return value
|
|
55
|
+
}
|
|
56
|
+
return `${value.slice(0, MAX_META_VALUE_LENGTH_TEST_OPTIMIZATION)}...`
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function truncateTestLevelMetadataTags (tags) {
|
|
60
|
+
const truncatedTags = {}
|
|
61
|
+
let hasTags = false
|
|
62
|
+
for (const key of Object.keys(tags)) {
|
|
63
|
+
const value = truncateTestLevelMetadataValue(tags[key])
|
|
64
|
+
if (!isEncodableMetadataValue(value)) continue
|
|
65
|
+
truncatedTags[key] = value
|
|
66
|
+
hasTags = true
|
|
67
|
+
}
|
|
68
|
+
return hasTags ? truncatedTags : undefined
|
|
69
|
+
}
|
|
70
|
+
|
|
39
71
|
class AgentlessCiVisibilityEncoder extends AgentEncoder {
|
|
40
72
|
constructor (writer, { runtimeId, service, env }) {
|
|
41
73
|
super(writer, INTAKE_SOFT_LIMIT)
|
|
@@ -49,6 +81,7 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
|
|
|
49
81
|
|
|
50
82
|
this.metadataTags = {}
|
|
51
83
|
this.wildcardMetadataTags = {}
|
|
84
|
+
this.testLevelsMetadataKeys = []
|
|
52
85
|
|
|
53
86
|
this.reset()
|
|
54
87
|
}
|
|
@@ -60,12 +93,39 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
|
|
|
60
93
|
...tags['*'],
|
|
61
94
|
}
|
|
62
95
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
96
|
+
|
|
97
|
+
for (const target of Object.keys(tags)) {
|
|
98
|
+
if (target === '*' || !tags[target] || !isAllowedMetadataTarget(target)) continue
|
|
99
|
+
|
|
100
|
+
const targetTags = target === TEST_LEVELS_METADATA
|
|
101
|
+
? truncateTestLevelMetadataTags(tags[target])
|
|
102
|
+
: tags[target]
|
|
103
|
+
if (!targetTags) continue
|
|
104
|
+
|
|
105
|
+
this.metadataTags[target] = {
|
|
106
|
+
...this.metadataTags[target],
|
|
107
|
+
...targetTags,
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (target === TEST_LEVELS_METADATA) {
|
|
111
|
+
this.testLevelsMetadataKeys = Object.keys(this.metadataTags[target])
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
_removeDuplicateTestLevelsMetadata (event) {
|
|
117
|
+
if (!ALLOWED_CONTENT_TYPES.has(event.type)) return
|
|
118
|
+
|
|
119
|
+
const meta = event.content.meta
|
|
120
|
+
if (!meta) return
|
|
121
|
+
|
|
122
|
+
const testLevelsMetadataTags = this.metadataTags[TEST_LEVELS_METADATA]
|
|
123
|
+
const testLevelsMetadataKeys = this.testLevelsMetadataKeys
|
|
124
|
+
|
|
125
|
+
for (let i = 0; i < testLevelsMetadataKeys.length; i++) {
|
|
126
|
+
const key = testLevelsMetadataKeys[i]
|
|
127
|
+
if (meta[key] === testLevelsMetadataTags[key]) {
|
|
128
|
+
delete meta[key]
|
|
69
129
|
}
|
|
70
130
|
}
|
|
71
131
|
}
|
|
@@ -276,7 +336,11 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
|
|
|
276
336
|
|
|
277
337
|
this._eventCount += events.length
|
|
278
338
|
|
|
339
|
+
const hasTestLevelsMetadata = this.testLevelsMetadataKeys.length !== 0
|
|
279
340
|
for (const event of events) {
|
|
341
|
+
if (hasTestLevelsMetadata) {
|
|
342
|
+
this._removeDuplicateTestLevelsMetadata(event)
|
|
343
|
+
}
|
|
280
344
|
this._encodeEvent(bytes, event)
|
|
281
345
|
}
|
|
282
346
|
distributionMetric(
|
|
@@ -343,24 +407,11 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
|
|
|
343
407
|
this._encodeString(bytes, 'version')
|
|
344
408
|
bytes.writeNumber(payload.version)
|
|
345
409
|
this._encodeString(bytes, 'metadata')
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
this.
|
|
351
|
-
this._encodeMap(bytes, payload.metadata.test)
|
|
352
|
-
}
|
|
353
|
-
if (payload.metadata.test_suite_end) {
|
|
354
|
-
this._encodeString(bytes, 'test_suite_end')
|
|
355
|
-
this._encodeMap(bytes, payload.metadata.test_suite_end)
|
|
356
|
-
}
|
|
357
|
-
if (payload.metadata.test_module_end) {
|
|
358
|
-
this._encodeString(bytes, 'test_module_end')
|
|
359
|
-
this._encodeMap(bytes, payload.metadata.test_module_end)
|
|
360
|
-
}
|
|
361
|
-
if (payload.metadata.test_session_end) {
|
|
362
|
-
this._encodeString(bytes, 'test_session_end')
|
|
363
|
-
this._encodeMap(bytes, payload.metadata.test_session_end)
|
|
410
|
+
const metadataKeys = Object.keys(payload.metadata)
|
|
411
|
+
bytes.writeMapPrefix(metadataKeys.length)
|
|
412
|
+
for (const metadataKey of metadataKeys) {
|
|
413
|
+
this._encodeString(bytes, metadataKey)
|
|
414
|
+
this._encodeMap(bytes, payload.metadata[metadataKey])
|
|
364
415
|
}
|
|
365
416
|
this._encodeString(bytes, 'events')
|
|
366
417
|
this._eventsOffset = bytes.length
|
|
@@ -11,7 +11,7 @@ const zlib = require('zlib')
|
|
|
11
11
|
|
|
12
12
|
const { storage } = require('../../../../datadog-core')
|
|
13
13
|
const log = require('../../log')
|
|
14
|
-
const {
|
|
14
|
+
const { parseUrl } = require('./url')
|
|
15
15
|
const docker = require('./docker')
|
|
16
16
|
const { httpAgent, httpsAgent } = require('./agents')
|
|
17
17
|
const {
|
|
@@ -27,25 +27,6 @@ const maxActiveBufferSize = 1024 * 1024 * 64
|
|
|
27
27
|
|
|
28
28
|
let activeBufferSize = 0
|
|
29
29
|
|
|
30
|
-
/**
|
|
31
|
-
* @param {string|URL|object} urlObjOrString
|
|
32
|
-
* @returns {object}
|
|
33
|
-
*/
|
|
34
|
-
function parseUrl (urlObjOrString) {
|
|
35
|
-
if (urlObjOrString !== null && typeof urlObjOrString === 'object') return urlToHttpOptions(urlObjOrString)
|
|
36
|
-
|
|
37
|
-
const url = urlToHttpOptions(new URL(urlObjOrString))
|
|
38
|
-
|
|
39
|
-
// Special handling if we're using named pipes on Windows
|
|
40
|
-
if (url.protocol === 'unix:' && url.hostname === '.') {
|
|
41
|
-
const udsPath = urlObjOrString.slice(5)
|
|
42
|
-
url.path = udsPath
|
|
43
|
-
url.pathname = udsPath
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return url
|
|
47
|
-
}
|
|
48
|
-
|
|
49
30
|
/**
|
|
50
31
|
* @param {string} hostname Host as resolved by {@link parseUrl}; IPv6 is unbracketed (`::1`).
|
|
51
32
|
*/
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { urlToHttpOptions } = require('./url-to-http-options-polyfill')
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Convert an agent/intake URL into Node http(s) request options.
|
|
7
|
+
*
|
|
8
|
+
* A Windows named pipe (`unix://./pipe/<name>`) parses the `.` as the URL
|
|
9
|
+
* authority, dropping it from the path. Fold it back so the socket path stays
|
|
10
|
+
* `//./pipe/<name>`; otherwise it collapses to `/pipe/<name>` and misses the
|
|
11
|
+
* pipe. Exporters receive `config.url` as a URL object, so this must run for
|
|
12
|
+
* both string and object input.
|
|
13
|
+
*
|
|
14
|
+
* @param {string|URL|object} urlObjOrString
|
|
15
|
+
* @returns {object}
|
|
16
|
+
*/
|
|
17
|
+
function parseUrl (urlObjOrString) {
|
|
18
|
+
// `urlToHttpOptions` returns `pathname` at runtime, but @types/node narrows it
|
|
19
|
+
// to `ClientRequestArgs`, which omits it; cast so the named-pipe fold below can
|
|
20
|
+
// read and rewrite it.
|
|
21
|
+
const url = /** @type {import('node:http').ClientRequestArgs & { pathname: string }} */ (
|
|
22
|
+
urlObjOrString !== null && typeof urlObjOrString === 'object'
|
|
23
|
+
? urlToHttpOptions(urlObjOrString)
|
|
24
|
+
: urlToHttpOptions(new URL(urlObjOrString))
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
if (url.protocol === 'unix:' && url.hostname === '.') {
|
|
28
|
+
url.path = url.pathname = `//.${url.pathname}`
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return url
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = { parseUrl }
|
|
@@ -37,9 +37,7 @@ function makeRequest (data, url, cb) {
|
|
|
37
37
|
'Datadog-Meta-Tracer-Version': pkg.version,
|
|
38
38
|
'Content-Type': 'application/msgpack',
|
|
39
39
|
},
|
|
40
|
-
|
|
41
|
-
hostname: url.hostname,
|
|
42
|
-
port: url.port,
|
|
40
|
+
url,
|
|
43
41
|
}
|
|
44
42
|
|
|
45
43
|
log.debug('Request to the intake: %j', options)
|
|
@@ -14,11 +14,13 @@ function guard (fn) {
|
|
|
14
14
|
var initBailout = false
|
|
15
15
|
var clobberBailout = false
|
|
16
16
|
var forced = isTrue(process.env.DD_INJECT_FORCE)
|
|
17
|
-
var
|
|
18
|
-
var
|
|
17
|
+
var pkg = require('../../../../package.json')
|
|
18
|
+
var engines = pkg.engines
|
|
19
|
+
var versions = engines.node.match(/^>=(\d+)$/)
|
|
19
20
|
var minMajor = versions[1]
|
|
20
|
-
var nextMajor =
|
|
21
|
+
var nextMajor = pkg.nodeMaxMajor
|
|
21
22
|
var version = process.versions.node
|
|
23
|
+
var supportedRange = engines.node + ' <' + nextMajor
|
|
22
24
|
|
|
23
25
|
if (process.env.DD_INJECTION_ENABLED) {
|
|
24
26
|
// If we're running via single-step install, and we're in the app's
|
|
@@ -45,16 +47,17 @@ function guard (fn) {
|
|
|
45
47
|
// should not initialize the tracer.
|
|
46
48
|
if (!clobberBailout && (NODE_MAJOR < minMajor || NODE_MAJOR >= nextMajor)) {
|
|
47
49
|
initBailout = true
|
|
50
|
+
var runtimeInfo = 'Incompatible runtime Node.js ' + version + ', supported runtimes: Node.js ' + supportedRange
|
|
48
51
|
telemetry([
|
|
49
52
|
{ name: 'abort', tags: ['reason:incompatible_runtime'] },
|
|
50
53
|
{ name: 'abort.runtime', tags: [] }
|
|
51
54
|
], undefined, {
|
|
52
55
|
result: 'abort',
|
|
53
56
|
result_class: 'incompatible_runtime',
|
|
54
|
-
result_reason:
|
|
57
|
+
result_reason: runtimeInfo
|
|
55
58
|
})
|
|
56
59
|
log.info('Aborting application instrumentation due to incompatible_runtime.')
|
|
57
|
-
log.info('Found incompatible runtime Node.js %s, Supported runtimes: Node.js %s.', version,
|
|
60
|
+
log.info('Found incompatible runtime Node.js %s, Supported runtimes: Node.js %s.', version, supportedRange)
|
|
58
61
|
if (forced) {
|
|
59
62
|
log.info('DD_INJECT_FORCE enabled, allowing unsupported runtimes and continuing.')
|
|
60
63
|
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { getValueFromEnvSources } = require('./config/helper')
|
|
4
|
-
const { isFalse } = require('./util')
|
|
5
4
|
|
|
6
5
|
// Global `jest` is only present in Jest workers.
|
|
7
6
|
const inJestWorker = typeof jest !== 'undefined'
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
// skipDefault: distinguish an unset DD_TRACE_ENABLED (fall back to the OTel signal) from an
|
|
9
|
+
// explicit value; the registered default would otherwise mask the OTEL_TRACES_EXPORTER check.
|
|
10
|
+
const ddTraceEnabled = getValueFromEnvSources('DD_TRACE_ENABLED', true)
|
|
11
|
+
const ddTraceDisabled = ddTraceEnabled === undefined
|
|
12
|
+
? getValueFromEnvSources('OTEL_TRACES_EXPORTER') === 'none'
|
|
13
|
+
: ddTraceEnabled === false
|
|
12
14
|
|
|
13
15
|
module.exports = ddTraceDisabled || inJestWorker
|
|
14
16
|
? require('./noop/proxy')
|
|
@@ -14,6 +14,12 @@ module.exports = {
|
|
|
14
14
|
PROPAGATED_PARENT_ID_KEY: '_dd.p.llmobs_parent_id',
|
|
15
15
|
PROPAGATED_ML_APP_KEY: '_dd.p.llmobs_ml_app',
|
|
16
16
|
PARENT_ID_KEY: '_ml_obs.llmobs_parent_id',
|
|
17
|
+
PROPAGATED_SAMPLE_RATE_KEY: '_dd.p.llmobs_sr',
|
|
18
|
+
PROPAGATED_SAMPLING_DECISION_KEY: '_dd.p.llmobs_sd',
|
|
19
|
+
SAMPLE_RATE: '_ml_obs.sample_rate',
|
|
20
|
+
SAMPLING_DECISION: '_ml_obs.sampling_decision',
|
|
21
|
+
SAMPLING_DECISION_SAMPLED: '1',
|
|
22
|
+
SAMPLING_DECISION_DROPPED: '0',
|
|
17
23
|
TAGS: '_ml_obs.tags',
|
|
18
24
|
NAME: '_ml_obs.name',
|
|
19
25
|
TRACE_ID: '_ml_obs.trace_id',
|
|
@@ -9,6 +9,10 @@ const {
|
|
|
9
9
|
ML_APP,
|
|
10
10
|
PROPAGATED_ML_APP_KEY,
|
|
11
11
|
PROPAGATED_PARENT_ID_KEY,
|
|
12
|
+
SAMPLE_RATE,
|
|
13
|
+
SAMPLING_DECISION,
|
|
14
|
+
PROPAGATED_SAMPLE_RATE_KEY,
|
|
15
|
+
PROPAGATED_SAMPLING_DECISION_KEY,
|
|
12
16
|
} = require('./constants/tags')
|
|
13
17
|
const { storage } = require('./storage')
|
|
14
18
|
const telemetry = require('./telemetry')
|
|
@@ -68,7 +72,7 @@ function enable (config) {
|
|
|
68
72
|
spanFinishCh.subscribe(handleSpanProcess)
|
|
69
73
|
|
|
70
74
|
// distributed tracing for llmobs
|
|
71
|
-
injectCh.subscribe(
|
|
75
|
+
injectCh.subscribe(handleLLMObsInjection)
|
|
72
76
|
|
|
73
77
|
setAgentStrategy(config, useAgentless => {
|
|
74
78
|
if (useAgentless && !(config.apiKey && config.site)) {
|
|
@@ -92,7 +96,7 @@ function disable () {
|
|
|
92
96
|
if (evalMetricAppendCh.hasSubscribers) evalMetricAppendCh.unsubscribe(handleEvalMetricAppend)
|
|
93
97
|
if (flushCh.hasSubscribers) flushCh.unsubscribe(handleFlush)
|
|
94
98
|
if (spanFinishCh.hasSubscribers) spanFinishCh.unsubscribe(handleSpanProcess)
|
|
95
|
-
if (injectCh.hasSubscribers) injectCh.unsubscribe(
|
|
99
|
+
if (injectCh.hasSubscribers) injectCh.unsubscribe(handleLLMObsInjection)
|
|
96
100
|
if (registerUserSpanProcessorCh.hasSubscribers) registerUserSpanProcessorCh.unsubscribe(handleRegisterProcessor)
|
|
97
101
|
|
|
98
102
|
spanWriter?.destroy()
|
|
@@ -106,8 +110,8 @@ function disable () {
|
|
|
106
110
|
}
|
|
107
111
|
|
|
108
112
|
// since LLMObs traces can extend between services and be the same trace,
|
|
109
|
-
// we need to propagate the parent id and
|
|
110
|
-
function
|
|
113
|
+
// we need to propagate the parent id, mlApp, and sampling rate/decision.
|
|
114
|
+
function handleLLMObsInjection ({ carrier }) {
|
|
111
115
|
// Respect the standard propagator's gate: when trace tag propagation is
|
|
112
116
|
// disabled, don't write `x-datadog-tags` for LLMObs either.
|
|
113
117
|
if (globalTracerConfig.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH === 0) return
|
|
@@ -122,7 +126,12 @@ function handleLLMObsParentIdInjection ({ carrier }) {
|
|
|
122
126
|
parentContext?._trace?.tags?.[PROPAGATED_ML_APP_KEY] ||
|
|
123
127
|
globalTracerConfig.llmobs.mlApp
|
|
124
128
|
|
|
125
|
-
|
|
129
|
+
const sampleRate =
|
|
130
|
+
mlObsSpanTags?.[SAMPLE_RATE] ?? parentContext?._trace?.tags?.[PROPAGATED_SAMPLE_RATE_KEY]
|
|
131
|
+
const samplingDecision =
|
|
132
|
+
mlObsSpanTags?.[SAMPLING_DECISION] ?? parentContext?._trace?.tags?.[PROPAGATED_SAMPLING_DECISION_KEY]
|
|
133
|
+
|
|
134
|
+
if (!parentId && !mlApp && samplingDecision == null) return
|
|
126
135
|
|
|
127
136
|
// `_injectTags` only writes `x-datadog-tags` when the trace has `_dd.p.*`
|
|
128
137
|
// tags, so it may be undefined here — coalesce before appending.
|
|
@@ -130,6 +139,8 @@ function handleLLMObsParentIdInjection ({ carrier }) {
|
|
|
130
139
|
let tags = existing || ''
|
|
131
140
|
if (parentId) tags += `${tags ? ',' : ''}${PROPAGATED_PARENT_ID_KEY}=${parentId}`
|
|
132
141
|
if (mlApp) tags += `${tags ? ',' : ''}${PROPAGATED_ML_APP_KEY}=${mlApp}`
|
|
142
|
+
if (sampleRate != null) tags += `${tags ? ',' : ''}${PROPAGATED_SAMPLE_RATE_KEY}=${sampleRate}`
|
|
143
|
+
if (samplingDecision != null) tags += `${tags ? ',' : ''}${PROPAGATED_SAMPLING_DECISION_KEY}=${samplingDecision}`
|
|
133
144
|
if (tags !== existing) carrier['x-datadog-tags'] = tags
|
|
134
145
|
}
|
|
135
146
|
|
|
@@ -114,7 +114,7 @@ function extractMetrics (response) {
|
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
const totalTokens = tokenUsage.totalTokenCount ||
|
|
117
|
-
|
|
117
|
+
(tokenUsage.promptTokenCount || 0) + (tokenUsage.candidatesTokenCount || 0)
|
|
118
118
|
if (totalTokens) {
|
|
119
119
|
metrics.totalTokens = totalTokens
|
|
120
120
|
}
|
|
@@ -458,8 +458,8 @@ function formatStreamingOutput (response) {
|
|
|
458
458
|
|
|
459
459
|
// Skip special cases in streaming (handle them as non-streaming)
|
|
460
460
|
if (content.parts.some(part => part.functionCall ||
|
|
461
|
-
|
|
462
|
-
|
|
461
|
+
part.executableCode ||
|
|
462
|
+
part.codeExecutionResult)) {
|
|
463
463
|
messages.push(...formatNonStreamingCandidate(candidate))
|
|
464
464
|
continue
|
|
465
465
|
}
|
|
@@ -9,7 +9,11 @@ const ANTHROPIC_PROVIDER_NAME = 'anthropic'
|
|
|
9
9
|
const BEDROCK_PROVIDER_NAME = 'amazon_bedrock'
|
|
10
10
|
const OPENAI_PROVIDER_NAME = 'openai'
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
// Providers that ship their own LLMObs integration. When one of these is also
|
|
13
|
+
// enabled, the LangChain model span is demoted to `workflow` so the provider
|
|
14
|
+
// integration emits the single `llm` span — avoiding two llm spans (and
|
|
15
|
+
// double-counted tokens/cost) for one underlying call.
|
|
16
|
+
const SUPPORTED_INTEGRATIONS = new Set(['openai', 'anthropic'])
|
|
13
17
|
const LLM_SPAN_TYPES = new Set(['llm', 'chat_model', 'embedding'])
|
|
14
18
|
const LLM = 'llm'
|
|
15
19
|
const WORKFLOW = 'workflow'
|