dd-trace 6.8.0 → 6.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ci/diagnose.js +27 -25
- package/ci/init.js +6 -6
- package/ci/runbook.md +114 -20
- package/ci/test-optimization-validation/approval-artifacts.js +36 -3
- package/ci/test-optimization-validation/approval.js +78 -22
- package/ci/test-optimization-validation/blocker-category.js +24 -0
- package/ci/test-optimization-validation/ci-discovery.js +23 -17
- package/ci/test-optimization-validation/ci-package-scripts.js +34 -6
- package/ci/test-optimization-validation/ci-remediation.js +1 -1
- package/ci/test-optimization-validation/cli.js +280 -52
- package/ci/test-optimization-validation/command-blocker.js +159 -20
- package/ci/test-optimization-validation/command-output-policy.js +9 -3
- package/ci/test-optimization-validation/command-runner.js +9 -7
- package/ci/test-optimization-validation/environment.js +4 -2
- package/ci/test-optimization-validation/executable.js +15 -13
- package/ci/test-optimization-validation/execution-lock.js +70 -0
- package/ci/test-optimization-validation/framework-adapters/cucumber-validation.json +3 -0
- package/ci/test-optimization-validation/framework-adapters/cucumber.js +657 -5
- package/ci/test-optimization-validation/framework-adapters/vitest.js +517 -0
- package/ci/test-optimization-validation/generated-files.js +38 -28
- package/ci/test-optimization-validation/generated-test-contract.js +8 -4
- package/ci/test-optimization-validation/generated-verifier.js +24 -17
- package/ci/test-optimization-validation/literal-glob.js +52 -0
- package/ci/test-optimization-validation/manifest-loader.js +3 -0
- package/ci/test-optimization-validation/manifest-scaffold.js +300 -50
- package/ci/test-optimization-validation/manifest-schema.js +80 -17
- package/ci/test-optimization-validation/offline-fixtures.js +13 -4
- package/ci/test-optimization-validation/package-check.js +94 -0
- package/ci/test-optimization-validation/plan-writer.js +183 -10
- package/ci/test-optimization-validation/preflight-runner.js +110 -31
- package/ci/test-optimization-validation/project-build-artifact.js +31 -0
- package/ci/test-optimization-validation/redaction.js +18 -27
- package/ci/test-optimization-validation/report-writer.js +182 -48
- package/ci/test-optimization-validation/result-semantics.js +20 -3
- package/ci/test-optimization-validation/runner-command.js +40 -20
- package/ci/test-optimization-validation/runner-contract.js +79 -43
- package/ci/test-optimization-validation/scenarios/basic-reporting.js +59 -5
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +141 -35
- package/ci/test-optimization-validation/scenarios/helpers.js +30 -6
- package/ci/test-optimization-validation/static-diagnosis.js +10 -3
- package/ci/test-optimization-validation/test-output.js +2 -1
- package/ci/test-optimization-validation/validation-blocker.js +21 -0
- package/ci/vitest-no-worker-init-setup.mjs +140 -42
- package/index.d.ts +140 -1
- package/init.js +1 -17
- package/initialize.mjs +11 -0
- package/loader-hook.mjs +6 -4
- package/package.json +8 -5
- package/packages/datadog-core/src/utils/src/parse-tags.js +0 -1
- package/packages/datadog-esbuild/src/utils.js +16 -1
- package/packages/datadog-instrumentations/src/ai.js +61 -34
- package/packages/datadog-instrumentations/src/bluebird.js +6 -6
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +81 -5
- package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +111 -6
- package/packages/datadog-instrumentations/src/cucumber.js +124 -47
- package/packages/datadog-instrumentations/src/dns.js +21 -2
- package/packages/datadog-instrumentations/src/electron.js +1 -0
- package/packages/datadog-instrumentations/src/fastify.js +3 -1
- package/packages/datadog-instrumentations/src/grpc/client.js +4 -2
- package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +3 -1
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +629 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +34 -9
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +199 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +20 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.js +32 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +62 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +83 -1
- package/packages/datadog-instrumentations/src/helpers/router-helper.js +37 -6
- package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +1 -1
- package/packages/datadog-instrumentations/src/jest.js +980 -234
- package/packages/datadog-instrumentations/src/mocha/main.js +108 -10
- package/packages/datadog-instrumentations/src/mocha/utils.js +66 -50
- package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +25 -0
- package/packages/datadog-instrumentations/src/mocha/worker.js +12 -18
- package/packages/datadog-instrumentations/src/mysql.js +46 -16
- package/packages/datadog-instrumentations/src/mysql2.js +119 -18
- package/packages/datadog-instrumentations/src/next.js +155 -23
- package/packages/datadog-instrumentations/src/openai.js +14 -0
- package/packages/datadog-instrumentations/src/path-to-regexp.js +67 -1
- package/packages/datadog-instrumentations/src/pg.js +209 -11
- package/packages/datadog-instrumentations/src/playwright.js +173 -79
- package/packages/datadog-instrumentations/src/promise-js.js +4 -4
- package/packages/datadog-instrumentations/src/promise.js +3 -3
- package/packages/datadog-instrumentations/src/router.js +195 -19
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +85 -13
- package/packages/datadog-instrumentations/src/vitest-main.js +528 -72
- package/packages/datadog-instrumentations/src/vitest-util.js +106 -12
- package/packages/datadog-instrumentations/src/vitest-worker.js +382 -40
- package/packages/datadog-instrumentations/src/webdriverio.js +212 -22
- package/packages/datadog-instrumentations/src/when.js +3 -3
- package/packages/datadog-instrumentations/src/ws.js +5 -1
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +12 -6
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +59 -48
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -5
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -5
- package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +1 -3
- package/packages/datadog-plugin-cucumber/src/index.js +5 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +51 -47
- package/packages/datadog-plugin-cypress/src/support.js +7 -4
- package/packages/datadog-plugin-electron/src/net.js +10 -4
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +31 -17
- package/packages/datadog-plugin-http/src/client.js +1 -1
- package/packages/datadog-plugin-http2/src/client.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +6 -22
- package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +2 -3
- package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -3
- package/packages/datadog-plugin-kafkajs/src/producer.js +5 -3
- package/packages/datadog-plugin-kafkajs/src/utils.js +65 -7
- package/packages/datadog-plugin-mocha/src/index.js +832 -4
- package/packages/datadog-plugin-mysql/src/index.js +37 -0
- package/packages/datadog-plugin-next/src/index.js +51 -21
- package/packages/datadog-plugin-openai/src/tracing.js +4 -0
- package/packages/datadog-plugin-openai-agents/src/integration.js +278 -69
- package/packages/datadog-plugin-openai-agents/src/processor.js +16 -4
- package/packages/datadog-plugin-pg/src/index.js +66 -1
- package/packages/datadog-plugin-playwright/src/index.js +1 -1
- package/packages/datadog-plugin-vitest/src/index.js +103 -27
- package/packages/datadog-plugin-ws/src/util.js +2 -1
- package/packages/datadog-shimmer/src/shimmer.js +6 -2
- package/packages/dd-trace/src/aiguard/sdk.js +3 -0
- package/packages/dd-trace/src/appsec/api_security/normalized-route.js +453 -0
- package/packages/dd-trace/src/appsec/{blocking.js → blocking/index.js} +26 -6
- package/packages/dd-trace/src/appsec/handlers/auth.js +62 -0
- package/packages/dd-trace/src/appsec/handlers/http-request.js +228 -0
- package/packages/dd-trace/src/appsec/handlers/http-response.js +78 -0
- package/packages/dd-trace/src/appsec/handlers/http-shared.js +24 -0
- package/packages/dd-trace/src/appsec/handlers/payments.js +104 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
- package/packages/dd-trace/src/appsec/index.js +48 -476
- package/packages/dd-trace/src/appsec/sdk/track_event.js +9 -0
- package/packages/dd-trace/src/carrier.js +356 -0
- package/packages/dd-trace/src/ci-visibility/coverage-report-discovery.js +46 -14
- package/packages/dd-trace/src/ci-visibility/efd-retry-policy.js +89 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +27 -13
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +17 -0
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +21 -1
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +22 -7
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +9 -14
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +21 -5
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +1 -1
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +5 -1
- package/packages/dd-trace/src/config/generated-config-types.d.ts +5 -0
- package/packages/dd-trace/src/config/helper.js +2 -0
- package/packages/dd-trace/src/config/index.js +16 -5
- package/packages/dd-trace/src/config/remote_config.js +1 -1
- package/packages/dd-trace/src/config/supported-configurations.json +18 -0
- package/packages/dd-trace/src/datastreams/context.js +6 -1
- package/packages/dd-trace/src/datastreams/manager.js +5 -1
- package/packages/dd-trace/src/datastreams/pathway.js +23 -22
- package/packages/dd-trace/src/datastreams/processor.js +3 -2
- package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -1
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +0 -1
- package/packages/dd-trace/src/guardrails/apply-pm2-env.js +16 -0
- package/packages/dd-trace/src/guardrails/log.js +1 -10
- package/packages/dd-trace/src/llmobs/constants/tags.js +5 -0
- package/packages/dd-trace/src/llmobs/eval-metric.js +249 -0
- package/packages/dd-trace/src/llmobs/experiments/index.js +33 -25
- package/packages/dd-trace/src/llmobs/experiments/util.js +4 -2
- package/packages/dd-trace/src/llmobs/index.js +34 -2
- package/packages/dd-trace/src/llmobs/noop.js +2 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +5 -5
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +149 -68
- package/packages/dd-trace/src/llmobs/span_processor.js +16 -0
- package/packages/dd-trace/src/llmobs/tagger.js +96 -0
- package/packages/dd-trace/src/llmobs/telemetry.js +18 -0
- package/packages/dd-trace/src/llmobs/util.js +78 -0
- package/packages/dd-trace/src/llmobs/writers/base.js +3 -0
- package/packages/dd-trace/src/llmobs/writers/util.js +2 -0
- package/packages/dd-trace/src/log/channels.js +1 -9
- package/packages/dd-trace/src/log/levels.js +12 -0
- package/packages/dd-trace/src/openfeature/writers/exposures.js +1 -1
- package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/meter.js +8 -4
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -1
- package/packages/dd-trace/src/opentelemetry/span.js +4 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +126 -120
- package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +4 -8
- package/packages/dd-trace/src/plugin_manager.js +2 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +21 -5
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/util/ci.js +6 -0
- package/packages/dd-trace/src/plugins/util/git.js +6 -4
- package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +2 -2
- package/packages/dd-trace/src/plugins/util/ip_extractor.js +1 -1
- package/packages/{datadog-plugin-jest/src/util.js → dd-trace/src/plugins/util/jest.js} +3 -62
- package/packages/dd-trace/src/plugins/util/stacktrace.js +3 -0
- package/packages/dd-trace/src/plugins/util/test.js +397 -74
- package/packages/dd-trace/src/plugins/util/url.js +1 -1
- package/packages/dd-trace/src/plugins/util/web.js +76 -3
- package/packages/dd-trace/src/profiler.js +1 -1
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/events.js +2 -2
- package/packages/dd-trace/src/proxy.js +1 -0
- package/packages/dd-trace/src/ritm.js +5 -0
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +4 -2
- package/packages/dd-trace/src/sampling_rule.js +4 -2
- package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +1 -1
- package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +1 -1
- package/packages/dd-trace/src/telemetry/logs/index.js +3 -0
- package/packages/dd-trace/src/telemetry/telemetry.js +1 -1
- package/vendor/dist/pprof-format/index.js +1 -1
- package/version.js +10 -8
- /package/packages/dd-trace/src/appsec/{blocked_templates.js → blocking/templates.js} +0 -0
|
@@ -16,6 +16,11 @@ module.exports = {
|
|
|
16
16
|
PROPAGATED_ML_APP_KEY: '_dd.p.llmobs_ml_app',
|
|
17
17
|
PROPAGATED_SESSION_ID_KEY: '_dd.p.llmobs_sid',
|
|
18
18
|
PARENT_ID_KEY: '_ml_obs.llmobs_parent_id',
|
|
19
|
+
|
|
20
|
+
PARENT_AGENT_NAME: '_ml_obs.parent_agent_name',
|
|
21
|
+
PARENT_AGENT_SPAN_ID: '_ml_obs.parent_agent_span_id',
|
|
22
|
+
PROPAGATED_PARENT_AGENT_ID_KEY: '_dd.p.llmobs_pagent_span_id',
|
|
23
|
+
PROPAGATED_PARENT_AGENT_NAME_KEY: '_dd.p.llmobs_pagent_name',
|
|
19
24
|
PROPAGATED_SAMPLE_RATE_KEY: '_dd.p.llmobs_sr',
|
|
20
25
|
PROPAGATED_SAMPLING_DECISION_KEY: '_dd.p.llmobs_sd',
|
|
21
26
|
SAMPLE_RATE: '_ml_obs.sample_rate',
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const tracerVersion = require('../../../../package.json').version
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Kind of event submitted through the evaluation metrics intake. Both kinds share the
|
|
7
|
+
* writer, the endpoint and most of their validation, and are told apart on the wire by
|
|
8
|
+
* the `event_kind` field.
|
|
9
|
+
* @typedef {'evaluation' | 'feedback'} MetricEventKind
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Tag value the intake accepts: a string, a nullish value, or anything the SDK can coerce
|
|
14
|
+
* through `toString()`.
|
|
15
|
+
* @typedef {string | null | undefined | { toString?: unknown }} MetricTagValue
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/** @type {string[]} */
|
|
19
|
+
const EVALUATION_METRIC_TYPES = ['categorical', 'score', 'boolean', 'json']
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* End-user feedback additionally accepts free-form text values.
|
|
23
|
+
* @type {string[]}
|
|
24
|
+
*/
|
|
25
|
+
const FEEDBACK_METRIC_TYPES = [...EVALUATION_METRIC_TYPES, 'text']
|
|
26
|
+
|
|
27
|
+
/** @type {Record<MetricEventKind, string[]>} */
|
|
28
|
+
const METRIC_TYPES = {
|
|
29
|
+
evaluation: EVALUATION_METRIC_TYPES,
|
|
30
|
+
feedback: FEEDBACK_METRIC_TYPES,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Feedback target option accepted by the SDK, mapped to the payload key it is sent as. `span` and
|
|
35
|
+
* `spanId` are two ways of naming the same target.
|
|
36
|
+
* @type {Record<string, string>}
|
|
37
|
+
*/
|
|
38
|
+
const FEEDBACK_TARGET_KEYS = {
|
|
39
|
+
span: 'span_id',
|
|
40
|
+
spanId: 'span_id',
|
|
41
|
+
traceId: 'trace_id',
|
|
42
|
+
sessionId: 'session_id',
|
|
43
|
+
feedbackJoinKey: 'feedback_join_key',
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// The validation is shared but the user-facing wording is per-kind. Keeping the terms in one
|
|
47
|
+
// table avoids threading three near-identical strings through every validator.
|
|
48
|
+
/** @type {Record<MetricEventKind, { metric: string, metrics: string, data: string }>} */
|
|
49
|
+
const TERMS = {
|
|
50
|
+
evaluation: {
|
|
51
|
+
metric: 'evaluation metric',
|
|
52
|
+
metrics: 'evaluation metrics',
|
|
53
|
+
data: 'Evaluation metric data',
|
|
54
|
+
},
|
|
55
|
+
feedback: {
|
|
56
|
+
metric: 'feedback metric',
|
|
57
|
+
metrics: 'feedback metrics',
|
|
58
|
+
data: 'Feedback data',
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @param {string[]} metricTypes
|
|
64
|
+
* @returns {string} the quoted list, e.g. `"categorical", "score" or "json"`
|
|
65
|
+
*/
|
|
66
|
+
function formatMetricTypes (metricTypes) {
|
|
67
|
+
const quoted = metricTypes.map(metricType => `"${metricType}"`)
|
|
68
|
+
return `${quoted.slice(0, -1).join(', ')} or ${quoted.at(-1)}`
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Built once at load time: only ever used to build an error message.
|
|
72
|
+
/** @type {Record<MetricEventKind, string>} */
|
|
73
|
+
const METRIC_TYPES_MESSAGE = {
|
|
74
|
+
evaluation: formatMetricTypes(EVALUATION_METRIC_TYPES),
|
|
75
|
+
feedback: formatMetricTypes(FEEDBACK_METRIC_TYPES),
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Builds an error carrying the telemetry error tag the SDK reports for the failed submission.
|
|
80
|
+
* @param {string} errorTag - Telemetry `error_type` tag value.
|
|
81
|
+
* @param {string} message - User-facing message.
|
|
82
|
+
* @returns {Error} the tagged error, to be thrown by the caller.
|
|
83
|
+
*/
|
|
84
|
+
function taggedError (errorTag, message) {
|
|
85
|
+
const error = new Error(message)
|
|
86
|
+
// Same shape as the tagger's failures, which the SDK already reads back as `e.ddErrorTag`.
|
|
87
|
+
Object.defineProperty(error, 'ddErrorTag', { get () { return errorTag } })
|
|
88
|
+
return error
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @param {MetricEventKind} kind
|
|
93
|
+
* @returns {Error} the tagged error, to be thrown by the caller.
|
|
94
|
+
*/
|
|
95
|
+
function invalidTagsError (kind) {
|
|
96
|
+
return taggedError('invalid_tags', `Failed to parse tags. Tags for ${TERMS[kind].metrics} must be strings`)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @param {unknown} timestampMs
|
|
101
|
+
* @param {MetricEventKind} kind
|
|
102
|
+
* @returns {void}
|
|
103
|
+
*/
|
|
104
|
+
function validateTimestamp (timestampMs, kind) {
|
|
105
|
+
if (typeof timestampMs !== 'number' || timestampMs < 0) {
|
|
106
|
+
throw taggedError(
|
|
107
|
+
'invalid_timestamp',
|
|
108
|
+
`timestampMs must be a non-negative integer. ${TERMS[kind].data} will not be sent`
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Validates the label and returns it as sent on the wire.
|
|
115
|
+
* @param {unknown} label
|
|
116
|
+
* @param {MetricEventKind} kind
|
|
117
|
+
* @returns {unknown} the label, coerced to a string for feedback and left untouched for evaluations.
|
|
118
|
+
*/
|
|
119
|
+
function validateLabel (label, kind) {
|
|
120
|
+
if (!label) {
|
|
121
|
+
throw taggedError('invalid_metric_label', `label must be the specified name of the ${TERMS[kind].metric}`)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Evaluations keep the looser check they shipped with: a dotted or non-string label reaches the
|
|
125
|
+
// intake as-is. Aligning them with feedback rejects calls that succeed today, so it is deferred.
|
|
126
|
+
if (kind === 'evaluation') return label
|
|
127
|
+
|
|
128
|
+
// A dot makes the label unusable as a facet key.
|
|
129
|
+
const labelValue = String(label)
|
|
130
|
+
if (labelValue.includes('.')) {
|
|
131
|
+
throw taggedError('invalid_label_value', 'label value must not contain a "."')
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return labelValue
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* @param {string | undefined} metricType - Already lower-cased metric type.
|
|
139
|
+
* @param {MetricEventKind} kind
|
|
140
|
+
* @returns {void}
|
|
141
|
+
*/
|
|
142
|
+
function validateMetricType (metricType, kind) {
|
|
143
|
+
if (!metricType || !METRIC_TYPES[kind].includes(metricType)) {
|
|
144
|
+
throw taggedError('invalid_metric_type', `metricType must be one of ${METRIC_TYPES_MESSAGE[kind]}`)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* @param {string | undefined} metricType - Metric type already validated for the submitted kind.
|
|
150
|
+
* @param {unknown} value
|
|
151
|
+
* @returns {void}
|
|
152
|
+
*/
|
|
153
|
+
function validateMetricValue (metricType, value) {
|
|
154
|
+
if (metricType === 'categorical' && typeof value !== 'string') {
|
|
155
|
+
throw taggedError('invalid_metric_value', 'value must be a string for a categorical metric.')
|
|
156
|
+
}
|
|
157
|
+
if (metricType === 'score' && typeof value !== 'number') {
|
|
158
|
+
throw taggedError('invalid_metric_value', 'value must be a number for a score metric.')
|
|
159
|
+
}
|
|
160
|
+
if (metricType === 'boolean' && typeof value !== 'boolean') {
|
|
161
|
+
throw taggedError('invalid_metric_value', 'value must be a boolean for a boolean metric')
|
|
162
|
+
}
|
|
163
|
+
if (metricType === 'json' && (typeof value !== 'object' || value == null || Array.isArray(value))) {
|
|
164
|
+
throw taggedError('invalid_metric_value', 'value must be a JSON object for a json metric')
|
|
165
|
+
}
|
|
166
|
+
if (metricType === 'text' && typeof value !== 'string') {
|
|
167
|
+
throw taggedError('invalid_metric_value', 'value must be a string for a text metric')
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* @param {unknown} assessment
|
|
173
|
+
* @returns {void}
|
|
174
|
+
*/
|
|
175
|
+
function validateAssessment (assessment) {
|
|
176
|
+
if (assessment != null && assessment !== 'pass' && assessment !== 'fail') {
|
|
177
|
+
throw taggedError('invalid_assessment', 'assessment must be pass or fail')
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* @param {unknown} reasoning
|
|
183
|
+
* @returns {void}
|
|
184
|
+
*/
|
|
185
|
+
function validateReasoning (reasoning) {
|
|
186
|
+
if (reasoning != null && typeof reasoning !== 'string') {
|
|
187
|
+
throw taggedError('invalid_reasoning', 'reasoning must be a string')
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Serializes the user-provided tags into the `key:value` list expected by the intake, along
|
|
193
|
+
* with the tags every submission carries.
|
|
194
|
+
* @param {Record<string, MetricTagValue> | undefined} tags - User-provided tags, coerced when not strings.
|
|
195
|
+
* @param {string} mlApp - Resolved ML app name.
|
|
196
|
+
* @param {MetricEventKind} kind
|
|
197
|
+
* @param {boolean} [otelEnabled] - Adds `source:otel` so the backend waits for OTel span conversion.
|
|
198
|
+
* @returns {string[]} the serialized tag list.
|
|
199
|
+
*/
|
|
200
|
+
function buildMetricTags (tags, mlApp, kind, otelEnabled = false) {
|
|
201
|
+
// An array or a string would otherwise be walked by index and produce tags named after
|
|
202
|
+
// their offsets, which the intake would happily store.
|
|
203
|
+
if (tags != null && (typeof tags !== 'object' || Array.isArray(tags))) {
|
|
204
|
+
throw invalidTagsError(kind)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const metricTags = {
|
|
208
|
+
'ddtrace.version': tracerVersion,
|
|
209
|
+
ml_app: mlApp,
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (tags) {
|
|
213
|
+
for (const key of Object.keys(tags)) {
|
|
214
|
+
const tag = tags[key]
|
|
215
|
+
if (typeof tag === 'string') {
|
|
216
|
+
metricTags[key] = tag
|
|
217
|
+
} else if (tag == null) {
|
|
218
|
+
// A nullish value can be intentional, so it is serialized rather than rejected. Checked
|
|
219
|
+
// before the `toString` lookup below, which would throw a raw TypeError on it.
|
|
220
|
+
metricTags[key] = String(tag)
|
|
221
|
+
} else if (typeof tag.toString === 'function') {
|
|
222
|
+
metricTags[key] = tag.toString()
|
|
223
|
+
} else {
|
|
224
|
+
// Every other value carries a `toString`, either its own or the one inherited from
|
|
225
|
+
// `Object.prototype`, so this is only reached for a null-prototype object.
|
|
226
|
+
throw invalidTagsError(kind)
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (otelEnabled) {
|
|
232
|
+
metricTags.source = 'otel'
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return Object.entries(metricTags).map(([key, value]) => `${key}:${value}`)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
module.exports = {
|
|
239
|
+
EVALUATION_METRIC_TYPES,
|
|
240
|
+
FEEDBACK_METRIC_TYPES,
|
|
241
|
+
FEEDBACK_TARGET_KEYS,
|
|
242
|
+
buildMetricTags,
|
|
243
|
+
validateAssessment,
|
|
244
|
+
validateLabel,
|
|
245
|
+
validateMetricType,
|
|
246
|
+
validateMetricValue,
|
|
247
|
+
validateReasoning,
|
|
248
|
+
validateTimestamp,
|
|
249
|
+
}
|
|
@@ -47,15 +47,17 @@ class Experiments {
|
|
|
47
47
|
: (descriptionOrOptions ?? {})
|
|
48
48
|
const dataset = new Dataset(this.#client, name, options.description ?? '')
|
|
49
49
|
const recordIds = new Set()
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (
|
|
56
|
-
|
|
50
|
+
if ((options.records) != null) {
|
|
51
|
+
for (const record of options.records) {
|
|
52
|
+
if (record.id !== undefined && (typeof record.id !== 'string' || record.id.length === 0)) {
|
|
53
|
+
throw new Error('record id must be a non-empty string')
|
|
54
|
+
}
|
|
55
|
+
if (record.id !== undefined) {
|
|
56
|
+
if (recordIds.has(record.id)) throw new Error(`Duplicate record id '${record.id}'`)
|
|
57
|
+
recordIds.add(record.id)
|
|
58
|
+
}
|
|
59
|
+
dataset.addRecord(new DatasetRecord(record.inputData, record.expectedOutput, record.metadata, record.id))
|
|
57
60
|
}
|
|
58
|
-
dataset.addRecord(new DatasetRecord(record.inputData, record.expectedOutput, record.metadata, record.id))
|
|
59
61
|
}
|
|
60
62
|
return dataset
|
|
61
63
|
}
|
|
@@ -82,13 +84,16 @@ class Experiments {
|
|
|
82
84
|
'GET',
|
|
83
85
|
`${API_BASE_PATH}/${projectId}/datasets?filter[name]=${encodeURIComponent(name)}`
|
|
84
86
|
)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
const datasets = listed?.data
|
|
88
|
+
if (datasets) {
|
|
89
|
+
for (const item of datasets) {
|
|
90
|
+
if (item?.attributes?.name === name) {
|
|
91
|
+
datasetId = String(item?.id ?? '')
|
|
92
|
+
description = String(item?.attributes?.description ?? '')
|
|
93
|
+
latestVersion = item?.attributes?.current_version ?? null
|
|
94
|
+
datasetVersion = version ?? latestVersion
|
|
95
|
+
break
|
|
96
|
+
}
|
|
92
97
|
}
|
|
93
98
|
}
|
|
94
99
|
if (datasetId === null) return false
|
|
@@ -107,16 +112,19 @@ class Experiments {
|
|
|
107
112
|
'GET',
|
|
108
113
|
`${API_BASE_PATH}/${projectId}/datasets/${datasetId}/records?${query.toString()}`
|
|
109
114
|
)
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
attrs?.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
115
|
+
const recordData = resp?.data
|
|
116
|
+
if (recordData) {
|
|
117
|
+
for (const item of recordData) {
|
|
118
|
+
const attrs = item?.attributes ?? item
|
|
119
|
+
const recordId = String(item?.id ?? attrs?.id ?? '')
|
|
120
|
+
recs.push(new DatasetRecord(
|
|
121
|
+
attrs?.input ?? null,
|
|
122
|
+
attrs?.expected_output ?? null,
|
|
123
|
+
attrs?.metadata ?? {},
|
|
124
|
+
recordId === '' ? null : recordId
|
|
125
|
+
))
|
|
126
|
+
ids.push(recordId)
|
|
127
|
+
}
|
|
120
128
|
}
|
|
121
129
|
cursor = resp?.meta?.after ?? ''
|
|
122
130
|
if (!cursor) break
|
|
@@ -166,8 +166,10 @@ function stringify (value) {
|
|
|
166
166
|
*/
|
|
167
167
|
function buildTags (userTags, autoTags) {
|
|
168
168
|
const tags = new Map()
|
|
169
|
-
|
|
170
|
-
|
|
169
|
+
if ((userTags) != null) {
|
|
170
|
+
for (const [key, value] of Object.entries(userTags)) {
|
|
171
|
+
tags.set(key, `${key}:${value}`)
|
|
172
|
+
}
|
|
171
173
|
}
|
|
172
174
|
for (const [key, value] of Object.entries(autoTags)) {
|
|
173
175
|
if (value !== undefined && value !== null && value !== '') tags.set(key, `${key}:${value}`)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { channel } = require('dc-polyfill')
|
|
4
4
|
|
|
5
|
+
const { readDatadogTags, writeDatadogTags } = require('../carrier')
|
|
5
6
|
const log = require('../log')
|
|
6
7
|
const { DD_MAJOR } = require('../../../../version')
|
|
7
8
|
const startupLogs = require('../startup-log')
|
|
@@ -12,6 +13,8 @@ const {
|
|
|
12
13
|
PROPAGATED_ML_APP_KEY,
|
|
13
14
|
PROPAGATED_PARENT_ID_KEY,
|
|
14
15
|
PROPAGATED_SESSION_ID_KEY,
|
|
16
|
+
PROPAGATED_PARENT_AGENT_ID_KEY,
|
|
17
|
+
PROPAGATED_PARENT_AGENT_NAME_KEY,
|
|
15
18
|
SAMPLE_RATE,
|
|
16
19
|
SAMPLING_DECISION,
|
|
17
20
|
PROPAGATED_SAMPLE_RATE_KEY,
|
|
@@ -20,6 +23,7 @@ const {
|
|
|
20
23
|
PROPAGATED_TRACE_ID_KEY,
|
|
21
24
|
} = require('./constants/tags')
|
|
22
25
|
const { storage } = require('./storage')
|
|
26
|
+
const { agentNameWireSafe, appendOptionalPropagatedTag, resolveAgentAttribution, stripTagsetEntry } = require('./util')
|
|
23
27
|
const telemetry = require('./telemetry')
|
|
24
28
|
const LLMObsSpanProcessor = require('./span_processor')
|
|
25
29
|
const LLMObsEvalMetricsWriter = require('./writers/evaluations')
|
|
@@ -147,9 +151,17 @@ function handleLLMObsInjection ({ carrier }) {
|
|
|
147
151
|
|
|
148
152
|
if (!parentId && !mlApp && samplingDecision == null && !sessionId && !propagatedTraceId) return
|
|
149
153
|
|
|
154
|
+
// Propagate the nearest agent so spans in the downstream process attribute correctly. When the
|
|
155
|
+
// active span sits under a distributed agent, `resolveAgentAttribution` inherits the propagated
|
|
156
|
+
// id/name already on the parent's registry entry, so the chain survives multiple hops. Resolved
|
|
157
|
+
// after the bail-out above so we don't allocate when there is nothing to inject.
|
|
158
|
+
const { name: parentAgentName, spanId: parentAgentSpanId } = resolveAgentAttribution(
|
|
159
|
+
mlObsSpanTags, parent
|
|
160
|
+
)
|
|
161
|
+
|
|
150
162
|
// `_injectTags` only writes `x-datadog-tags` when the trace has `_dd.p.*`
|
|
151
163
|
// tags, so it may be undefined here — coalesce before appending.
|
|
152
|
-
const existing = carrier
|
|
164
|
+
const existing = readDatadogTags(carrier)
|
|
153
165
|
let tags = existing || ''
|
|
154
166
|
if (parentId) tags += `${tags ? ',' : ''}${PROPAGATED_PARENT_ID_KEY}=${parentId}`
|
|
155
167
|
if (mlApp) tags += `${tags ? ',' : ''}${PROPAGATED_ML_APP_KEY}=${mlApp}`
|
|
@@ -157,7 +169,27 @@ function handleLLMObsInjection ({ carrier }) {
|
|
|
157
169
|
if (sampleRate != null) tags += `${tags ? ',' : ''}${PROPAGATED_SAMPLE_RATE_KEY}=${sampleRate}`
|
|
158
170
|
if (samplingDecision != null) tags += `${tags ? ',' : ''}${PROPAGATED_SAMPLING_DECISION_KEY}=${samplingDecision}`
|
|
159
171
|
if (propagatedTraceId != null) tags += `${tags ? ',' : ''}${PROPAGATED_TRACE_ID_KEY}=${propagatedTraceId}`
|
|
160
|
-
|
|
172
|
+
// When a local agent attribution is resolved, strip any stale upstream pagent entries that
|
|
173
|
+
// `_injectTags` may have already written into the carrier (it propagates all `_dd.p.*` from
|
|
174
|
+
// `_trace.tags`). This ensures the downstream sees a consistent id-only or id+name pair
|
|
175
|
+
// rather than a mix from different hops. The id is always digit-safe; an unsafe name is wiped.
|
|
176
|
+
if (parentAgentSpanId) {
|
|
177
|
+
tags = stripTagsetEntry(tags, PROPAGATED_PARENT_AGENT_ID_KEY)
|
|
178
|
+
tags = stripTagsetEntry(tags, PROPAGATED_PARENT_AGENT_NAME_KEY)
|
|
179
|
+
}
|
|
180
|
+
const maxLength = globalTracerConfig.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH
|
|
181
|
+
const tagsWithId = appendOptionalPropagatedTag(
|
|
182
|
+
tags, PROPAGATED_PARENT_AGENT_ID_KEY, parentAgentSpanId, null, maxLength
|
|
183
|
+
)
|
|
184
|
+
// Only append the name when the id fit: a name without an id is unresolvable by the backend.
|
|
185
|
+
if (tagsWithId === tags) {
|
|
186
|
+
tags = tagsWithId
|
|
187
|
+
} else {
|
|
188
|
+
tags = appendOptionalPropagatedTag(
|
|
189
|
+
tagsWithId, PROPAGATED_PARENT_AGENT_NAME_KEY, parentAgentName, agentNameWireSafe, maxLength
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
if (tags !== existing) writeDatadogTags(carrier, tags)
|
|
161
193
|
}
|
|
162
194
|
|
|
163
195
|
function handleFlush () {
|
|
@@ -118,7 +118,7 @@ class DdTelemetryPlugin extends BaseLLMObsPlugin {
|
|
|
118
118
|
* @returns {string | undefined}
|
|
119
119
|
*/
|
|
120
120
|
findToolName (toolName, toolDescription) {
|
|
121
|
-
if (Number.isNaN(Number.parseInt(toolName))) return toolName
|
|
121
|
+
if (Number.isNaN(Number.parseInt(toolName, 10))) return toolName
|
|
122
122
|
|
|
123
123
|
for (const availableTool of this.#availableTools) {
|
|
124
124
|
const description = availableTool.description
|
|
@@ -322,7 +322,7 @@ function getToolNameFromTags (tags) {
|
|
|
322
322
|
const toolName = tags['ai.toolCall.name']
|
|
323
323
|
if (!toolName) return null
|
|
324
324
|
|
|
325
|
-
const parsedToolName = Number.parseInt(toolName)
|
|
325
|
+
const parsedToolName = Number.parseInt(toolName, 10)
|
|
326
326
|
if (!Number.isNaN(parsedToolName)) return null
|
|
327
327
|
|
|
328
328
|
return toolName
|
|
@@ -72,10 +72,10 @@ class BedrockRuntimeLLMObsPlugin extends BaseLLMObsPlugin {
|
|
|
72
72
|
const cacheWriteTokenCount = headers['x-amzn-bedrock-cache-write-input-token-count']
|
|
73
73
|
|
|
74
74
|
pendingTokenHeaders.set(requestId, {
|
|
75
|
-
inputTokensFromHeaders: inputTokenCount && Number.parseInt(inputTokenCount),
|
|
76
|
-
outputTokensFromHeaders: outputTokenCount && Number.parseInt(outputTokenCount),
|
|
77
|
-
cacheReadTokensFromHeaders: cacheReadTokenCount && Number.parseInt(cacheReadTokenCount),
|
|
78
|
-
cacheWriteTokensFromHeaders: cacheWriteTokenCount && Number.parseInt(cacheWriteTokenCount),
|
|
75
|
+
inputTokensFromHeaders: inputTokenCount && Number.parseInt(inputTokenCount, 10),
|
|
76
|
+
outputTokensFromHeaders: outputTokenCount && Number.parseInt(outputTokenCount, 10),
|
|
77
|
+
cacheReadTokensFromHeaders: cacheReadTokenCount && Number.parseInt(cacheReadTokenCount, 10),
|
|
78
|
+
cacheWriteTokensFromHeaders: cacheWriteTokenCount && Number.parseInt(cacheWriteTokenCount, 10),
|
|
79
79
|
})
|
|
80
80
|
})
|
|
81
81
|
|
|
@@ -138,7 +138,7 @@ class BedrockRuntimeLLMObsPlugin extends BaseLLMObsPlugin {
|
|
|
138
138
|
#tagCommon ({ span, requestParams, textAndResponseReason, tokensFromHeaders }) {
|
|
139
139
|
this._tagger.tagMetadata(span, {
|
|
140
140
|
temperature: Number.parseFloat(requestParams.temperature) || 0,
|
|
141
|
-
max_tokens: Number.parseInt(requestParams.maxTokens) || 0,
|
|
141
|
+
max_tokens: Number.parseInt(requestParams.maxTokens, 10) || 0,
|
|
142
142
|
})
|
|
143
143
|
this._tagger.tagLLMIO(span, requestParams.prompt, textAndResponseReason.messages)
|
|
144
144
|
this._tagger.tagMetrics(span, extractTokens({
|