dd-trace 6.16.0 → 6.18.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 +0 -23
- package/ci/test-optimization-validation/approval-artifacts.js +0 -3
- package/ci/test-optimization-validation/approval.js +0 -6
- package/ci/test-optimization-validation/artifact-id.js +0 -1
- package/ci/test-optimization-validation/bounded-json.js +0 -1
- package/ci/test-optimization-validation/cli.js +0 -12
- package/ci/test-optimization-validation/command-output-policy.js +0 -1
- package/ci/test-optimization-validation/command-runner.js +0 -3
- package/ci/test-optimization-validation/environment.js +0 -4
- package/ci/test-optimization-validation/executable-approval.js +0 -1
- package/ci/test-optimization-validation/executable.js +0 -5
- package/ci/test-optimization-validation/framework-adapters/cucumber.js +0 -6
- package/ci/test-optimization-validation/framework-adapters/cypress.js +0 -3
- package/ci/test-optimization-validation/framework-adapters/playwright.js +0 -6
- package/ci/test-optimization-validation/framework-adapters/vitest.js +0 -4
- package/ci/test-optimization-validation/generated-test-contract.js +0 -5
- package/ci/test-optimization-validation/generated-verifier.js +0 -1
- package/ci/test-optimization-validation/literal-environment.js +0 -1
- package/ci/test-optimization-validation/manifest-scaffold.js +0 -17
- package/ci/test-optimization-validation/manifest-schema.js +0 -21
- package/ci/test-optimization-validation/offline-fixtures.js +0 -4
- package/ci/test-optimization-validation/plan-writer.js +0 -11
- package/ci/test-optimization-validation/preflight-runner.js +0 -1
- package/ci/test-optimization-validation/redaction.js +0 -4
- package/ci/test-optimization-validation/report-writer.js +0 -16
- package/ci/test-optimization-validation/runner-command.js +0 -3
- package/ci/test-optimization-validation/runner-contract.js +0 -4
- package/ci/test-optimization-validation/safe-files.js +0 -1
- package/ci/test-optimization-validation/scenarios/basic-reporting.js +0 -1
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +0 -1
- package/ci/test-optimization-validation/source-text.js +0 -3
- package/ci/vitest-no-worker-init-setup.mjs +41 -16
- package/index.d.ts +208 -0
- package/package.json +17 -16
- package/packages/datadog-esbuild/src/utils.js +0 -2
- package/packages/datadog-instrumentations/src/aerospike.js +28 -6
- package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +0 -2
- package/packages/datadog-instrumentations/src/aws-sdk.js +0 -2
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +0 -2
- package/packages/datadog-instrumentations/src/console.js +97 -0
- package/packages/datadog-instrumentations/src/cucumber.js +77 -12
- package/packages/datadog-instrumentations/src/cypress-config.js +27 -38
- package/packages/datadog-instrumentations/src/helpers/bundler-register.js +6 -0
- package/packages/datadog-instrumentations/src/helpers/channel.js +0 -2
- package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/graphql-jit-runtime.js +0 -5
- package/packages/datadog-instrumentations/src/helpers/hooks.js +8 -6
- package/packages/datadog-instrumentations/src/helpers/instrumentation-utils.js +0 -3
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +0 -6
- package/packages/datadog-instrumentations/src/helpers/register.js +24 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +111 -26
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentation-registry.js +41 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +3 -17
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +55 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/postgres.js +36 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/supabase.js +128 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +13 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.js +28 -3
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +20 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms/postgres.js +388 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +0 -5
- package/packages/datadog-instrumentations/src/helpers/router-helper.js +0 -1
- package/packages/datadog-instrumentations/src/jest.js +165 -51
- package/packages/datadog-instrumentations/src/knex.js +0 -1
- package/packages/datadog-instrumentations/src/mariadb-bundle.js +0 -24
- package/packages/datadog-instrumentations/src/mariadb.js +0 -2
- package/packages/datadog-instrumentations/src/mocha/common.js +0 -1
- package/packages/datadog-instrumentations/src/mocha/main.js +25 -26
- package/packages/datadog-instrumentations/src/mocha/utils.js +119 -49
- package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +0 -6
- package/packages/datadog-instrumentations/src/mocha/worker.js +14 -17
- package/packages/datadog-instrumentations/src/mysql2.js +0 -2
- package/packages/datadog-instrumentations/src/openai-realtime/audio-accumulator.js +241 -0
- package/packages/datadog-instrumentations/src/openai-realtime/events.js +76 -0
- package/packages/datadog-instrumentations/src/openai-realtime/index.js +301 -0
- package/packages/datadog-instrumentations/src/openai-realtime/session.js +998 -0
- package/packages/datadog-instrumentations/src/openai-realtime/turns.js +211 -0
- package/packages/datadog-instrumentations/src/openai.js +51 -12
- package/packages/datadog-instrumentations/src/oracledb.js +9 -3
- package/packages/datadog-instrumentations/src/playwright-reporter.js +6 -20
- package/packages/datadog-instrumentations/src/playwright.js +280 -56
- package/packages/datadog-instrumentations/src/{azure-cosmos.js → postgres.js} +1 -1
- package/packages/datadog-instrumentations/src/router.js +0 -2
- package/packages/datadog-instrumentations/src/rum-browser-scripts.js +0 -2
- package/packages/datadog-instrumentations/src/sequelize.js +0 -1
- package/packages/datadog-instrumentations/src/supabase.js +15 -0
- package/packages/datadog-instrumentations/src/undici.js +7 -3
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +18 -18
- package/packages/datadog-instrumentations/src/vitest-main.js +121 -30
- package/packages/datadog-instrumentations/src/vitest-util.js +7 -1
- package/packages/datadog-instrumentations/src/vitest-worker.js +49 -13
- package/packages/datadog-instrumentations/src/webdriverio.js +44 -47
- package/packages/datadog-instrumentations/src/ws.js +41 -2
- package/packages/datadog-plugin-ai/src/utils.js +0 -2
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +0 -1
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +0 -2
- package/packages/datadog-plugin-aws-sdk/src/base.js +0 -1
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +0 -1
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +0 -1
- package/packages/datadog-plugin-aws-sdk/src/util.js +0 -1
- package/packages/datadog-plugin-azure-durable-functions/src/index.js +0 -1
- package/packages/datadog-plugin-cucumber/src/index.js +9 -0
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +211 -85
- package/packages/datadog-plugin-cypress/src/finalization.js +0 -3
- package/packages/datadog-plugin-cypress/src/index.js +3 -1
- package/packages/datadog-plugin-cypress/src/source-map-utils.js +0 -4
- package/packages/datadog-plugin-cypress/src/support.js +52 -7
- package/packages/datadog-plugin-graphql/src/resolve-error.js +1 -2
- package/packages/datadog-plugin-graphql/src/utils.js +0 -5
- package/packages/datadog-plugin-jest/src/index.js +12 -0
- package/packages/datadog-plugin-kafkajs/src/producer.js +0 -1
- package/packages/datadog-plugin-mocha/src/index.js +38 -11
- package/packages/datadog-plugin-mongodb-core/src/query.js +0 -1
- package/packages/datadog-plugin-openai/src/index.js +16 -1
- package/packages/datadog-plugin-openai/src/realtime.js +144 -0
- package/packages/datadog-plugin-openai-agents/src/integration.js +0 -4
- package/packages/datadog-plugin-openai-agents/src/util.js +0 -1
- package/packages/datadog-plugin-playwright/src/index.js +81 -9
- package/packages/datadog-plugin-postgres/src/index.js +167 -0
- package/packages/datadog-plugin-prisma/src/datadog-tracing-helper.js +0 -1
- package/packages/datadog-plugin-redis/src/index.js +15 -1
- package/packages/datadog-plugin-supabase/src/error.js +18 -0
- package/packages/datadog-plugin-supabase/src/index.js +21 -0
- package/packages/datadog-plugin-supabase/src/supabase-auth-js-gotrueclient-getuser.js +69 -0
- package/packages/datadog-plugin-supabase/src/supabase-functions-js-functionsclient-invoke.js +70 -0
- package/packages/datadog-plugin-supabase/src/supabase-postgrest-js-postgrestbuilder-then.js +122 -0
- package/packages/datadog-plugin-supabase/src/supabase-realtime-js-realtimechannel-send.js +66 -0
- package/packages/datadog-plugin-supabase/src/supabase-storage-js-handle-request.js +93 -0
- package/packages/datadog-plugin-supabase/src/url.js +17 -0
- package/packages/datadog-plugin-vitest/src/index.js +9 -0
- package/packages/datadog-plugin-ws/src/producer.js +1 -1
- package/packages/datadog-plugin-ws/src/util.js +0 -4
- package/packages/datadog-shimmer/src/shimmer.js +14 -10
- package/packages/datadog-turbopack/index.js +4 -7
- package/packages/datadog-turbopack/src/loader.js +6 -18
- package/packages/datadog-webpack/src/loader.js +0 -1
- package/packages/dd-trace/src/agent/info.js +5 -4
- package/packages/dd-trace/src/aiguard/client.js +2 -3
- package/packages/dd-trace/src/aiguard/evaluation.js +6 -12
- package/packages/dd-trace/src/aiguard/index.js +2 -2
- package/packages/dd-trace/src/aiguard/integrations/index.js +1 -1
- package/packages/dd-trace/src/aiguard/integrations/openai.js +81 -7
- package/packages/dd-trace/src/aiguard/messages/anthropic.js +0 -2
- package/packages/dd-trace/src/aiguard/messages/openai.js +64 -1
- package/packages/dd-trace/src/aiguard/messages/utils.js +0 -1
- package/packages/dd-trace/src/aiguard/redaction.js +0 -1
- package/packages/dd-trace/src/aiguard/sdk.js +1 -1
- package/packages/dd-trace/src/appsec/activation.js +1 -1
- package/packages/dd-trace/src/appsec/api_security/normalized-route.js +0 -5
- package/packages/dd-trace/src/appsec/api_security/sampler.js +0 -4
- package/packages/dd-trace/src/appsec/blocking/index.js +3 -3
- package/packages/dd-trace/src/appsec/downstream_requests.js +1 -4
- package/packages/dd-trace/src/appsec/iast/index.js +1 -1
- package/packages/dd-trace/src/appsec/iast/overhead-controller.js +5 -5
- package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +3 -3
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +8 -10
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +2 -2
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +4 -1
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +7 -7
- package/packages/dd-trace/src/appsec/index.js +2 -2
- package/packages/dd-trace/src/appsec/lambda.js +71 -2
- package/packages/dd-trace/src/appsec/rasp/utils.js +5 -1
- package/packages/dd-trace/src/appsec/remote_config.js +8 -6
- package/packages/dd-trace/src/appsec/reporter.js +10 -6
- package/packages/dd-trace/src/appsec/rule_manager.js +3 -3
- package/packages/dd-trace/src/appsec/sdk/track_event.js +0 -2
- package/packages/dd-trace/src/appsec/waf/index.js +2 -2
- package/packages/dd-trace/src/appsec/waf/waf_manager.js +8 -5
- package/packages/dd-trace/src/carrier.js +0 -5
- package/packages/dd-trace/src/ci-visibility/dynamic-atr-retries.js +104 -0
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +0 -2
- package/packages/dd-trace/src/ci-visibility/efd-retry-policy.js +34 -3
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +0 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +0 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +0 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/index.js +0 -4
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +0 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/sink.js +0 -2
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +37 -15
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +0 -3
- package/packages/dd-trace/src/ci-visibility/exporters/request.js +0 -11
- package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +0 -1
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +0 -1
- package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +0 -6
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +2 -2
- package/packages/dd-trace/src/ci-visibility/requests/rate-limit.js +0 -1
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +0 -5
- package/packages/dd-trace/src/ci-visibility/requests/video-request.js +0 -1
- package/packages/dd-trace/src/ci-visibility/telemetry.js +13 -0
- package/packages/dd-trace/src/ci-visibility/test-optimization-cache.js +0 -1
- package/packages/dd-trace/src/ci-visibility/test-screenshot.js +0 -2
- package/packages/dd-trace/src/ci-visibility/test-video.js +0 -1
- package/packages/dd-trace/src/config/defaults.js +3 -2
- package/packages/dd-trace/src/config/generated-config-types.d.ts +88 -82
- package/packages/dd-trace/src/config/helper.js +0 -1
- package/packages/dd-trace/src/config/index.js +31 -30
- package/packages/dd-trace/src/config/parsers.js +34 -6
- package/packages/dd-trace/src/config/remote_config.js +25 -109
- package/packages/dd-trace/src/config/supported-configurations.json +150 -10
- package/packages/dd-trace/src/datastreams/encoding.js +0 -1
- package/packages/dd-trace/src/datastreams/pathway.js +0 -1
- package/packages/dd-trace/src/debugger/constants.js +10 -0
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +62 -10
- package/packages/dd-trace/src/debugger/devtools_client/config.js +4 -1
- package/packages/dd-trace/src/debugger/devtools_client/guardrail-metrics.js +15 -0
- package/packages/dd-trace/src/debugger/devtools_client/index.js +103 -27
- package/packages/dd-trace/src/debugger/devtools_client/json-buffer.js +50 -7
- package/packages/dd-trace/src/debugger/devtools_client/log.js +3 -1
- package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +21 -8
- package/packages/dd-trace/src/debugger/devtools_client/send.js +41 -15
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +53 -13
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +118 -71
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/redaction.js +5 -3
- package/packages/dd-trace/src/debugger/devtools_client/snapshot-pruner.js +0 -3
- package/packages/dd-trace/src/debugger/devtools_client/status.js +21 -7
- package/packages/dd-trace/src/debugger/guardrail-metrics.js +167 -0
- package/packages/dd-trace/src/debugger/index.js +51 -3
- package/packages/dd-trace/src/debugger/inspect-segment.js +0 -3
- package/packages/dd-trace/src/debugger/probe_sampler.js +133 -9
- package/packages/dd-trace/src/debugger/probe_sampler_constants.js +31 -1
- package/packages/dd-trace/src/dogstatsd.js +0 -23
- package/packages/dd-trace/src/encode/0.4-cross-payload.js +0 -3
- package/packages/dd-trace/src/encode/0.4.js +0 -7
- package/packages/dd-trace/src/encode/0.5.js +0 -2
- package/packages/dd-trace/src/encode/tags-processors.js +0 -1
- package/packages/dd-trace/src/evp_proxy/constants.js +2 -0
- package/packages/dd-trace/src/evp_proxy/direct.js +17 -13
- package/packages/dd-trace/src/evp_proxy/discovery.js +14 -11
- package/packages/dd-trace/src/evp_proxy/path.js +21 -9
- package/packages/dd-trace/src/exporters/agent/writer.js +0 -1
- package/packages/dd-trace/src/exporters/agentless/index.js +0 -1
- package/packages/dd-trace/src/exporters/agentless/intake.js +0 -1
- package/packages/dd-trace/src/exporters/agentless/writer.js +0 -3
- package/packages/dd-trace/src/exporters/common/final-flush-request-tracker.js +0 -6
- package/packages/dd-trace/src/exporters/common/request.js +1 -0
- package/packages/dd-trace/src/exporters/common/url.js +33 -4
- package/packages/dd-trace/src/exporters/common/writer.js +0 -2
- package/packages/dd-trace/src/id.js +0 -11
- package/packages/dd-trace/src/llmobs/audio-codec.js +192 -0
- package/packages/dd-trace/src/llmobs/audio-utils.js +121 -0
- package/packages/dd-trace/src/llmobs/constants/audio.js +35 -0
- package/packages/dd-trace/src/llmobs/constants/prompts.js +14 -0
- package/packages/dd-trace/src/llmobs/constants/tags.js +17 -0
- package/packages/dd-trace/src/llmobs/eval-metric.js +0 -6
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +0 -4
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +0 -3
- package/packages/dd-trace/src/llmobs/experiments/index.js +7 -6
- package/packages/dd-trace/src/llmobs/experiments/util.js +1 -11
- package/packages/dd-trace/src/llmobs/index.js +1 -3
- package/packages/dd-trace/src/llmobs/noop.js +10 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +0 -4
- package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +0 -1
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +0 -4
- package/packages/dd-trace/src/llmobs/plugins/modelcontextprotocol-sdk/utils.js +0 -2
- package/packages/dd-trace/src/llmobs/plugins/openai/constants.js +0 -2
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +4 -6
- package/packages/dd-trace/src/llmobs/plugins/openai/realtime.js +285 -0
- package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +16 -3
- package/packages/dd-trace/src/llmobs/prompts/cache.js +272 -0
- package/packages/dd-trace/src/llmobs/prompts/manager.js +647 -0
- package/packages/dd-trace/src/llmobs/prompts/noop.js +29 -0
- package/packages/dd-trace/src/llmobs/prompts/prompt.js +158 -0
- package/packages/dd-trace/src/llmobs/sdk.js +21 -6
- package/packages/dd-trace/src/llmobs/span_processor.js +81 -2
- package/packages/dd-trace/src/llmobs/tagger.js +39 -11
- package/packages/dd-trace/src/llmobs/telemetry.js +36 -4
- package/packages/dd-trace/src/llmobs/util.js +0 -34
- package/packages/dd-trace/src/llmobs/writers/base.js +0 -1
- package/packages/dd-trace/src/llmobs/writers/util.js +1 -2
- package/packages/dd-trace/src/log/channels.js +3 -0
- package/packages/dd-trace/src/log/index.js +23 -5
- package/packages/dd-trace/src/log-submission/log-submission-plugin.js +74 -6
- package/packages/dd-trace/src/msgpack/chunk.js +0 -1
- package/packages/dd-trace/src/openfeature/agentless_configuration_source.js +0 -12
- package/packages/dd-trace/src/openfeature/configuration_source.js +4 -1
- package/packages/dd-trace/src/openfeature/encoding.js +0 -2
- package/packages/dd-trace/src/openfeature/eval-metrics-hook.js +0 -1
- package/packages/dd-trace/src/openfeature/flagging_provider.js +3 -3
- package/packages/dd-trace/src/openfeature/index.js +6 -6
- package/packages/dd-trace/src/openfeature/span-enrichment-hook.js +0 -1
- package/packages/dd-trace/src/openfeature/span-enrichment.js +0 -4
- package/packages/dd-trace/src/openfeature/writers/base.js +90 -23
- package/packages/dd-trace/src/openfeature/writers/exposures.js +4 -5
- package/packages/dd-trace/src/openfeature/writers/util.js +104 -37
- package/packages/dd-trace/src/opentelemetry/logs/logger.js +0 -1
- package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +0 -1
- package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +0 -1
- package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +0 -2
- package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +0 -1
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +0 -1
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +0 -1
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -14
- package/packages/dd-trace/src/opentelemetry/metrics/time.js +0 -1
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +30 -13
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +0 -1
- package/packages/dd-trace/src/opentelemetry/span-helpers.js +0 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +0 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +0 -3
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +0 -6
- package/packages/dd-trace/src/opentracing/span_context.js +0 -1
- package/packages/dd-trace/src/opentracing/tracer.js +3 -3
- package/packages/dd-trace/src/otel-thread-ctx.js +38 -11
- package/packages/dd-trace/src/payload-tagging/tagging.js +15 -3
- package/packages/dd-trace/src/plugin_manager.js +27 -8
- package/packages/dd-trace/src/plugins/ci_plugin.js +1 -2
- package/packages/dd-trace/src/plugins/database.js +2 -6
- package/packages/dd-trace/src/plugins/index.js +7 -0
- package/packages/dd-trace/src/plugins/plugin.js +0 -4
- package/packages/dd-trace/src/plugins/tracing.js +1 -1
- package/packages/dd-trace/src/plugins/util/git.js +1 -3
- package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +0 -1
- package/packages/dd-trace/src/plugins/util/llm.js +0 -2
- package/packages/dd-trace/src/plugins/util/status-validator.js +0 -1
- package/packages/dd-trace/src/plugins/util/test.js +26 -20
- package/packages/dd-trace/src/plugins/util/url.js +0 -5
- package/packages/dd-trace/src/priority_sampler.js +0 -6
- package/packages/dd-trace/src/process-tags/index.js +0 -1
- package/packages/dd-trace/src/profiler.js +132 -35
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +0 -1
- package/packages/dd-trace/src/profiling/profiler.js +101 -41
- package/packages/dd-trace/src/profiling/ssi-heuristics.js +29 -15
- package/packages/dd-trace/src/propagation-hash/index.js +0 -1
- package/packages/dd-trace/src/proxy.js +23 -47
- package/packages/dd-trace/src/random_sampler.js +0 -4
- package/packages/dd-trace/src/rate_limiter.js +0 -4
- package/packages/dd-trace/src/remote_config/capabilities.js +1 -9
- package/packages/dd-trace/src/remote_config/index.js +0 -1
- package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +0 -3
- package/packages/dd-trace/src/sampler.js +0 -4
- package/packages/dd-trace/src/sampling_rule.js +0 -7
- package/packages/dd-trace/src/serverless/vercel.js +0 -1
- package/packages/dd-trace/src/serverless.js +0 -1
- package/packages/dd-trace/src/service-naming/index.js +0 -1
- package/packages/dd-trace/src/service-naming/schemas/util.js +0 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +7 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +5 -0
- package/packages/dd-trace/src/span_processor.js +2 -2
- package/packages/dd-trace/src/span_sampler.js +0 -1
- package/packages/dd-trace/src/standalone/product.js +2 -2
- package/packages/dd-trace/src/standalone/tracesource.js +0 -1
- package/packages/dd-trace/src/startup-log.js +1 -1
- package/packages/dd-trace/src/telemetry/endpoints.js +0 -8
- package/packages/dd-trace/src/telemetry/logs/index.js +16 -5
- package/packages/dd-trace/src/telemetry/logs/log-collector.js +16 -6
- package/packages/dd-trace/src/telemetry/metrics.js +0 -2
- package/packages/dd-trace/src/telemetry/send-data.js +2 -1
- package/packages/dd-trace/src/telemetry/telemetry.js +4 -1
- package/packages/dd-trace/src/tracer.js +5 -4
- package/packages/dd-trace/src/util.js +13 -1
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
- package/packages/datadog-instrumentations/src/bullmq.js +0 -11
- package/packages/datadog-instrumentations/src/langchain.js +0 -7
- package/packages/datadog-instrumentations/src/langgraph.js +0 -7
- package/packages/datadog-instrumentations/src/mercurius.js +0 -11
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/** @typedef {import('../../../../../index').llmobs.FormattedPromptMessage} FormattedPromptMessage */
|
|
4
|
+
|
|
5
|
+
const VARIABLE_PATTERN = /(?<!\{)(?:\{\{\s*(\w+)\s*\}\}(?!\})|\{\s*(\w+)\s*\}(?!\}))/g
|
|
6
|
+
|
|
7
|
+
function isMessage (value) {
|
|
8
|
+
return typeof value?.role === 'string' &&
|
|
9
|
+
value?.type !== 'placeholder' &&
|
|
10
|
+
(value?.content == null || typeof value.content === 'string') &&
|
|
11
|
+
(typeof value?.content === 'string' ||
|
|
12
|
+
(Array.isArray(value?.tool_calls) && value.tool_calls.length > 0) ||
|
|
13
|
+
(Array.isArray(value?.tool_results) && value.tool_results.length > 0))
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function isPlaceholder (value) {
|
|
17
|
+
return value?.type === 'placeholder' && typeof value?.name === 'string'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function isObject (value) {
|
|
21
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function isTool (value) {
|
|
25
|
+
return isObject(value) &&
|
|
26
|
+
(value.id == null || typeof value.id === 'string') &&
|
|
27
|
+
(value.type == null || typeof value.type === 'string') &&
|
|
28
|
+
(value.tool_id == null || typeof value.tool_id === 'string') &&
|
|
29
|
+
(value.name == null || typeof value.name === 'string')
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function hasValidTools (message) {
|
|
33
|
+
return (message.tool_call_id == null || typeof message.tool_call_id === 'string') &&
|
|
34
|
+
(message.tool_calls == null || (Array.isArray(message.tool_calls) && message.tool_calls.every(call =>
|
|
35
|
+
isTool(call) && (call.function == null ||
|
|
36
|
+
(isObject(call.function) &&
|
|
37
|
+
typeof call.function.name === 'string' && typeof call.function.arguments === 'string'))))) &&
|
|
38
|
+
(message.tool_results == null || (Array.isArray(message.tool_results) && message.tool_results.every(isTool)))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function render (template, variables) {
|
|
42
|
+
return template.replaceAll(VARIABLE_PATTERN, (match, doubleName, singleName) => {
|
|
43
|
+
const name = doubleName ?? singleName
|
|
44
|
+
return Object.hasOwn(variables, name) ? String(variables[name]) : match
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function freezeConfig (config) {
|
|
49
|
+
if (config === undefined) return Object.freeze({})
|
|
50
|
+
if (!config || typeof config !== 'object' || Array.isArray(config)) {
|
|
51
|
+
throw new TypeError('Invalid prompt config: expected a JSON object')
|
|
52
|
+
}
|
|
53
|
+
const copy = structuredClone(config)
|
|
54
|
+
const freeze = value => {
|
|
55
|
+
if (!value || typeof value !== 'object' || Object.isFrozen(value)) return value
|
|
56
|
+
for (const nested of Object.values(value)) freeze(nested)
|
|
57
|
+
return Object.freeze(value)
|
|
58
|
+
}
|
|
59
|
+
return freeze(copy)
|
|
60
|
+
}
|
|
61
|
+
class ManagedPrompt {
|
|
62
|
+
/**
|
|
63
|
+
* @param {object} data
|
|
64
|
+
* @param {string} data.id
|
|
65
|
+
* @param {string} data.version
|
|
66
|
+
* @param {'registry'|'cache'|'fallback'|'ff'|'resolve'} data.source
|
|
67
|
+
* @param {string | Array<{role: string, content: string} | {type: 'placeholder', name: string}>} data.template
|
|
68
|
+
* @param {Record<string, unknown>} [data.config]
|
|
69
|
+
* @param {string} [data.promptUuid]
|
|
70
|
+
* @param {string} [data.promptVersionUuid]
|
|
71
|
+
*/
|
|
72
|
+
constructor ({ id, version, source, template, config, promptUuid, promptVersionUuid }) {
|
|
73
|
+
this.id = id
|
|
74
|
+
this.version = version
|
|
75
|
+
this.source = source
|
|
76
|
+
this.template = Array.isArray(template)
|
|
77
|
+
? Object.freeze(template.map(item => Object.freeze(isPlaceholder(item)
|
|
78
|
+
? { type: 'placeholder', name: item.name }
|
|
79
|
+
: { role: item.role, content: item.content })))
|
|
80
|
+
: template
|
|
81
|
+
this.config = freezeConfig(config)
|
|
82
|
+
this.promptUuid = promptUuid
|
|
83
|
+
this.promptVersionUuid = promptVersionUuid
|
|
84
|
+
Object.freeze(this)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Render the prompt without changing its stored template.
|
|
89
|
+
* @param {Record<string, unknown>} [variables]
|
|
90
|
+
* @returns {string | FormattedPromptMessage[]}
|
|
91
|
+
*/
|
|
92
|
+
format (variables = {}) {
|
|
93
|
+
if (typeof this.template === 'string') return render(this.template, variables)
|
|
94
|
+
return this.template.flatMap(item => {
|
|
95
|
+
if (!isPlaceholder(item)) return [{ ...item, content: render(item.content, variables) }]
|
|
96
|
+
if (!Object.hasOwn(variables, item.name)) {
|
|
97
|
+
throw new TypeError(`Missing message placeholder variable '${item.name}'`)
|
|
98
|
+
}
|
|
99
|
+
const messages = variables[item.name]
|
|
100
|
+
if (!Array.isArray(messages) || !messages.every(message => isMessage(message) && hasValidTools(message))) {
|
|
101
|
+
throw new TypeError(`Invalid message placeholder variable '${item.name}': expected an array of messages`)
|
|
102
|
+
}
|
|
103
|
+
return structuredClone(messages)
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Convert the managed prompt to the existing public annotation shape.
|
|
109
|
+
* @param {Record<string, unknown>} [variables]
|
|
110
|
+
* @returns {Record<string, unknown>}
|
|
111
|
+
*/
|
|
112
|
+
toAnnotation (variables) {
|
|
113
|
+
const annotation = {
|
|
114
|
+
id: this.id,
|
|
115
|
+
version: this.version,
|
|
116
|
+
template: typeof this.template === 'string' ? this.template : this.template.map(message => ({ ...message })),
|
|
117
|
+
}
|
|
118
|
+
const placeholderNames = new Set(Array.isArray(this.template)
|
|
119
|
+
? this.template.filter(isPlaceholder).map(item => item.name)
|
|
120
|
+
: [])
|
|
121
|
+
const entries = Object.entries(variables ?? {}).filter(([name]) => !placeholderNames.has(name))
|
|
122
|
+
if (entries.length) {
|
|
123
|
+
annotation.variables = Object.fromEntries(entries.map(([name, value]) => [name, String(value)]))
|
|
124
|
+
}
|
|
125
|
+
if (this.promptUuid) annotation.promptUuid = this.promptUuid
|
|
126
|
+
if (this.promptVersionUuid) annotation.promptVersionUuid = this.promptVersionUuid
|
|
127
|
+
return annotation
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Convert a caller fallback to a managed prompt.
|
|
132
|
+
* @param {string} promptId
|
|
133
|
+
* @param {string | object | Array<{role: string, content: string} | {type: 'placeholder', name: string}> |
|
|
134
|
+
* (() => string | object | Array<{role: string, content: string} | {type: 'placeholder', name: string}>)} fallback
|
|
135
|
+
* @returns {ManagedPrompt}
|
|
136
|
+
*/
|
|
137
|
+
static fromFallback (promptId, fallback) {
|
|
138
|
+
const value = typeof fallback === 'function' ? fallback() : fallback
|
|
139
|
+
const promptLike = value && !Array.isArray(value) && typeof value === 'object'
|
|
140
|
+
const template = promptLike ? value.template : value
|
|
141
|
+
const validTemplate = typeof template === 'string' || (
|
|
142
|
+
Array.isArray(template) && template.every(item =>
|
|
143
|
+
isPlaceholder(item) || (isMessage(item) && typeof item.content === 'string'))
|
|
144
|
+
)
|
|
145
|
+
if (!validTemplate) {
|
|
146
|
+
throw new TypeError('Invalid prompt fallback: expected a string, chat message array, or object with a template')
|
|
147
|
+
}
|
|
148
|
+
return new ManagedPrompt({
|
|
149
|
+
id: promptId,
|
|
150
|
+
version: String(promptLike && value.version ? value.version : 'fallback'),
|
|
151
|
+
source: 'fallback',
|
|
152
|
+
template,
|
|
153
|
+
config: promptLike ? value.config : undefined,
|
|
154
|
+
})
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
module.exports = ManagedPrompt
|
|
@@ -31,6 +31,7 @@ const { storage } = require('./storage')
|
|
|
31
31
|
const telemetry = require('./telemetry')
|
|
32
32
|
const LLMObsTagger = require('./tagger')
|
|
33
33
|
const { createExperiments } = require('./experiments')
|
|
34
|
+
const PromptManager = require('./prompts/manager')
|
|
34
35
|
|
|
35
36
|
// communicating with writer
|
|
36
37
|
const evalMetricAppendCh = channel('llmobs:eval-metric:append')
|
|
@@ -45,12 +46,17 @@ class LLMObs extends NoopLLMObs {
|
|
|
45
46
|
*/
|
|
46
47
|
#hasUserSpanProcessor = false
|
|
47
48
|
|
|
49
|
+
#promptManager
|
|
50
|
+
|
|
51
|
+
#getOpenFeatureProvider
|
|
52
|
+
|
|
48
53
|
/**
|
|
49
54
|
* @param {import('../tracer')} tracer - Tracer instance
|
|
50
55
|
* @param {import('./index')} llmobsModule - LLMObs module instance
|
|
51
56
|
* @param {import('../config/config-base')} config - Tracer configuration
|
|
57
|
+
* @param {() => object} getOpenFeatureProvider - Lazy getter for the tracer's existing OpenFeature provider
|
|
52
58
|
*/
|
|
53
|
-
constructor (tracer, llmobsModule, config) {
|
|
59
|
+
constructor (tracer, llmobsModule, config, getOpenFeatureProvider = () => {}) {
|
|
54
60
|
super(tracer)
|
|
55
61
|
|
|
56
62
|
/** @type {import('../config/config-base')} */
|
|
@@ -58,6 +64,7 @@ class LLMObs extends NoopLLMObs {
|
|
|
58
64
|
|
|
59
65
|
this._llmobsModule = llmobsModule
|
|
60
66
|
this._tagger = new LLMObsTagger(config)
|
|
67
|
+
this.#getOpenFeatureProvider = getOpenFeatureProvider
|
|
61
68
|
}
|
|
62
69
|
|
|
63
70
|
get enabled () {
|
|
@@ -73,6 +80,15 @@ class LLMObs extends NoopLLMObs {
|
|
|
73
80
|
return createExperiments(this._config, this)
|
|
74
81
|
}
|
|
75
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Prompt Management API.
|
|
85
|
+
* @returns {import('../../../../index').llmobs.Prompts}
|
|
86
|
+
*/
|
|
87
|
+
get prompts () {
|
|
88
|
+
this.#promptManager ??= new PromptManager(this._config, this.#getOpenFeatureProvider)
|
|
89
|
+
return this.#promptManager
|
|
90
|
+
}
|
|
91
|
+
|
|
76
92
|
enable (options = {}) {
|
|
77
93
|
logger.warn(
|
|
78
94
|
'Enabling LLM Observability via `llmobs.enable()` is deprecated and will be removed in dd-trace@7.0.0. ' +
|
|
@@ -97,8 +113,8 @@ class LLMObs extends NoopLLMObs {
|
|
|
97
113
|
|
|
98
114
|
// TODO: These configs should be passed through directly at construction time instead.
|
|
99
115
|
this._config.llmobs.DD_LLMOBS_ENABLED = true
|
|
100
|
-
this._config.llmobs.
|
|
101
|
-
this._config.llmobs.
|
|
116
|
+
this._config.llmobs.DD_LLMOBS_ML_APP = options.mlApp
|
|
117
|
+
this._config.llmobs.DD_LLMOBS_AGENTLESS_ENABLED = options.agentlessEnabled
|
|
102
118
|
|
|
103
119
|
// configure writers and channel subscribers
|
|
104
120
|
this._llmobsModule.enable(this._config)
|
|
@@ -393,7 +409,7 @@ class LLMObs extends NoopLLMObs {
|
|
|
393
409
|
'spanId and traceId must both be specified for the given evaluation metric to be submitted.'
|
|
394
410
|
)
|
|
395
411
|
}
|
|
396
|
-
const mlApp = options.mlApp || this._config.llmobs.
|
|
412
|
+
const mlApp = options.mlApp || this._config.llmobs.DD_LLMOBS_ML_APP
|
|
397
413
|
if (!mlApp) {
|
|
398
414
|
err = 'missing_ml_app'
|
|
399
415
|
throw new Error(
|
|
@@ -475,7 +491,6 @@ class LLMObs extends NoopLLMObs {
|
|
|
475
491
|
* @param {number} [options.timestampMs] - When the feedback was generated. Defaults to now.
|
|
476
492
|
* @param {'pass' | 'fail'} [options.assessment] - Assessment of the feedback.
|
|
477
493
|
* @param {string} [options.reasoning] - Explanation of the feedback.
|
|
478
|
-
* @returns {void}
|
|
479
494
|
*/
|
|
480
495
|
submitFeedback (options = {}) {
|
|
481
496
|
if (!this.enabled) return
|
|
@@ -532,7 +547,7 @@ class LLMObs extends NoopLLMObs {
|
|
|
532
547
|
throw new TypeError('submitter.type must be a string')
|
|
533
548
|
}
|
|
534
549
|
|
|
535
|
-
const mlApp = options.mlApp || this._config.llmobs.
|
|
550
|
+
const mlApp = options.mlApp || this._config.llmobs.DD_LLMOBS_ML_APP
|
|
536
551
|
if (!mlApp) {
|
|
537
552
|
err = 'missing_ml_app'
|
|
538
553
|
throw new Error('ML App name is required for sending feedback. Feedback data will not be sent.')
|
|
@@ -38,11 +38,41 @@ const {
|
|
|
38
38
|
SAMPLE_RATE,
|
|
39
39
|
SAMPLING_DECISION,
|
|
40
40
|
TRACE_ID,
|
|
41
|
+
INPUT_TOKENS_METRIC_KEY,
|
|
42
|
+
OUTPUT_TOKENS_METRIC_KEY,
|
|
43
|
+
TOTAL_TOKENS_METRIC_KEY,
|
|
44
|
+
CACHE_READ_INPUT_TOKENS_METRIC_KEY,
|
|
45
|
+
CACHE_WRITE_INPUT_TOKENS_METRIC_KEY,
|
|
46
|
+
REASONING_OUTPUT_TOKENS_METRIC_KEY,
|
|
47
|
+
GEN_AI_OPERATION_NAME,
|
|
48
|
+
GEN_AI_REQUEST_MODEL,
|
|
49
|
+
GEN_AI_PROVIDER_NAME,
|
|
50
|
+
GEN_AI_APPLICATION_NAME,
|
|
51
|
+
GEN_AI_CONVERSATION_ID,
|
|
52
|
+
GEN_AI_USAGE_INPUT_TOKENS_METRIC_KEY,
|
|
53
|
+
GEN_AI_USAGE_OUTPUT_TOKENS_METRIC_KEY,
|
|
54
|
+
GEN_AI_USAGE_TOTAL_TOKENS_METRIC_KEY,
|
|
55
|
+
GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS_METRIC_KEY,
|
|
56
|
+
GEN_AI_USAGE_CACHE_WRITE_INPUT_TOKENS_METRIC_KEY,
|
|
57
|
+
GEN_AI_USAGE_REASONING_OUTPUT_TOKENS_METRIC_KEY,
|
|
58
|
+
ARTIFICIAL_GEN_AI_TAGS,
|
|
41
59
|
} = require('./constants/tags')
|
|
42
60
|
const { UNSERIALIZABLE_VALUE_TEXT } = require('./constants/text')
|
|
43
61
|
const telemetry = require('./telemetry')
|
|
44
62
|
const LLMObsTagger = require('./tagger')
|
|
45
63
|
|
|
64
|
+
const DEFAULT_MODEL = 'custom'
|
|
65
|
+
const MODEL_BACKED_SPAN_KINDS = new Set(['llm', 'embedding'])
|
|
66
|
+
|
|
67
|
+
const GEN_AI_TOKEN_METRIC_KEYS = [
|
|
68
|
+
[INPUT_TOKENS_METRIC_KEY, GEN_AI_USAGE_INPUT_TOKENS_METRIC_KEY],
|
|
69
|
+
[OUTPUT_TOKENS_METRIC_KEY, GEN_AI_USAGE_OUTPUT_TOKENS_METRIC_KEY],
|
|
70
|
+
[TOTAL_TOKENS_METRIC_KEY, GEN_AI_USAGE_TOTAL_TOKENS_METRIC_KEY],
|
|
71
|
+
[CACHE_READ_INPUT_TOKENS_METRIC_KEY, GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS_METRIC_KEY],
|
|
72
|
+
[CACHE_WRITE_INPUT_TOKENS_METRIC_KEY, GEN_AI_USAGE_CACHE_WRITE_INPUT_TOKENS_METRIC_KEY],
|
|
73
|
+
[REASONING_OUTPUT_TOKENS_METRIC_KEY, GEN_AI_USAGE_REASONING_OUTPUT_TOKENS_METRIC_KEY],
|
|
74
|
+
]
|
|
75
|
+
|
|
46
76
|
class LLMObservabilitySpan {
|
|
47
77
|
/**
|
|
48
78
|
* @param {string} kind span kind
|
|
@@ -90,6 +120,12 @@ class LLMObsSpanProcessor {
|
|
|
90
120
|
// if the span is not in our private tagger map, it is not an llmobs span
|
|
91
121
|
if (!LLMObsTagger.tagMap.has(span)) return
|
|
92
122
|
|
|
123
|
+
try {
|
|
124
|
+
this.#setGenAiApmTags(span)
|
|
125
|
+
} catch (e) {
|
|
126
|
+
logger.debug('Failed to set gen_ai APM tags:', e.message)
|
|
127
|
+
}
|
|
128
|
+
|
|
93
129
|
try {
|
|
94
130
|
const formattedEvent = this.format(span)
|
|
95
131
|
telemetry.incrementLLMObsSpanFinishedCount(span)
|
|
@@ -138,8 +174,8 @@ class LLMObsSpanProcessor {
|
|
|
138
174
|
const output = {}
|
|
139
175
|
|
|
140
176
|
if (['llm', 'embedding'].includes(spanKind)) {
|
|
141
|
-
meta.model_name = mlObsTags[MODEL_NAME] ||
|
|
142
|
-
meta.model_provider = (mlObsTags[MODEL_PROVIDER] ||
|
|
177
|
+
meta.model_name = mlObsTags[MODEL_NAME] || DEFAULT_MODEL
|
|
178
|
+
meta.model_provider = (mlObsTags[MODEL_PROVIDER] || DEFAULT_MODEL).toLowerCase()
|
|
143
179
|
}
|
|
144
180
|
|
|
145
181
|
if (mlObsTags[METADATA] || mlObsTags[COST_TAGS]) {
|
|
@@ -283,6 +319,49 @@ class LLMObsSpanProcessor {
|
|
|
283
319
|
return llmObsSpanEvent
|
|
284
320
|
}
|
|
285
321
|
|
|
322
|
+
/**
|
|
323
|
+
* Writes the scalar `gen_ai.*` attributes onto the APM span, so model, provider, application,
|
|
324
|
+
* conversation and token usage are searchable in APM. Message bodies stay off the APM span.
|
|
325
|
+
*
|
|
326
|
+
* @param {import('../opentracing/span')} span
|
|
327
|
+
*/
|
|
328
|
+
#setGenAiApmTags (span) {
|
|
329
|
+
const mlObsTags = LLMObsTagger.tagMap.get(span)
|
|
330
|
+
const spanContext = span.context()
|
|
331
|
+
const spanKind = mlObsTags[SPAN_KIND]
|
|
332
|
+
|
|
333
|
+
if (spanKind) spanContext.setTag(GEN_AI_OPERATION_NAME, spanKind)
|
|
334
|
+
|
|
335
|
+
const modelName = mlObsTags[MODEL_NAME]
|
|
336
|
+
const modelProvider = mlObsTags[MODEL_PROVIDER]
|
|
337
|
+
const modelBacked = MODEL_BACKED_SPAN_KINDS.has(spanKind)
|
|
338
|
+
if (modelBacked) {
|
|
339
|
+
spanContext.setTag(GEN_AI_REQUEST_MODEL, modelName || DEFAULT_MODEL)
|
|
340
|
+
spanContext.setTag(GEN_AI_PROVIDER_NAME, (modelProvider || DEFAULT_MODEL).toLowerCase())
|
|
341
|
+
} else {
|
|
342
|
+
if (modelName) spanContext.setTag(GEN_AI_REQUEST_MODEL, modelName)
|
|
343
|
+
if (modelProvider) spanContext.setTag(GEN_AI_PROVIDER_NAME, modelProvider.toLowerCase())
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const mlApp = mlObsTags[ML_APP]
|
|
347
|
+
if (mlApp) spanContext.setTag(GEN_AI_APPLICATION_NAME, mlApp)
|
|
348
|
+
|
|
349
|
+
const sessionId = mlObsTags[SESSION_ID]
|
|
350
|
+
if (sessionId) spanContext.setTag(GEN_AI_CONVERSATION_ID, sessionId)
|
|
351
|
+
|
|
352
|
+
const metrics = mlObsTags[METRICS]
|
|
353
|
+
// Other kinds carry unrelated metrics that would be misleading under a `gen_ai.usage.*` key.
|
|
354
|
+
if (modelBacked && metrics) {
|
|
355
|
+
for (const [metricKey, genAiKey] of GEN_AI_TOKEN_METRIC_KEYS) {
|
|
356
|
+
const value = metrics[metricKey]
|
|
357
|
+
if (value != null) spanContext.setTag(genAiKey, value)
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// matches the value dd-trace-py writes
|
|
362
|
+
spanContext.setTag(ARTIFICIAL_GEN_AI_TAGS, 'true')
|
|
363
|
+
}
|
|
364
|
+
|
|
286
365
|
// For now, this only applies to metadata, as we let users annotate this field with any object
|
|
287
366
|
// However, we want to protect against circular references or BigInts (unserializable)
|
|
288
367
|
// This function can be reused for other fields if needed
|
|
@@ -87,7 +87,7 @@ class LLMObsTagger {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
/**
|
|
90
|
-
* The sampler reads its rate from `config.llmobs.
|
|
90
|
+
* The sampler reads its rate from `config.llmobs.DD_LLMOBS_SAMPLE_RATE`, which can change
|
|
91
91
|
* at runtime (e.g. via remote config). Rebuild the sampler whenever the rate
|
|
92
92
|
* changes so decisions reflect the current config, while reusing the existing
|
|
93
93
|
* sampler when it hasn't.
|
|
@@ -95,7 +95,7 @@ class LLMObsTagger {
|
|
|
95
95
|
* @returns {import('../sampler')}
|
|
96
96
|
*/
|
|
97
97
|
#getSampler () {
|
|
98
|
-
const rate = this.#config.llmobs?.
|
|
98
|
+
const rate = this.#config.llmobs?.DD_LLMOBS_SAMPLE_RATE ?? 1
|
|
99
99
|
if (this.#sampler === null || rate !== this.#sampler.rate()) {
|
|
100
100
|
this.#sampler = new Sampler(rate)
|
|
101
101
|
}
|
|
@@ -128,7 +128,7 @@ class LLMObsTagger {
|
|
|
128
128
|
mlApp ||
|
|
129
129
|
registry.get(parent)?.[ML_APP] ||
|
|
130
130
|
span.context()._trace.tags[PROPAGATED_ML_APP_KEY] ||
|
|
131
|
-
this.#config.llmobs.
|
|
131
|
+
this.#config.llmobs.DD_LLMOBS_ML_APP ||
|
|
132
132
|
this.#config.service // this should always have a default
|
|
133
133
|
|
|
134
134
|
if (!spanMlApp) {
|
|
@@ -415,6 +415,8 @@ class LLMObsTagger {
|
|
|
415
415
|
template,
|
|
416
416
|
contextVariables,
|
|
417
417
|
queryVariables,
|
|
418
|
+
promptUuid,
|
|
419
|
+
promptVersionUuid,
|
|
418
420
|
} = prompt
|
|
419
421
|
|
|
420
422
|
if (strictValidation) {
|
|
@@ -429,6 +431,8 @@ class LLMObsTagger {
|
|
|
429
431
|
}
|
|
430
432
|
}
|
|
431
433
|
|
|
434
|
+
const currentPrompt = registry.get(span)?.[INPUT_PROMPT]
|
|
435
|
+
const replacesPrompt = id != null || version != null || template != null
|
|
432
436
|
const finalPromptId = id ?? `${mlApp}_${DEFAULT_PROMPT_NAME}`
|
|
433
437
|
const finalCtxVariablesKeys = contextVariables ?? ['context']
|
|
434
438
|
const finalQueryVariablesKeys = queryVariables ?? ['question']
|
|
@@ -471,6 +475,16 @@ class LLMObsTagger {
|
|
|
471
475
|
return
|
|
472
476
|
}
|
|
473
477
|
|
|
478
|
+
if (promptUuid != null && typeof promptUuid !== 'string') {
|
|
479
|
+
this.#handleFailure('Prompt UUID must be a string.', 'invalid_prompt')
|
|
480
|
+
return
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (promptVersionUuid != null && typeof promptVersionUuid !== 'string') {
|
|
484
|
+
this.#handleFailure('Prompt version UUID must be a string.', 'invalid_prompt')
|
|
485
|
+
return
|
|
486
|
+
}
|
|
487
|
+
|
|
474
488
|
// validate prompt tags
|
|
475
489
|
if (tags && (typeof tags !== 'object' || tags instanceof Map)) {
|
|
476
490
|
this.#handleFailure('Prompt tags must be an non-Map object.', 'invalid_prompt')
|
|
@@ -491,10 +505,13 @@ class LLMObsTagger {
|
|
|
491
505
|
}
|
|
492
506
|
|
|
493
507
|
if (Array.isArray(template)) {
|
|
494
|
-
for (const
|
|
495
|
-
|
|
508
|
+
for (const item of template) {
|
|
509
|
+
const valid = item?.type === 'placeholder'
|
|
510
|
+
? typeof item.name === 'string'
|
|
511
|
+
: typeof item?.role === 'string' && typeof item?.content === 'string'
|
|
512
|
+
if (!valid) {
|
|
496
513
|
this.#handleFailure(
|
|
497
|
-
'Prompt chat template must
|
|
514
|
+
'Prompt chat template must contain messages or message placeholders.', 'invalid_prompt'
|
|
498
515
|
)
|
|
499
516
|
return
|
|
500
517
|
}
|
|
@@ -518,21 +535,32 @@ class LLMObsTagger {
|
|
|
518
535
|
if (typeof template === 'string') {
|
|
519
536
|
finalTemplate = template
|
|
520
537
|
} else if (Array.isArray(template)) {
|
|
521
|
-
finalChatTemplate = template.map(
|
|
538
|
+
finalChatTemplate = template.map(item => item.type === 'placeholder'
|
|
539
|
+
? { type: 'placeholder', name: item.name }
|
|
540
|
+
: { role: item.role, content: item.content })
|
|
522
541
|
}
|
|
523
542
|
|
|
524
543
|
const validatedPrompt = {}
|
|
525
|
-
if (finalPromptId) validatedPrompt.id = finalPromptId
|
|
544
|
+
if (finalPromptId && (!currentPrompt || replacesPrompt)) validatedPrompt.id = finalPromptId
|
|
526
545
|
if (version) validatedPrompt.version = version
|
|
546
|
+
if (promptUuid) validatedPrompt.prompt_uuid = promptUuid
|
|
547
|
+
if (promptVersionUuid) validatedPrompt.prompt_version_uuid = promptVersionUuid
|
|
527
548
|
if (variables) validatedPrompt.variables = variables
|
|
528
549
|
if (finalTemplate) validatedPrompt.template = finalTemplate
|
|
529
550
|
if (finalChatTemplate?.length) validatedPrompt.chat_template = finalChatTemplate
|
|
530
551
|
if (tags) validatedPrompt.tags = tags
|
|
531
|
-
if (finalCtxVariablesKeys
|
|
532
|
-
|
|
552
|
+
if (finalCtxVariablesKeys && (!currentPrompt || replacesPrompt || contextVariables != null)) {
|
|
553
|
+
validatedPrompt[INTERNAL_CONTEXT_VARIABLE_KEYS] = finalCtxVariablesKeys
|
|
554
|
+
}
|
|
555
|
+
if (finalQueryVariablesKeys && (!currentPrompt || replacesPrompt || queryVariables != null)) {
|
|
556
|
+
validatedPrompt[INTERNAL_QUERY_VARIABLE_KEYS] = finalQueryVariablesKeys
|
|
557
|
+
}
|
|
533
558
|
|
|
534
|
-
const currentPrompt = registry.get(span)?.[INPUT_PROMPT]
|
|
535
559
|
if (currentPrompt) {
|
|
560
|
+
if (replacesPrompt) {
|
|
561
|
+
if (promptUuid == null) currentPrompt.prompt_uuid = undefined
|
|
562
|
+
if (promptVersionUuid == null) currentPrompt.prompt_version_uuid = undefined
|
|
563
|
+
}
|
|
536
564
|
Object.assign(currentPrompt, validatedPrompt)
|
|
537
565
|
} else {
|
|
538
566
|
this._setTag(span, INPUT_PROMPT, validatedPrompt)
|
|
@@ -84,11 +84,11 @@ function recordLLMObsEnabled (startTime, config, value = 1) {
|
|
|
84
84
|
const autoEnabled = !!config._env?.['llmobs.enabled']
|
|
85
85
|
const tags = {
|
|
86
86
|
error: 0,
|
|
87
|
-
agentless: Number(config.llmobs.
|
|
87
|
+
agentless: Number(config.llmobs.DD_LLMOBS_AGENTLESS_ENABLED),
|
|
88
88
|
site: config.site,
|
|
89
89
|
auto: Number(autoEnabled),
|
|
90
|
-
ml_app: config.llmobs.
|
|
91
|
-
sample_rate: config.llmobs.
|
|
90
|
+
ml_app: config.llmobs.DD_LLMOBS_ML_APP,
|
|
91
|
+
sample_rate: config.llmobs.DD_LLMOBS_SAMPLE_RATE,
|
|
92
92
|
}
|
|
93
93
|
llmobsMetrics.count('product_enabled', tags).inc(value)
|
|
94
94
|
llmobsMetrics.distribution('init_time', tags).track(initTimeMs)
|
|
@@ -188,7 +188,6 @@ function recordSubmitEvaluation (options, err, value = 1) {
|
|
|
188
188
|
* @param {string} targetType - The payload key the feedback was attached to, `'other'` if unresolved.
|
|
189
189
|
* @param {string} err - The telemetry error tag, empty when the submission succeeded.
|
|
190
190
|
* @param {number} [value]
|
|
191
|
-
* @returns {void}
|
|
192
191
|
*/
|
|
193
192
|
function recordSubmitFeedback (metricType, targetType, err, value = 1) {
|
|
194
193
|
const tags = {
|
|
@@ -205,6 +204,36 @@ function recordLLMObsUserProcessorCalled (error, value = 1) {
|
|
|
205
204
|
llmobsMetrics.count('user_processor_called', tags).inc(value)
|
|
206
205
|
}
|
|
207
206
|
|
|
207
|
+
/**
|
|
208
|
+
* @param {'hot_cache'|'warm_cache'|'registry'|'resolve'|'fallback'|'ff'} source
|
|
209
|
+
* @param {number} [value]
|
|
210
|
+
*/
|
|
211
|
+
function recordPromptSource (source, value = 1) {
|
|
212
|
+
llmobsMetrics.count('prompt.source', { from: source }).inc(value)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* @param {'NotFound'|'FetchError'} errorType
|
|
217
|
+
* @param {number} [value]
|
|
218
|
+
*/
|
|
219
|
+
function recordPromptFetchError (errorType, value = 1) {
|
|
220
|
+
llmobsMetrics.count('prompt.fetch.error', { error_type: errorType }).inc(value)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* @param {'GET'|'POST'|'PATCH'|'DELETE'} method
|
|
225
|
+
* @param {string} errorType
|
|
226
|
+
* @param {number} status
|
|
227
|
+
* @param {number} [value]
|
|
228
|
+
*/
|
|
229
|
+
function recordPromptCrudError (method, errorType, status, value = 1) {
|
|
230
|
+
llmobsMetrics.count('prompt.crud.error', {
|
|
231
|
+
method,
|
|
232
|
+
error_type: errorType,
|
|
233
|
+
status: String(status),
|
|
234
|
+
}).inc(value)
|
|
235
|
+
}
|
|
236
|
+
|
|
208
237
|
module.exports = {
|
|
209
238
|
recordLLMObsEnabled,
|
|
210
239
|
incrementLLMObsSpanStartCount,
|
|
@@ -220,4 +249,7 @@ module.exports = {
|
|
|
220
249
|
recordSubmitEvaluation,
|
|
221
250
|
recordSubmitFeedback,
|
|
222
251
|
recordLLMObsUserProcessorCalled,
|
|
252
|
+
recordPromptSource,
|
|
253
|
+
recordPromptFetchError,
|
|
254
|
+
recordPromptCrudError,
|
|
223
255
|
}
|
|
@@ -306,7 +306,6 @@ function getFunctionArguments (fn, args = []) {
|
|
|
306
306
|
// keys), so a name containing `=` is safe and must not be dropped.
|
|
307
307
|
/**
|
|
308
308
|
* @param {string} name
|
|
309
|
-
* @returns {boolean}
|
|
310
309
|
*/
|
|
311
310
|
function agentNameWireSafe (name) {
|
|
312
311
|
// Conservative slice of the 512B shared tagset budget, mirroring dd-trace-py.
|
|
@@ -413,7 +412,6 @@ function findGenAIAncestorSpanId (span) {
|
|
|
413
412
|
/**
|
|
414
413
|
* Generate a 128-bit LLMObs trace ID with the span start time encoded in its high bits.
|
|
415
414
|
* @param {number} startTime
|
|
416
|
-
* @returns {string}
|
|
417
415
|
*/
|
|
418
416
|
function generateLlmObsTraceId (startTime) {
|
|
419
417
|
const identifier = id()
|
|
@@ -455,45 +453,13 @@ function normalizeLlmObsTraceId (traceId) {
|
|
|
455
453
|
return identifier > MAX_UINT_64 ? identifier.toString(16).padStart(32, '0') : traceId
|
|
456
454
|
}
|
|
457
455
|
|
|
458
|
-
// Maps an audio `format` (e.g. "wav", "mp3") to a MIME type. Defaults to `audio/wav` when the
|
|
459
|
-
// format is missing. Provider-specific overrides (e.g. OpenAI's mp3 -> audio/mpeg) are passed in
|
|
460
|
-
// via `mimeTypeLookup` so this stays provider-agnostic. A non-string `format` is treated as missing
|
|
461
|
-
// so a malformed auto-instrumented payload can't throw and disable the plugin.
|
|
462
|
-
/**
|
|
463
|
-
* @param {string} fmt
|
|
464
|
-
* @param {Record<string, string>} [mimeTypeLookup]
|
|
465
|
-
* @returns {string}
|
|
466
|
-
*/
|
|
467
|
-
function audioMimeTypeFromFormat (fmt, mimeTypeLookup = {}) {
|
|
468
|
-
fmt = typeof fmt === 'string' ? fmt.trim().toLowerCase() : ''
|
|
469
|
-
if (!fmt) return 'audio/wav'
|
|
470
|
-
return mimeTypeLookup[fmt] ?? `audio/${fmt}`
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
// Builds an audio part from raw audio bytes (base64-encoded) or an existing base64 string. Only
|
|
474
|
-
// Buffer/Uint8Array inputs are base64-encoded; any other shape is passed through so a malformed
|
|
475
|
-
// auto-instrumented payload can't throw (the tagger soft-skips a non-string `content`).
|
|
476
|
-
/**
|
|
477
|
-
* @param {Buffer | Uint8Array | string} data
|
|
478
|
-
* @param {string} mimeType
|
|
479
|
-
* @returns {{ mimeType: string, content: string }}
|
|
480
|
-
*/
|
|
481
|
-
function formatAudioPart (data, mimeType) {
|
|
482
|
-
const content = Buffer.isBuffer(data) || ArrayBuffer.isView(data)
|
|
483
|
-
? Buffer.from(data).toString('base64')
|
|
484
|
-
: data
|
|
485
|
-
return { mimeType, content }
|
|
486
|
-
}
|
|
487
|
-
|
|
488
456
|
module.exports = {
|
|
489
457
|
agentNameWireSafe,
|
|
490
|
-
audioMimeTypeFromFormat,
|
|
491
458
|
encodeUnicode,
|
|
492
459
|
findGenAIAncestorSpanId,
|
|
493
460
|
generateLlmObsTraceId,
|
|
494
461
|
llmObsTraceIdToWire,
|
|
495
462
|
normalizeLlmObsTraceId,
|
|
496
|
-
formatAudioPart,
|
|
497
463
|
resolveAgentAttribution,
|
|
498
464
|
validateCostTags,
|
|
499
465
|
validateKind,
|
|
@@ -102,7 +102,6 @@ class BaseLLMObsWriter {
|
|
|
102
102
|
* @param {Record<string, unknown>} event
|
|
103
103
|
* @param {{ apiKey?: string, site?: string }} [routing]
|
|
104
104
|
* @param {number} [byteLength]
|
|
105
|
-
* @returns {boolean} `true` if the event was buffered, `false` if it was dropped
|
|
106
105
|
* (e.g. the per-routing buffer was full). Callers that depend on the event
|
|
107
106
|
* actually being submitted should check this value.
|
|
108
107
|
*/
|
|
@@ -8,10 +8,9 @@ const { fetchAgentInfo } = require('../../agent/info')
|
|
|
8
8
|
/**
|
|
9
9
|
* @param {import('../../config/config-base')} config
|
|
10
10
|
* @param {(agentless: boolean) => void} setWritersAgentlessValue
|
|
11
|
-
* @returns {void}
|
|
12
11
|
*/
|
|
13
12
|
function setAgentStrategy (config, setWritersAgentlessValue) {
|
|
14
|
-
const agentlessEnabled = config.llmobs.
|
|
13
|
+
const agentlessEnabled = config.llmobs.DD_LLMOBS_AGENTLESS_ENABLED
|
|
15
14
|
|
|
16
15
|
if (agentlessEnabled != null) {
|
|
17
16
|
setWritersAgentlessValue(agentlessEnabled)
|
|
@@ -9,6 +9,8 @@ const debugChannel = channel('datadog:log:debug')
|
|
|
9
9
|
const infoChannel = channel('datadog:log:info')
|
|
10
10
|
const warnChannel = channel('datadog:log:warn')
|
|
11
11
|
const errorChannel = channel('datadog:log:error')
|
|
12
|
+
// Telemetry needs the unformatted message, cause and transmission policy, not console output.
|
|
13
|
+
const errorRecordChannel = channel('datadog:log:error:record')
|
|
12
14
|
|
|
13
15
|
const defaultLevel = Level.debug
|
|
14
16
|
|
|
@@ -67,4 +69,5 @@ module.exports = {
|
|
|
67
69
|
infoChannel,
|
|
68
70
|
warnChannel,
|
|
69
71
|
errorChannel,
|
|
72
|
+
errorRecordChannel,
|
|
70
73
|
}
|