dd-trace 5.82.0 → 5.83.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 +78 -79
- package/ci/init.js +6 -6
- package/index.d.ts +152 -3
- package/loader-hook.mjs +1 -1
- package/package.json +58 -55
- package/packages/datadog-core/src/storage.js +7 -7
- package/packages/datadog-esbuild/index.js +6 -0
- package/packages/datadog-instrumentations/src/ai.js +7 -3
- package/packages/datadog-instrumentations/src/child_process.js +1 -1
- package/packages/datadog-instrumentations/src/cucumber.js +1 -1
- package/packages/datadog-instrumentations/src/graphql.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/instrumentations.js +4 -3
- package/packages/datadog-instrumentations/src/helpers/register.js +3 -7
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +1 -1
- package/packages/datadog-instrumentations/src/http/client.js +2 -2
- package/packages/datadog-instrumentations/src/jest.js +35 -14
- package/packages/datadog-instrumentations/src/koa.js +2 -1
- package/packages/datadog-instrumentations/src/light-my-request.js +2 -2
- package/packages/datadog-instrumentations/src/mocha/main.js +2 -2
- package/packages/datadog-instrumentations/src/mocha/worker.js +1 -1
- package/packages/datadog-instrumentations/src/mocha.js +1 -1
- package/packages/datadog-instrumentations/src/mysql.js +1 -1
- package/packages/datadog-instrumentations/src/mysql2.js +2 -2
- package/packages/datadog-instrumentations/src/net.js +13 -5
- package/packages/datadog-instrumentations/src/nyc.js +1 -1
- package/packages/datadog-instrumentations/src/otel-sdk-trace.js +4 -4
- package/packages/datadog-instrumentations/src/pg.js +4 -2
- package/packages/datadog-instrumentations/src/playwright.js +3 -3
- package/packages/datadog-instrumentations/src/selenium.js +2 -2
- package/packages/datadog-instrumentations/src/undici.js +12 -1
- package/packages/datadog-plugin-aws-sdk/src/base.js +4 -4
- package/packages/datadog-plugin-azure-event-hubs/src/producer.js +2 -2
- package/packages/datadog-plugin-azure-service-bus/src/producer.js +2 -2
- package/packages/datadog-plugin-cucumber/src/index.js +2 -2
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +2 -2
- package/packages/datadog-plugin-dd-trace-api/src/index.js +2 -2
- package/packages/datadog-plugin-express/src/code_origin.js +21 -15
- package/packages/datadog-plugin-fastify/src/code_origin.js +17 -4
- package/packages/datadog-plugin-jest/src/index.js +2 -2
- package/packages/datadog-plugin-mocha/src/index.js +2 -2
- package/packages/datadog-plugin-mongodb-core/src/index.js +2 -2
- package/packages/datadog-plugin-playwright/src/index.js +3 -3
- package/packages/datadog-plugin-undici/src/index.js +305 -2
- package/packages/datadog-plugin-vitest/src/index.js +5 -5
- package/packages/dd-trace/index.js +19 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +1 -1
- package/packages/dd-trace/src/appsec/rasp/index.js +2 -4
- package/packages/dd-trace/src/azure_metadata.js +8 -3
- package/packages/dd-trace/src/baggage.js +36 -11
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +5 -1
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +3 -2
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +3 -3
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +4 -4
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -2
- package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +4 -4
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -4
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +2 -2
- package/packages/dd-trace/src/{config_defaults.js → config/defaults.js} +3 -3
- package/packages/dd-trace/src/{config-helper.js → config/helper.js} +88 -15
- package/packages/dd-trace/src/{config.js → config/index.js} +92 -45
- package/packages/dd-trace/src/config/remote_config.js +187 -19
- package/packages/dd-trace/src/{config_stable.js → config/stable.js} +20 -32
- package/packages/dd-trace/src/{supported-configurations.json → config/supported-configurations.json} +2 -0
- package/packages/dd-trace/src/crashtracking/crashtracker.js +1 -1
- package/packages/dd-trace/src/datastreams/processor.js +1 -1
- package/packages/dd-trace/src/datastreams/writer.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/condition.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/config.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/send.js +3 -3
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/constants.js +1 -1
- package/packages/dd-trace/src/debugger/index.js +83 -15
- package/packages/dd-trace/src/dogstatsd.js +2 -2
- package/packages/dd-trace/src/encode/0.4.js +2 -2
- package/packages/dd-trace/src/exporter.js +1 -1
- package/packages/dd-trace/src/exporters/agent/index.js +2 -4
- package/packages/dd-trace/src/exporters/agent/writer.js +9 -14
- package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +1 -1
- package/packages/dd-trace/src/exporters/common/docker.js +2 -2
- package/packages/dd-trace/src/exporters/common/request.js +1 -1
- package/packages/dd-trace/src/exporters/common/util.js +2 -2
- package/packages/dd-trace/src/exporters/span-stats/index.js +1 -1
- package/packages/dd-trace/src/flare/index.js +1 -1
- package/packages/dd-trace/src/guardrails/telemetry.js +1 -1
- package/packages/dd-trace/src/index.js +4 -4
- package/packages/dd-trace/src/lambda/handler.js +2 -2
- package/packages/dd-trace/src/lambda/index.js +2 -2
- package/packages/dd-trace/src/lambda/runtime/patch.js +2 -2
- package/packages/dd-trace/src/lambda/runtime/ritm.js +2 -2
- package/packages/dd-trace/src/llmobs/constants/tags.js +8 -1
- package/packages/dd-trace/src/llmobs/index.js +2 -2
- package/packages/dd-trace/src/llmobs/noop.js +2 -0
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +3 -4
- package/packages/dd-trace/src/llmobs/sdk.js +33 -6
- package/packages/dd-trace/src/llmobs/span_processor.js +17 -7
- package/packages/dd-trace/src/llmobs/tagger.js +175 -1
- package/packages/dd-trace/src/llmobs/writers/base.js +116 -37
- package/packages/dd-trace/src/llmobs/writers/spans.js +4 -3
- package/packages/dd-trace/src/log/index.js +5 -5
- package/packages/dd-trace/src/noop/proxy.js +3 -3
- package/packages/dd-trace/src/openfeature/writers/base.js +7 -8
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +2 -2
- package/packages/dd-trace/src/opentelemetry/tracer.js +48 -6
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +45 -21
- package/packages/dd-trace/src/opentracing/span.js +4 -4
- package/packages/dd-trace/src/plugin_manager.js +8 -6
- package/packages/dd-trace/src/plugins/util/ci.js +5 -8
- package/packages/dd-trace/src/plugins/util/git-cache.js +3 -3
- package/packages/dd-trace/src/plugins/util/test.js +1 -1
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +41 -43
- package/packages/dd-trace/src/profiler.js +4 -39
- package/packages/dd-trace/src/profiling/config.js +74 -31
- package/packages/dd-trace/src/profiling/exporter_cli.js +5 -5
- package/packages/dd-trace/src/profiling/exporters/agent.js +1 -1
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +9 -2
- package/packages/dd-trace/src/profiling/index.js +1 -1
- package/packages/dd-trace/src/profiling/libuv-size.js +1 -1
- package/packages/dd-trace/src/profiling/profiler.js +57 -2
- package/packages/dd-trace/src/proxy.js +34 -5
- package/packages/dd-trace/src/remote_config/capabilities.js +3 -0
- package/packages/dd-trace/src/remote_config/index.js +1 -1
- package/packages/dd-trace/src/ritm.js +8 -4
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +2 -2
- package/packages/dd-trace/src/serverless.js +2 -2
- package/packages/dd-trace/src/span_processor.js +2 -2
- package/packages/dd-trace/src/startup-log.js +6 -15
- package/packages/dd-trace/src/telemetry/endpoints.js +67 -5
- package/packages/dd-trace/src/telemetry/send-data.js +103 -4
- package/packages/dd-trace/src/telemetry/telemetry.js +229 -110
- /package/packages/dd-trace/src/{git_properties.js → config/git_properties.js} +0 -0
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { inspect } = require('util')
|
|
4
|
+
|
|
3
5
|
const request = require('../common/request')
|
|
4
6
|
const { startupLog } = require('../../startup-log')
|
|
5
7
|
const runtimeMetrics = require('../../runtime_metrics')
|
|
@@ -26,7 +28,10 @@ class AgentWriter extends BaseWriter {
|
|
|
26
28
|
runtimeMetrics.increment(`${METRIC_PREFIX}.requests`, true)
|
|
27
29
|
|
|
28
30
|
const { _headers, _lookup, _protocolVersion, _url } = this
|
|
29
|
-
makeRequest(_protocolVersion, data, count, _url, _headers, _lookup,
|
|
31
|
+
makeRequest(_protocolVersion, data, count, _url, _headers, _lookup, (err, res, status) => {
|
|
32
|
+
// Note that logging will only happen once, regardless of how many times this is called.
|
|
33
|
+
startupLog(status !== 404 && status !== 200 ? { status, message: err?.message ?? inspect(err) } : undefined)
|
|
34
|
+
|
|
30
35
|
if (status) {
|
|
31
36
|
runtimeMetrics.increment(`${METRIC_PREFIX}.responses`, true)
|
|
32
37
|
runtimeMetrics.increment(`${METRIC_PREFIX}.responses.by.status`, `status:${status}`, true)
|
|
@@ -39,8 +44,6 @@ class AgentWriter extends BaseWriter {
|
|
|
39
44
|
}
|
|
40
45
|
}
|
|
41
46
|
|
|
42
|
-
startupLog({ agentError: err })
|
|
43
|
-
|
|
44
47
|
if (err) {
|
|
45
48
|
log.errorWithoutTelemetry('Error sending payload to the agent (status code: %s)', err.status, err)
|
|
46
49
|
done()
|
|
@@ -68,7 +71,7 @@ function getEncoder (protocolVersion) {
|
|
|
68
71
|
: require('../../encode/0.4').AgentEncoder
|
|
69
72
|
}
|
|
70
73
|
|
|
71
|
-
function makeRequest (version, data, count, url, headers, lookup,
|
|
74
|
+
function makeRequest (version, data, count, url, headers, lookup, cb) {
|
|
72
75
|
const options = {
|
|
73
76
|
path: `/v${version}/traces`,
|
|
74
77
|
method: 'PUT',
|
|
@@ -79,7 +82,7 @@ function makeRequest (version, data, count, url, headers, lookup, needsStartupLo
|
|
|
79
82
|
'X-Datadog-Trace-Count': String(count),
|
|
80
83
|
'Datadog-Meta-Lang': 'nodejs',
|
|
81
84
|
'Datadog-Meta-Lang-Version': process.version,
|
|
82
|
-
'Datadog-Meta-Lang-Interpreter': process.
|
|
85
|
+
'Datadog-Meta-Lang-Interpreter': process.versions.bun ? 'JavaScriptCore' : 'v8'
|
|
83
86
|
},
|
|
84
87
|
lookup,
|
|
85
88
|
url
|
|
@@ -87,15 +90,7 @@ function makeRequest (version, data, count, url, headers, lookup, needsStartupLo
|
|
|
87
90
|
|
|
88
91
|
log.debug('Request to the agent: %j', options)
|
|
89
92
|
|
|
90
|
-
request(data, options,
|
|
91
|
-
if (needsStartupLog) {
|
|
92
|
-
// Note that logging will only happen once, regardless of how many times this is called.
|
|
93
|
-
startupLog({
|
|
94
|
-
agentError: status !== 404 && status !== 200 ? err : undefined
|
|
95
|
-
})
|
|
96
|
-
}
|
|
97
|
-
cb(err, res, status)
|
|
98
|
-
})
|
|
93
|
+
request(data, options, cb)
|
|
99
94
|
}
|
|
100
95
|
|
|
101
96
|
module.exports = AgentWriter
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { URL, format } = require('url')
|
|
4
4
|
|
|
5
|
-
const defaults = require('../../
|
|
5
|
+
const defaults = require('../../config/defaults')
|
|
6
6
|
const { incrementCountMetric, TELEMETRY_EVENTS_ENQUEUED_FOR_SERIALIZATION } = require('../../ci-visibility/telemetry')
|
|
7
7
|
const request = require('./request')
|
|
8
8
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const fs = require('fs')
|
|
4
|
-
const {
|
|
4
|
+
const { getValueFromEnvSources } = require('../../config/helper')
|
|
5
5
|
|
|
6
|
-
const DD_EXTERNAL_ENV =
|
|
6
|
+
const DD_EXTERNAL_ENV = getValueFromEnvSources('DD_EXTERNAL_ENV')
|
|
7
7
|
|
|
8
8
|
// The second part is the PCF / Garden regexp. We currently assume no suffix($) to avoid matching pod UIDs
|
|
9
9
|
// See https://github.com/DataDog/datadog-agent/blob/7.40.x/pkg/util/cgroups/reader.go#L50
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { getValueFromEnvSources } = require('../../config/helper')
|
|
4
4
|
|
|
5
5
|
function safeJSONStringify (value) {
|
|
6
6
|
return JSON.stringify(
|
|
7
7
|
value,
|
|
8
8
|
(key, value) => key === 'dd-api-key' ? undefined : value,
|
|
9
|
-
|
|
9
|
+
getValueFromEnvSources('DD_TRACE_BEAUTIFUL_LOGS') ? 2 : undefined
|
|
10
10
|
)
|
|
11
11
|
}
|
|
12
12
|
|
|
@@ -80,7 +80,7 @@ function sendTelemetry (name, tags, resultMetadata) {
|
|
|
80
80
|
proc.on('error', function () {
|
|
81
81
|
log.error('Failed to spawn telemetry forwarder')
|
|
82
82
|
})
|
|
83
|
-
proc.
|
|
83
|
+
proc.once('exit', function (code) {
|
|
84
84
|
if (code !== 0) {
|
|
85
85
|
log.error('Telemetry forwarder exited with code', code)
|
|
86
86
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { getValueFromEnvSources } = require('./config/helper')
|
|
4
4
|
const { isFalse } = require('./util')
|
|
5
5
|
|
|
6
6
|
// Global `jest` is only present in Jest workers.
|
|
7
7
|
const inJestWorker = typeof jest !== 'undefined'
|
|
8
8
|
|
|
9
|
-
const ddTraceDisabled =
|
|
10
|
-
? isFalse(
|
|
11
|
-
: String(
|
|
9
|
+
const ddTraceDisabled = getValueFromEnvSources('DD_TRACE_ENABLED')
|
|
10
|
+
? isFalse(getValueFromEnvSources('DD_TRACE_ENABLED'))
|
|
11
|
+
: String(getValueFromEnvSources('OTEL_TRACES_EXPORTER')).toLowerCase() === 'none'
|
|
12
12
|
|
|
13
13
|
module.exports = ddTraceDisabled || inJestWorker
|
|
14
14
|
? require('./noop/proxy')
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const log = require('../log')
|
|
4
4
|
const { channel } = require('../../../datadog-instrumentations/src/helpers/instrument')
|
|
5
5
|
const { ERROR_MESSAGE, ERROR_TYPE } = require('../constants')
|
|
6
|
-
const {
|
|
6
|
+
const { getValueFromEnvSources } = require('../config/helper')
|
|
7
7
|
const { ImpendingTimeout } = require('./runtime/errors')
|
|
8
8
|
|
|
9
9
|
const globalTracer = global._ddtrace
|
|
@@ -26,7 +26,7 @@ let __lambdaTimeout
|
|
|
26
26
|
function checkTimeout (context) {
|
|
27
27
|
const remainingTimeInMillis = context.getRemainingTimeInMillis()
|
|
28
28
|
|
|
29
|
-
let apmFlushDeadline = Number.parseInt(
|
|
29
|
+
let apmFlushDeadline = Number.parseInt(getValueFromEnvSources('DD_APM_FLUSH_DEADLINE_MILLISECONDS')) || 100
|
|
30
30
|
apmFlushDeadline = apmFlushDeadline < 0 ? 100 : apmFlushDeadline
|
|
31
31
|
|
|
32
32
|
__lambdaTimeout = setTimeout(() => {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { getValueFromEnvSources } = require('../config/helper')
|
|
4
4
|
const { registerLambdaHook } = require('./runtime/ritm')
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* It is safe to do it this way, since customers will never be expected to disable
|
|
8
8
|
* this specific instrumentation through the init config object.
|
|
9
9
|
*/
|
|
10
|
-
const _DD_TRACE_DISABLED_INSTRUMENTATIONS =
|
|
10
|
+
const _DD_TRACE_DISABLED_INSTRUMENTATIONS = getValueFromEnvSources('DD_TRACE_DISABLED_INSTRUMENTATIONS') || ''
|
|
11
11
|
const _disabledInstrumentations = new Set(
|
|
12
12
|
_DD_TRACE_DISABLED_INSTRUMENTATIONS ? _DD_TRACE_DISABLED_INSTRUMENTATIONS.split(',') : []
|
|
13
13
|
)
|
|
@@ -5,7 +5,7 @@ const path = require('path')
|
|
|
5
5
|
const { datadog } = require('../handler')
|
|
6
6
|
const { addHook } = require('../../../../datadog-instrumentations/src/helpers/instrument')
|
|
7
7
|
const shimmer = require('../../../../datadog-shimmer')
|
|
8
|
-
const { getEnvironmentVariable } = require('../../config
|
|
8
|
+
const { getEnvironmentVariable, getValueFromEnvSources } = require('../../config/helper')
|
|
9
9
|
const { _extractModuleNameAndHandlerPath, _extractModuleRootAndHandler, _getLambdaFilePaths } = require('./ritm')
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -59,7 +59,7 @@ function patchLambdaHandler (lambdaHandler) {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
const lambdaTaskRoot = getEnvironmentVariable('LAMBDA_TASK_ROOT')
|
|
62
|
-
const originalLambdaHandler =
|
|
62
|
+
const originalLambdaHandler = getValueFromEnvSources('DD_LAMBDA_HANDLER')
|
|
63
63
|
|
|
64
64
|
if (originalLambdaHandler === undefined) {
|
|
65
65
|
// Instrumentation is done manually.
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
const path = require('path')
|
|
11
11
|
|
|
12
12
|
const log = require('../../log')
|
|
13
|
-
const { getEnvironmentVariable } = require('../../config
|
|
13
|
+
const { getEnvironmentVariable, getValueFromEnvSources } = require('../../config/helper')
|
|
14
14
|
const Hook = require('../../../../datadog-instrumentations/src/helpers/hook')
|
|
15
15
|
const instrumentations = require('../../../../datadog-instrumentations/src/helpers/instrumentations')
|
|
16
16
|
const {
|
|
@@ -79,7 +79,7 @@ function _getLambdaFilePaths (lambdaStylePath) {
|
|
|
79
79
|
*/
|
|
80
80
|
const registerLambdaHook = () => {
|
|
81
81
|
const lambdaTaskRoot = getEnvironmentVariable('LAMBDA_TASK_ROOT')
|
|
82
|
-
const originalLambdaHandler =
|
|
82
|
+
const originalLambdaHandler = getValueFromEnvSources('DD_LAMBDA_HANDLER')
|
|
83
83
|
|
|
84
84
|
if (originalLambdaHandler !== undefined && lambdaTaskRoot !== undefined) {
|
|
85
85
|
const [moduleRoot, moduleAndHandler] = _extractModuleRootAndHandler(originalLambdaHandler)
|
|
@@ -17,6 +17,9 @@ module.exports = {
|
|
|
17
17
|
TRACE_ID: '_ml_obs.trace_id',
|
|
18
18
|
PROPAGATED_TRACE_ID_KEY: '_dd.p.llmobs_trace_id',
|
|
19
19
|
ROOT_PARENT_ID: 'undefined',
|
|
20
|
+
DEFAULT_PROMPT_NAME: 'unnamed-prompt',
|
|
21
|
+
INTERNAL_CONTEXT_VARIABLE_KEYS: '_dd_context_variable_keys',
|
|
22
|
+
INTERNAL_QUERY_VARIABLE_KEYS: '_dd_query_variable_keys',
|
|
20
23
|
|
|
21
24
|
MODEL_NAME: '_ml_obs.meta.model_name',
|
|
22
25
|
MODEL_PROVIDER: '_ml_obs.meta.model_provider',
|
|
@@ -24,6 +27,7 @@ module.exports = {
|
|
|
24
27
|
INPUT_DOCUMENTS: '_ml_obs.meta.input.documents',
|
|
25
28
|
INPUT_MESSAGES: '_ml_obs.meta.input.messages',
|
|
26
29
|
INPUT_VALUE: '_ml_obs.meta.input.value',
|
|
30
|
+
INPUT_PROMPT: '_ml_obs.meta.input.prompt',
|
|
27
31
|
|
|
28
32
|
OUTPUT_DOCUMENTS: '_ml_obs.meta.output.documents',
|
|
29
33
|
OUTPUT_MESSAGES: '_ml_obs.meta.output.messages',
|
|
@@ -42,5 +46,8 @@ module.exports = {
|
|
|
42
46
|
PROMPT_MULTIMODAL: 'prompt_multimodal',
|
|
43
47
|
INSTRUMENTATION_METHOD_AUTO: 'auto',
|
|
44
48
|
INSTRUMENTATION_METHOD_ANNOTATED: 'annotated',
|
|
45
|
-
INSTRUMENTATION_METHOD_UNKNOWN: 'unknown'
|
|
49
|
+
INSTRUMENTATION_METHOD_UNKNOWN: 'unknown',
|
|
50
|
+
|
|
51
|
+
ROUTING_API_KEY: '_dd.llmobs.routing.api_key',
|
|
52
|
+
ROUTING_SITE: '_dd.llmobs.routing.site'
|
|
46
53
|
}
|
|
@@ -136,9 +136,9 @@ function handleSpanProcess (span) {
|
|
|
136
136
|
spanProcessor.process(span)
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
function handleEvalMetricAppend (payload) {
|
|
139
|
+
function handleEvalMetricAppend ({ payload, routing }) {
|
|
140
140
|
try {
|
|
141
|
-
evalWriter.append(payload)
|
|
141
|
+
evalWriter.append(payload, routing)
|
|
142
142
|
} catch (e) {
|
|
143
143
|
log.warn(
|
|
144
144
|
// eslint-disable-next-line @stylistic/max-len
|
|
@@ -422,16 +422,15 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
|
|
|
422
422
|
// Handle prompt tracking for reusable prompts
|
|
423
423
|
if (inputs.prompt && response?.prompt) {
|
|
424
424
|
const { id, version } = response.prompt // ResponsePrompt
|
|
425
|
-
// TODO: Add proper tagger API for prompt metadata
|
|
426
425
|
if (id && version) {
|
|
427
426
|
const normalizedVariables = normalizePromptVariables(inputs.prompt.variables)
|
|
428
427
|
const chatTemplate = extractChatTemplateFromInstructions(response.instructions, normalizedVariables)
|
|
429
|
-
this._tagger.
|
|
428
|
+
this._tagger.tagPrompt(span, {
|
|
430
429
|
id,
|
|
431
430
|
version,
|
|
432
431
|
variables: normalizedVariables,
|
|
433
|
-
|
|
434
|
-
})
|
|
432
|
+
template: chatTemplate
|
|
433
|
+
}, true)
|
|
435
434
|
const tags = { [PROMPT_TRACKING_INSTRUMENTATION_METHOD]: INSTRUMENTATION_METHOD_AUTO }
|
|
436
435
|
if (hasMultimodalInputs(inputs.prompt.variables)) {
|
|
437
436
|
tags[PROMPT_MULTIMODAL] = 'true'
|
|
@@ -5,7 +5,7 @@ const { channel } = require('dc-polyfill')
|
|
|
5
5
|
const { isTrue, isError } = require('../util')
|
|
6
6
|
const tracerVersion = require('../../../../package.json').version
|
|
7
7
|
const logger = require('../log')
|
|
8
|
-
const {
|
|
8
|
+
const { getValueFromEnvSources } = require('../config/helper')
|
|
9
9
|
const Span = require('../opentracing/span')
|
|
10
10
|
const { SPAN_KIND, OUTPUT_VALUE, INPUT_VALUE } = require('./constants/tags')
|
|
11
11
|
const {
|
|
@@ -49,7 +49,7 @@ class LLMObs extends NoopLLMObs {
|
|
|
49
49
|
|
|
50
50
|
logger.debug('Enabling LLMObs')
|
|
51
51
|
|
|
52
|
-
const DD_LLMOBS_ENABLED =
|
|
52
|
+
const DD_LLMOBS_ENABLED = getValueFromEnvSources('DD_LLMOBS_ENABLED')
|
|
53
53
|
|
|
54
54
|
if (DD_LLMOBS_ENABLED != null && !isTrue(DD_LLMOBS_ENABLED)) {
|
|
55
55
|
logger.debug('LLMObs.enable() called when DD_LLMOBS_ENABLED is false. No action taken.')
|
|
@@ -62,7 +62,7 @@ class LLMObs extends NoopLLMObs {
|
|
|
62
62
|
}
|
|
63
63
|
// TODO: This will update config telemetry with the origin 'code', which is not ideal when `enable()` is called
|
|
64
64
|
// based on `APM_TRACING` RC product updates.
|
|
65
|
-
this._config.
|
|
65
|
+
this._config.updateOptions({ llmobs })
|
|
66
66
|
|
|
67
67
|
// configure writers and channel subscribers
|
|
68
68
|
this._llmobsModule.enable(this._config)
|
|
@@ -241,7 +241,7 @@ class LLMObs extends NoopLLMObs {
|
|
|
241
241
|
throw new Error('LLMObs span must have a span kind specified')
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
-
const { inputData, outputData, metadata, metrics, tags } = options
|
|
244
|
+
const { inputData, outputData, metadata, metrics, tags, prompt } = options
|
|
245
245
|
|
|
246
246
|
if (inputData || outputData) {
|
|
247
247
|
if (spanKind === 'llm') {
|
|
@@ -264,6 +264,9 @@ class LLMObs extends NoopLLMObs {
|
|
|
264
264
|
if (tags) {
|
|
265
265
|
this._tagger.tagSpanTags(span, tags)
|
|
266
266
|
}
|
|
267
|
+
if (prompt) {
|
|
268
|
+
this._tagger.tagPrompt(span, prompt)
|
|
269
|
+
}
|
|
267
270
|
} catch (e) {
|
|
268
271
|
if (e.ddErrorTag) {
|
|
269
272
|
err = e.ddErrorTag
|
|
@@ -404,7 +407,7 @@ class LLMObs extends NoopLLMObs {
|
|
|
404
407
|
}
|
|
405
408
|
|
|
406
409
|
// When OTel tracing is enabled, add source:otel tag to allow backend to wait for OTel span conversion
|
|
407
|
-
if (isTrue(
|
|
410
|
+
if (isTrue(getValueFromEnvSources('DD_TRACE_OTEL_ENABLED'))) {
|
|
408
411
|
evaluationTags.source = 'otel'
|
|
409
412
|
}
|
|
410
413
|
|
|
@@ -418,7 +421,9 @@ class LLMObs extends NoopLLMObs {
|
|
|
418
421
|
timestamp_ms: timestampMs,
|
|
419
422
|
tags: Object.entries(evaluationTags).map(([key, value]) => `${key}:${value}`)
|
|
420
423
|
}
|
|
421
|
-
|
|
424
|
+
const currentStore = storage.getStore()
|
|
425
|
+
const routing = currentStore?.routingContext
|
|
426
|
+
evalMetricAppendCh.publish({ payload, routing })
|
|
422
427
|
} finally {
|
|
423
428
|
telemetry.recordSubmitEvaluation(options, err)
|
|
424
429
|
}
|
|
@@ -440,6 +445,28 @@ class LLMObs extends NoopLLMObs {
|
|
|
440
445
|
return storage.run(store, fn)
|
|
441
446
|
}
|
|
442
447
|
|
|
448
|
+
routingContext (options, fn) {
|
|
449
|
+
if (!this.enabled) return fn()
|
|
450
|
+
if (!options?.ddApiKey) {
|
|
451
|
+
throw new Error('ddApiKey is required for routing context')
|
|
452
|
+
}
|
|
453
|
+
const currentStore = storage.getStore()
|
|
454
|
+
if (currentStore?.routingContext) {
|
|
455
|
+
logger.warn(
|
|
456
|
+
'[LLM Observability] Nested routing context detected. Inner context will override outer context. ' +
|
|
457
|
+
'Spans created in the inner context will only be sent to the inner context.'
|
|
458
|
+
)
|
|
459
|
+
}
|
|
460
|
+
const store = {
|
|
461
|
+
...currentStore,
|
|
462
|
+
routingContext: {
|
|
463
|
+
apiKey: options.ddApiKey,
|
|
464
|
+
site: options.ddSite
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
return storage.run(store, fn)
|
|
468
|
+
}
|
|
469
|
+
|
|
443
470
|
flush () {
|
|
444
471
|
if (!this.enabled) return
|
|
445
472
|
|
|
@@ -26,7 +26,10 @@ const {
|
|
|
26
26
|
TAGS,
|
|
27
27
|
PARENT_ID_KEY,
|
|
28
28
|
SESSION_ID,
|
|
29
|
-
NAME
|
|
29
|
+
NAME,
|
|
30
|
+
INPUT_PROMPT,
|
|
31
|
+
ROUTING_API_KEY,
|
|
32
|
+
ROUTING_SITE
|
|
30
33
|
} = require('./constants/tags')
|
|
31
34
|
const { UNSERIALIZABLE_VALUE_TEXT } = require('./constants/text')
|
|
32
35
|
const telemetry = require('./telemetry')
|
|
@@ -78,7 +81,13 @@ class LLMObsSpanProcessor {
|
|
|
78
81
|
telemetry.incrementLLMObsSpanFinishedCount(span)
|
|
79
82
|
if (formattedEvent == null) return
|
|
80
83
|
|
|
81
|
-
|
|
84
|
+
const mlObsTags = LLMObsTagger.tagMap.get(span)
|
|
85
|
+
const routing = {
|
|
86
|
+
apiKey: mlObsTags[ROUTING_API_KEY],
|
|
87
|
+
site: mlObsTags[ROUTING_SITE]
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
this.#writer.append(formattedEvent, routing)
|
|
82
91
|
} catch (e) {
|
|
83
92
|
// this should be a rare case
|
|
84
93
|
// we protect against unserializable properties in the format function, and in
|
|
@@ -122,11 +131,6 @@ class LLMObsSpanProcessor {
|
|
|
122
131
|
inputType = 'value'
|
|
123
132
|
}
|
|
124
133
|
|
|
125
|
-
// Handle prompt metadata for reusable prompts
|
|
126
|
-
if (mlObsTags['_ml_obs.meta.input.prompt']) {
|
|
127
|
-
input.prompt = mlObsTags['_ml_obs.meta.input.prompt']
|
|
128
|
-
}
|
|
129
|
-
|
|
130
134
|
if (spanKind === 'llm' && mlObsTags[OUTPUT_MESSAGES]) {
|
|
131
135
|
llmObsSpan.output = mlObsTags[OUTPUT_MESSAGES]
|
|
132
136
|
outputType = 'messages'
|
|
@@ -177,6 +181,12 @@ class LLMObsSpanProcessor {
|
|
|
177
181
|
if (input) meta.input = input
|
|
178
182
|
if (output) meta.output = output
|
|
179
183
|
|
|
184
|
+
const prompt = mlObsTags[INPUT_PROMPT]
|
|
185
|
+
if (prompt && spanKind === 'llm') {
|
|
186
|
+
// by this point, we should have logged a warning if the span kind was not llm
|
|
187
|
+
meta.input.prompt = prompt
|
|
188
|
+
}
|
|
189
|
+
|
|
180
190
|
const llmObsSpanEvent = {
|
|
181
191
|
trace_id: span.context().toTraceId(true),
|
|
182
192
|
span_id: span.context().toSpanId(),
|
|
@@ -28,7 +28,15 @@ const {
|
|
|
28
28
|
REASONING_OUTPUT_TOKENS_METRIC_KEY,
|
|
29
29
|
INTEGRATION,
|
|
30
30
|
DECORATOR,
|
|
31
|
-
PROPAGATED_ML_APP_KEY
|
|
31
|
+
PROPAGATED_ML_APP_KEY,
|
|
32
|
+
DEFAULT_PROMPT_NAME,
|
|
33
|
+
INTERNAL_CONTEXT_VARIABLE_KEYS,
|
|
34
|
+
INTERNAL_QUERY_VARIABLE_KEYS,
|
|
35
|
+
INPUT_PROMPT,
|
|
36
|
+
ROUTING_API_KEY,
|
|
37
|
+
ROUTING_SITE,
|
|
38
|
+
PROMPT_TRACKING_INSTRUMENTATION_METHOD,
|
|
39
|
+
INSTRUMENTATION_METHOD_ANNOTATED
|
|
32
40
|
} = require('./constants/tags')
|
|
33
41
|
const { storage } = require('./storage')
|
|
34
42
|
|
|
@@ -110,6 +118,18 @@ class LLMObsTagger {
|
|
|
110
118
|
// apply annotation context name
|
|
111
119
|
const annotationContextName = annotationContext?.name
|
|
112
120
|
if (annotationContextName) this._setTag(span, NAME, annotationContextName)
|
|
121
|
+
|
|
122
|
+
// apply annotation context prompt
|
|
123
|
+
const annotationContextPrompt = annotationContext?.prompt
|
|
124
|
+
if (annotationContextPrompt) this.tagPrompt(span, annotationContextPrompt)
|
|
125
|
+
|
|
126
|
+
const routing = storage.getStore()?.routingContext
|
|
127
|
+
if (routing) {
|
|
128
|
+
this._setTag(span, ROUTING_API_KEY, routing.apiKey)
|
|
129
|
+
if (routing.site) {
|
|
130
|
+
this._setTag(span, ROUTING_SITE, routing.site)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
113
133
|
}
|
|
114
134
|
|
|
115
135
|
// TODO: similarly for the following `tag` methods,
|
|
@@ -194,6 +214,160 @@ class LLMObsTagger {
|
|
|
194
214
|
}
|
|
195
215
|
}
|
|
196
216
|
|
|
217
|
+
/**
|
|
218
|
+
* Tags a prompt on an LLMObs span.
|
|
219
|
+
* @param {import('../opentracing/span')} span
|
|
220
|
+
* @param {string | Record<string, unknown>} prompt
|
|
221
|
+
* @param {boolean?} strictValidation
|
|
222
|
+
* whether to validate the prompt against the strict schema, used for auto-instrumentation
|
|
223
|
+
*/
|
|
224
|
+
tagPrompt (span, prompt, strictValidation = false) {
|
|
225
|
+
const spanKind = registry.get(span)?.[SPAN_KIND]
|
|
226
|
+
if (spanKind !== 'llm') {
|
|
227
|
+
log.warn('Dropping prompt on non-LLM span kind, annotating prompts is only supported for LLM span kinds.')
|
|
228
|
+
return
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (!prompt || typeof prompt !== 'object') {
|
|
232
|
+
this.#handleFailure('Prompt must be an object.', 'invalid_prompt')
|
|
233
|
+
return
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const mlApp = registry.get(span)?.[ML_APP] // this should be defined at this point
|
|
237
|
+
const {
|
|
238
|
+
id,
|
|
239
|
+
version,
|
|
240
|
+
tags,
|
|
241
|
+
variables,
|
|
242
|
+
template,
|
|
243
|
+
contextVariables,
|
|
244
|
+
queryVariables,
|
|
245
|
+
} = prompt
|
|
246
|
+
|
|
247
|
+
if (strictValidation) {
|
|
248
|
+
if (id == null) {
|
|
249
|
+
this.#handleFailure('Prompt ID is required.', 'invalid_prompt')
|
|
250
|
+
return
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (template == null) {
|
|
254
|
+
this.#handleFailure('Prompt template is required.', 'invalid_prompt')
|
|
255
|
+
return
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const finalPromptId = id ?? `${mlApp}_${DEFAULT_PROMPT_NAME}`
|
|
260
|
+
const finalCtxVariablesKeys = contextVariables ?? ['context']
|
|
261
|
+
const finalQueryVariablesKeys = queryVariables ?? ['question']
|
|
262
|
+
|
|
263
|
+
// validate prompt id
|
|
264
|
+
if (typeof finalPromptId !== 'string') {
|
|
265
|
+
this.#handleFailure('Prompt ID must be a string.', 'invalid_prompt')
|
|
266
|
+
return
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// validate prompt context variables keys
|
|
270
|
+
if (Array.isArray(finalCtxVariablesKeys)) {
|
|
271
|
+
for (const key of finalCtxVariablesKeys) {
|
|
272
|
+
if (typeof key !== 'string') {
|
|
273
|
+
this.#handleFailure('Prompt context variables keys must be an array of strings.', 'invalid_prompt')
|
|
274
|
+
return
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
} else if (finalCtxVariablesKeys) {
|
|
278
|
+
this.#handleFailure('Prompt context variables keys must be an array.', 'invalid_prompt')
|
|
279
|
+
return
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// validate prompt query variables keys
|
|
283
|
+
if (Array.isArray(finalQueryVariablesKeys)) {
|
|
284
|
+
for (const key of finalQueryVariablesKeys) {
|
|
285
|
+
if (typeof key !== 'string') {
|
|
286
|
+
this.#handleFailure('Prompt query variables keys must be an array of strings.', 'invalid_prompt')
|
|
287
|
+
return
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
} else if (finalQueryVariablesKeys) {
|
|
291
|
+
this.#handleFailure('Prompt query variables keys must be an array.', 'invalid_prompt')
|
|
292
|
+
return
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// validate prompt version
|
|
296
|
+
if (version && typeof version !== 'string') {
|
|
297
|
+
this.#handleFailure('Prompt version must be a string.', 'invalid_prompt')
|
|
298
|
+
return
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// validate prompt tags
|
|
302
|
+
if (tags && (typeof tags !== 'object' || tags instanceof Map)) {
|
|
303
|
+
this.#handleFailure('Prompt tags must be an non-Map object.', 'invalid_prompt')
|
|
304
|
+
return
|
|
305
|
+
} else if (tags) {
|
|
306
|
+
for (const [key, value] of Object.entries(tags)) {
|
|
307
|
+
if (typeof key !== 'string' || typeof value !== 'string') {
|
|
308
|
+
this.#handleFailure('Prompt tags must be an object of string key-value pairs.', 'invalid_prompt')
|
|
309
|
+
return
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// validate prompt template is either string or list of messages
|
|
315
|
+
if (template && !(typeof template === 'string' || Array.isArray(template))) {
|
|
316
|
+
this.#handleFailure('Prompt template must be a string or an array of messages.', 'invalid_prompt')
|
|
317
|
+
return
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (Array.isArray(template)) {
|
|
321
|
+
for (const message of template) {
|
|
322
|
+
if (typeof message !== 'object' || !message.role || !message.content) {
|
|
323
|
+
this.#handleFailure(
|
|
324
|
+
'Prompt chat template must be an array of objects with role and content properties.', 'invalid_prompt'
|
|
325
|
+
)
|
|
326
|
+
return
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// validate variables are a string-string mapping
|
|
332
|
+
if (variables && (typeof variables !== 'object' || variables instanceof Map)) {
|
|
333
|
+
this.#handleFailure('Prompt variables must be an non-Map object.', 'invalid_prompt')
|
|
334
|
+
return
|
|
335
|
+
} else if (variables) {
|
|
336
|
+
for (const [key, value] of Object.entries(variables)) {
|
|
337
|
+
if (typeof key !== 'string' || typeof value !== 'string') {
|
|
338
|
+
this.#handleFailure('Prompt variables must be an object of string key-value pairs.', 'invalid_prompt')
|
|
339
|
+
return
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
let finalTemplate, finalChatTemplate
|
|
345
|
+
if (typeof template === 'string') {
|
|
346
|
+
finalTemplate = template
|
|
347
|
+
} else if (Array.isArray(template)) {
|
|
348
|
+
finalChatTemplate = template.map(message => ({ role: message.role, content: message.content }))
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const validatedPrompt = {}
|
|
352
|
+
if (finalPromptId) validatedPrompt.id = finalPromptId
|
|
353
|
+
if (version) validatedPrompt.version = version
|
|
354
|
+
if (variables) validatedPrompt.variables = variables
|
|
355
|
+
if (finalTemplate) validatedPrompt.template = finalTemplate
|
|
356
|
+
if (finalChatTemplate?.length) validatedPrompt.chat_template = finalChatTemplate
|
|
357
|
+
if (tags) validatedPrompt.tags = tags
|
|
358
|
+
if (finalCtxVariablesKeys) validatedPrompt[INTERNAL_CONTEXT_VARIABLE_KEYS] = finalCtxVariablesKeys
|
|
359
|
+
if (finalQueryVariablesKeys) validatedPrompt[INTERNAL_QUERY_VARIABLE_KEYS] = finalQueryVariablesKeys
|
|
360
|
+
|
|
361
|
+
const currentPrompt = registry.get(span)?.[INPUT_PROMPT]
|
|
362
|
+
if (currentPrompt) {
|
|
363
|
+
Object.assign(currentPrompt, validatedPrompt)
|
|
364
|
+
} else {
|
|
365
|
+
this._setTag(span, INPUT_PROMPT, validatedPrompt)
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
this.tagSpanTags(span, { [PROMPT_TRACKING_INSTRUMENTATION_METHOD]: INSTRUMENTATION_METHOD_ANNOTATED })
|
|
369
|
+
}
|
|
370
|
+
|
|
197
371
|
changeKind (span, newKind) {
|
|
198
372
|
this._setTag(span, SPAN_KIND, newKind)
|
|
199
373
|
}
|