dd-trace 6.15.0 → 6.17.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/README.md +15 -0
- 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 +4 -8
- 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/package-check.js +4 -1
- 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 +1 -2
- package/ci/test-optimization-validation/source-text.js +0 -3
- package/ci/vitest-no-worker-init-setup.mjs +41 -16
- package/index.d.ts +169 -0
- package/next.d.ts +27 -0
- package/next.js +3 -0
- package/package.json +28 -23
- package/packages/datadog-esbuild/index.js +14 -1
- package/packages/datadog-esbuild/src/resolver.js +284 -0
- package/packages/datadog-esbuild/src/utils.js +162 -120
- package/packages/datadog-instrumentations/src/aerospike.js +28 -6
- package/packages/datadog-instrumentations/src/ai.js +39 -97
- package/packages/datadog-instrumentations/src/anthropic.js +76 -134
- package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +1 -3
- package/packages/datadog-instrumentations/src/aws-sdk.js +0 -2
- package/packages/datadog-instrumentations/src/azure-cosmos.js +1 -1
- package/packages/datadog-instrumentations/src/azure-durable-functions.js +23 -9
- package/packages/datadog-instrumentations/src/azure-functions.js +48 -0
- package/packages/datadog-instrumentations/src/bullmq.js +1 -1
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +1 -3
- package/packages/datadog-instrumentations/src/console.js +97 -0
- package/packages/datadog-instrumentations/src/cucumber.js +72 -12
- package/packages/datadog-instrumentations/src/cypress-config.js +0 -23
- package/packages/datadog-instrumentations/src/dd-trace-api.js +2 -2
- package/packages/datadog-instrumentations/src/graphql.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/bundler-register.js +39 -14
- package/packages/datadog-instrumentations/src/helpers/channel.js +0 -2
- package/packages/datadog-instrumentations/src/helpers/graphql-jit-runtime.js +0 -5
- package/packages/datadog-instrumentations/src/helpers/hooks.js +8 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +25 -8
- package/packages/datadog-instrumentations/src/helpers/instrumentation-utils.js +69 -0
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +0 -6
- package/packages/datadog-instrumentations/src/helpers/register.js +13 -64
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +109 -29
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentation-registry.js +39 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/azure-durable-functions.js +17 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +3 -16
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +12 -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 +6 -7
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +17 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms/postgres.js +388 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +99 -75
- package/packages/datadog-instrumentations/src/helpers/router-helper.js +0 -1
- package/packages/datadog-instrumentations/src/jest.js +141 -51
- package/packages/datadog-instrumentations/src/knex.js +0 -1
- package/packages/datadog-instrumentations/src/langchain.js +1 -1
- package/packages/datadog-instrumentations/src/langgraph.js +1 -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/mercurius.js +1 -1
- package/packages/datadog-instrumentations/src/mocha/common.js +0 -1
- package/packages/datadog-instrumentations/src/mocha/main.js +22 -25
- 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/modelcontextprotocol-sdk.js +1 -1
- package/packages/datadog-instrumentations/src/mysql2.js +0 -2
- package/packages/datadog-instrumentations/src/openai-agents.js +1 -1
- 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 +97 -80
- package/packages/datadog-instrumentations/src/oracledb.js +9 -3
- package/packages/datadog-instrumentations/src/playwright-reporter.js +3 -16
- package/packages/datadog-instrumentations/src/playwright.js +238 -55
- package/packages/datadog-instrumentations/src/postgres.js +7 -0
- 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/vitest-main-no-worker-init.js +18 -18
- package/packages/datadog-instrumentations/src/vitest-main.js +37 -17
- package/packages/datadog-instrumentations/src/vitest-util.js +7 -1
- package/packages/datadog-instrumentations/src/vitest-worker.js +56 -23
- package/packages/datadog-instrumentations/src/webdriverio.js +23 -42
- package/packages/datadog-instrumentations/src/ws.js +0 -1
- 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 +7 -2
- 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-cosmos/src/index.js +1 -1
- package/packages/datadog-plugin-azure-durable-functions/src/index.js +95 -1
- package/packages/datadog-plugin-cucumber/src/index.js +10 -0
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +171 -75
- package/packages/datadog-plugin-cypress/src/finalization.js +0 -3
- 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-google-cloud-pubsub/src/consumer.js +2 -24
- package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +2 -36
- package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-request-context.js +31 -0
- package/packages/datadog-plugin-graphql/src/resolve-error.js +32 -11
- package/packages/datadog-plugin-graphql/src/utils.js +0 -5
- package/packages/datadog-plugin-jest/src/index.js +13 -0
- package/packages/datadog-plugin-kafkajs/src/producer.js +0 -1
- package/packages/datadog-plugin-mocha/src/index.js +39 -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 +76 -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 +10 -0
- package/packages/datadog-plugin-ws/src/util.js +0 -4
- package/packages/datadog-turbopack/index.js +206 -0
- package/packages/datadog-turbopack/src/constants.js +5 -0
- package/packages/datadog-turbopack/src/loader.js +484 -0
- package/packages/datadog-webpack/src/loader.js +0 -1
- 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 +3 -2
- package/packages/dd-trace/src/aiguard/integrations/anthropic.js +53 -25
- package/packages/dd-trace/src/aiguard/integrations/evaluate.js +49 -25
- package/packages/dd-trace/src/aiguard/integrations/index.js +4 -3
- package/packages/dd-trace/src/aiguard/integrations/openai.js +132 -29
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +116 -42
- 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 +29 -2
- package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +25 -0
- 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-analyzers/url-sensitive-analyzer.js +2 -2
- 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/telemetry/api_security.js +1 -3
- package/packages/dd-trace/src/appsec/telemetry/common.js +4 -0
- package/packages/dd-trace/src/appsec/telemetry/index.js +23 -5
- package/packages/dd-trace/src/appsec/telemetry/rasp.js +1 -20
- package/packages/dd-trace/src/appsec/telemetry/user.js +1 -3
- package/packages/dd-trace/src/appsec/telemetry/waf.js +1 -20
- 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 +74 -66
- package/packages/dd-trace/src/config/helper.js +9 -2
- package/packages/dd-trace/src/config/index.js +44 -29
- 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 +146 -9
- 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 +76 -17
- package/packages/dd-trace/src/debugger/devtools_client/config.js +2 -0
- 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 +34 -8
- package/packages/dd-trace/src/debugger/devtools_client/send.js +40 -14
- 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-pruner.js +0 -3
- package/packages/dd-trace/src/debugger/devtools_client/status.js +20 -6
- package/packages/dd-trace/src/debugger/guardrail-metrics.js +167 -0
- package/packages/dd-trace/src/debugger/index.js +50 -2
- 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/discovery.js +3 -5
- package/packages/dd-trace/src/evp_proxy/path.js +11 -4
- 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/docker.js +4 -1
- package/packages/dd-trace/src/exporters/common/final-flush-request-tracker.js +0 -6
- package/packages/dd-trace/src/exporters/common/proxy.js +3 -0
- package/packages/dd-trace/src/exporters/common/request.js +1 -0
- package/packages/dd-trace/src/exporters/common/url.js +0 -2
- 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 +639 -0
- package/packages/dd-trace/src/llmobs/prompts/noop.js +29 -0
- package/packages/dd-trace/src/llmobs/prompts/prompt.js +92 -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 +30 -7
- 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/encoding.js +0 -2
- package/packages/dd-trace/src/openfeature/eval-metrics-hook.js +0 -1
- package/packages/dd-trace/src/openfeature/index.js +0 -4
- 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 +0 -6
- package/packages/dd-trace/src/openfeature/writers/exposures.js +0 -5
- package/packages/dd-trace/src/openfeature/writers/util.js +0 -4
- 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/index.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +4 -4
- 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 +103 -35
- package/packages/dd-trace/src/opentelemetry/metrics/time.js +0 -1
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +36 -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/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 +9 -3
- package/packages/dd-trace/src/plugins/ci_plugin.js +0 -1
- package/packages/dd-trace/src/plugins/database.js +2 -6
- package/packages/dd-trace/src/plugins/index.js +8 -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 +25 -21
- 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 +21 -45
- 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/telemetry.js +4 -1
- package/packages/dd-trace/src/util.js +13 -1
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/utils.js +0 -10
|
@@ -330,7 +330,6 @@ class Dataset {
|
|
|
330
330
|
* @param {string} recordId Dataset record id.
|
|
331
331
|
* @param {'add' | 'remove' | 'replace'} operation Tag operation to queue.
|
|
332
332
|
* @param {string[]} tags Tags in key:value format.
|
|
333
|
-
* @returns {void}
|
|
334
333
|
*/
|
|
335
334
|
#queueTagOperation (recordId, operation, tags) {
|
|
336
335
|
if (operation !== 'replace' && tags.length === 0) return
|
|
@@ -503,7 +502,6 @@ class Dataset {
|
|
|
503
502
|
* Detach tag changes sent by this batch so edits made while the request is in flight
|
|
504
503
|
* are queued relative to the response that this batch will commit.
|
|
505
504
|
* @param {PendingBatch} pending
|
|
506
|
-
* @returns {void}
|
|
507
505
|
*/
|
|
508
506
|
#detachCommittedTagOperations (pending) {
|
|
509
507
|
pending.inFlightTagOperations = new Map()
|
|
@@ -527,7 +525,6 @@ class Dataset {
|
|
|
527
525
|
/**
|
|
528
526
|
* Restore tag changes when a batch request fails, including edits made while it was in flight.
|
|
529
527
|
* @param {PendingBatch} pending
|
|
530
|
-
* @returns {void}
|
|
531
528
|
*/
|
|
532
529
|
#restoreFailedTagOperations (pending) {
|
|
533
530
|
if (!pending.inFlightTagOperations) return
|
|
@@ -549,7 +546,6 @@ class Dataset {
|
|
|
549
546
|
/**
|
|
550
547
|
* Clear the changes represented by a completed batch while retaining concurrent local edits.
|
|
551
548
|
* @param {PendingBatch} pending
|
|
552
|
-
* @returns {void}
|
|
553
549
|
*/
|
|
554
550
|
#clearCommittedChanges (pending) {
|
|
555
551
|
for (const [recordId, payload] of pending.insertPayloads) {
|
|
@@ -35,8 +35,8 @@ class Experiments {
|
|
|
35
35
|
|
|
36
36
|
constructor (config, llmobs) {
|
|
37
37
|
this.#config = config
|
|
38
|
-
this.#llmobs = config.llmobs?.
|
|
39
|
-
this.#projectName = config.llmobs?.
|
|
38
|
+
this.#llmobs = config.llmobs?.DD_LLMOBS_ML_APP || config.service ? llmobs : undefined
|
|
39
|
+
this.#projectName = config.llmobs?.DD_LLMOBS_PROJECT_NAME || DEFAULT_PROJECT_NAME
|
|
40
40
|
this.#client = this.#clientForProject(this.#projectName)
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -179,9 +179,9 @@ class Experiments {
|
|
|
179
179
|
const projectName = options?.projectName ?? datasetProjectName
|
|
180
180
|
const client = this.#clientForOperation(projectName)
|
|
181
181
|
const usesDatasetOverride = datasetProjectName !== undefined && datasetProjectName !== this.#projectName
|
|
182
|
-
const resolvedProjectName = projectName ?? this.#config.llmobs?.
|
|
182
|
+
const resolvedProjectName = projectName ?? this.#config.llmobs?.DD_LLMOBS_PROJECT_NAME
|
|
183
183
|
const experimentOptions = options?.projectName === undefined &&
|
|
184
|
-
(usesDatasetOverride || this.#config.llmobs?.
|
|
184
|
+
(usesDatasetOverride || this.#config.llmobs?.DD_LLMOBS_PROJECT_NAME !== undefined) &&
|
|
185
185
|
resolvedProjectName !== undefined
|
|
186
186
|
? { ...options, projectName: resolvedProjectName }
|
|
187
187
|
: options
|
|
@@ -198,8 +198,9 @@ class Experiments {
|
|
|
198
198
|
*/
|
|
199
199
|
startExperiment (options) {
|
|
200
200
|
const client = this.#clientForOperation(options?.projectName)
|
|
201
|
-
const experimentOptions = options?.projectName === undefined &&
|
|
202
|
-
|
|
201
|
+
const experimentOptions = options?.projectName === undefined &&
|
|
202
|
+
this.#config.llmobs?.DD_LLMOBS_PROJECT_NAME !== undefined
|
|
203
|
+
? { ...options, projectName: this.#config.llmobs.DD_LLMOBS_PROJECT_NAME }
|
|
203
204
|
: options
|
|
204
205
|
return new Experiment(client, { ...experimentOptions, external: true }).start()
|
|
205
206
|
.then(experiment => new ExternalExperiment(experiment))
|
|
@@ -14,7 +14,6 @@ const EVALUATOR_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/
|
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* @param {object | null | undefined} value
|
|
17
|
-
* @returns {boolean}
|
|
18
17
|
*/
|
|
19
18
|
function hasEntries (value) {
|
|
20
19
|
if (!value) return false
|
|
@@ -47,16 +46,12 @@ function tagOperationsAreEmpty (operations) {
|
|
|
47
46
|
/**
|
|
48
47
|
* @param {unknown} value
|
|
49
48
|
* @param {string} name
|
|
50
|
-
* @returns {number}
|
|
51
49
|
*/
|
|
52
50
|
function normalizePositiveInteger (value, name) {
|
|
53
51
|
if (!Number.isInteger(value) || value < 1) throw new Error(`${name} must be a positive integer`)
|
|
54
|
-
return value
|
|
52
|
+
return /** @type {number} */ (value)
|
|
55
53
|
}
|
|
56
54
|
|
|
57
|
-
/**
|
|
58
|
-
* @returns {string}
|
|
59
|
-
*/
|
|
60
55
|
function generateRunId () {
|
|
61
56
|
return randomUUID()
|
|
62
57
|
}
|
|
@@ -77,7 +72,6 @@ function validateEvaluatorName (name) {
|
|
|
77
72
|
/**
|
|
78
73
|
* @param {(...args: unknown[]) => unknown} fn
|
|
79
74
|
* @param {string} fallback
|
|
80
|
-
* @returns {string}
|
|
81
75
|
*/
|
|
82
76
|
function functionName (fn, fallback) {
|
|
83
77
|
return typeof fn.name === 'string' && fn.name.length > 0 ? fn.name : fallback
|
|
@@ -124,7 +118,6 @@ function normalizeEvaluators (evaluators, kind) {
|
|
|
124
118
|
|
|
125
119
|
/**
|
|
126
120
|
* @param {unknown} value
|
|
127
|
-
* @returns {string}
|
|
128
121
|
*/
|
|
129
122
|
function inferMetricType (value) {
|
|
130
123
|
if (typeof value === 'boolean') return 'boolean'
|
|
@@ -184,7 +177,6 @@ function normalizeJsonMetricValue (value) {
|
|
|
184
177
|
|
|
185
178
|
/**
|
|
186
179
|
* @param {unknown} value
|
|
187
|
-
* @returns {string}
|
|
188
180
|
*/
|
|
189
181
|
function stringify (value) {
|
|
190
182
|
if (value == null) return ''
|
|
@@ -270,7 +262,6 @@ function sleep (ms) {
|
|
|
270
262
|
/**
|
|
271
263
|
* @param {unknown} value
|
|
272
264
|
* @param {number} fallback
|
|
273
|
-
* @returns {number}
|
|
274
265
|
*/
|
|
275
266
|
function timestampMs (value, fallback = Date.now()) {
|
|
276
267
|
if (value === null || value === undefined) return fallback
|
|
@@ -286,7 +277,6 @@ function timestampMs (value, fallback = Date.now()) {
|
|
|
286
277
|
/**
|
|
287
278
|
* @param {{ durationMs?: unknown, completedAt?: unknown }} row
|
|
288
279
|
* @param {number} startMs
|
|
289
|
-
* @returns {number}
|
|
290
280
|
*/
|
|
291
281
|
function durationNs (row, startMs) {
|
|
292
282
|
if (typeof row.durationMs === 'number' && Number.isFinite(row.durationMs)) {
|
|
@@ -152,7 +152,6 @@ function disable () {
|
|
|
152
152
|
* Keeps retired writers reachable until their destroy-triggered deliveries complete.
|
|
153
153
|
* @param {LLMObsSpanWriter | null} retiredSpanWriter
|
|
154
154
|
* @param {LLMObsEvalMetricsWriter | null} retiredEvalWriter
|
|
155
|
-
* @returns {void}
|
|
156
155
|
*/
|
|
157
156
|
function retireWriters (retiredSpanWriter, retiredEvalWriter) {
|
|
158
157
|
const retiredWriters = [retiredSpanWriter, retiredEvalWriter].filter(Boolean)
|
|
@@ -183,7 +182,7 @@ function handleLLMObsInjection (injection) {
|
|
|
183
182
|
const mlApp =
|
|
184
183
|
mlObsSpanTags?.[ML_APP] ||
|
|
185
184
|
parentContext?._trace?.tags?.[PROPAGATED_ML_APP_KEY] ||
|
|
186
|
-
globalTracerConfig.llmobs.
|
|
185
|
+
globalTracerConfig.llmobs.DD_LLMOBS_ML_APP
|
|
187
186
|
|
|
188
187
|
const sampleRate =
|
|
189
188
|
mlObsSpanTags?.[SAMPLE_RATE] ?? parentContext?._trace?.tags?.[PROPAGATED_SAMPLE_RATE_KEY]
|
|
@@ -239,7 +238,6 @@ function handleLLMObsInjection (injection) {
|
|
|
239
238
|
* @param {Function} [done]
|
|
240
239
|
* @param {LLMObsSpanWriter | null} [currentSpanWriter]
|
|
241
240
|
* @param {LLMObsEvalMetricsWriter | null} [currentEvalWriter]
|
|
242
|
-
* @returns {boolean} `true` when a writer throws synchronously.
|
|
243
241
|
*/
|
|
244
242
|
function flushWriters (done, currentSpanWriter = spanWriter, currentEvalWriter = evalWriter) {
|
|
245
243
|
let failed = false
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const NoopPrompts = require('./prompts/noop')
|
|
4
|
+
|
|
3
5
|
let NoopExperiments
|
|
4
6
|
|
|
5
7
|
class NoopLLMObs {
|
|
@@ -16,6 +18,14 @@ class NoopLLMObs {
|
|
|
16
18
|
return new NoopExperiments('LLM Observability is not enabled')
|
|
17
19
|
}
|
|
18
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Prompt Management API.
|
|
23
|
+
* @returns {import('../../../../index').llmobs.Prompts}
|
|
24
|
+
*/
|
|
25
|
+
get prompts () {
|
|
26
|
+
return new NoopPrompts()
|
|
27
|
+
}
|
|
28
|
+
|
|
19
29
|
enable (options) {}
|
|
20
30
|
|
|
21
31
|
disable () {}
|
|
@@ -337,7 +337,6 @@ function getToolNameFromTags (tags) {
|
|
|
337
337
|
|
|
338
338
|
/**
|
|
339
339
|
* @param {unknown} value
|
|
340
|
-
* @returns {string}
|
|
341
340
|
*/
|
|
342
341
|
function stringifyToolCallResult (value) {
|
|
343
342
|
return JSON.stringify(value) ?? UNPARSABLE_TOOL_RESULT
|
|
@@ -345,7 +344,6 @@ function stringifyToolCallResult (value) {
|
|
|
345
344
|
|
|
346
345
|
/**
|
|
347
346
|
* @param {ToolCallContentPart[]} value
|
|
348
|
-
* @returns {string}
|
|
349
347
|
*/
|
|
350
348
|
function formatToolCallContent (value) {
|
|
351
349
|
if (!Array.isArray(value)) return UNPARSABLE_TOOL_RESULT
|
|
@@ -388,7 +386,6 @@ function formatToolCallContent (value) {
|
|
|
388
386
|
|
|
389
387
|
/**
|
|
390
388
|
* @param {ToolCallResultContent | null | undefined} content
|
|
391
|
-
* @returns {string}
|
|
392
389
|
*/
|
|
393
390
|
function getToolCallResultContent (content) {
|
|
394
391
|
try {
|
|
@@ -426,7 +423,6 @@ function getToolCallResultContent (content) {
|
|
|
426
423
|
* Computes the LLM Observability `ai` span name
|
|
427
424
|
* @param {string} operation
|
|
428
425
|
* @param {string} functionId
|
|
429
|
-
* @returns {string}
|
|
430
426
|
*/
|
|
431
427
|
function getLlmObsSpanName (operation, functionId) {
|
|
432
428
|
return functionId ? `${functionId}.${operation}` : operation
|
|
@@ -62,7 +62,6 @@ function formatAnthropicToolResultContent (content) {
|
|
|
62
62
|
*
|
|
63
63
|
* @param {AnthropicLlmObsMessage[]} messages
|
|
64
64
|
* @param {{ role: string, content: string | Array<TextBlock | ImageBlock | ToolUseBlock | ToolResultBlock> }} message
|
|
65
|
-
* @returns {void}
|
|
66
65
|
*/
|
|
67
66
|
function appendMessage (messages, { role, content }) {
|
|
68
67
|
if (typeof content === 'string') {
|
|
@@ -55,7 +55,6 @@ function groupPartsByRole (parts) {
|
|
|
55
55
|
/**
|
|
56
56
|
* Check if parts contain thought/reasoning content
|
|
57
57
|
* @param {Array<{thought?: boolean}>} parts
|
|
58
|
-
* @returns {boolean}
|
|
59
58
|
*/
|
|
60
59
|
function hasThoughtParts (parts) {
|
|
61
60
|
return parts.some(part => part.thought === true)
|
|
@@ -65,7 +64,6 @@ function hasThoughtParts (parts) {
|
|
|
65
64
|
* Determine the role from a candidate and its parts
|
|
66
65
|
* @param {object} candidate
|
|
67
66
|
* @param {Array<{thought?: boolean}>} parts
|
|
68
|
-
* @returns {string}
|
|
69
67
|
*/
|
|
70
68
|
function determineRole (candidate, parts = []) {
|
|
71
69
|
// Check parts for thought indicators
|
|
@@ -84,7 +82,6 @@ function determineRole (candidate, parts = []) {
|
|
|
84
82
|
/**
|
|
85
83
|
* Normalize role to standard values
|
|
86
84
|
* @param {string} role
|
|
87
|
-
* @returns {string}
|
|
88
85
|
*/
|
|
89
86
|
function normalizeRole (role) {
|
|
90
87
|
if (role === ROLES.MODEL) return ROLES.ASSISTANT
|
|
@@ -520,7 +517,6 @@ function formatOutputMessages (response, isStreaming = false) {
|
|
|
520
517
|
/**
|
|
521
518
|
* Format embedding output from response
|
|
522
519
|
* @param {object} response
|
|
523
|
-
* @returns {string}
|
|
524
520
|
*/
|
|
525
521
|
function formatEmbeddingOutput (response) {
|
|
526
522
|
if (!response?.embeddings?.length) {
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
* Formats tool call input as a JSON string.
|
|
5
5
|
* @param {string} toolName - The name of the tool being called
|
|
6
6
|
* @param {object} toolArguments - The arguments passed to the tool
|
|
7
|
-
* @returns {string} Formatted input string
|
|
8
7
|
*/
|
|
9
8
|
function formatInput (toolName, toolArguments) {
|
|
10
9
|
if (!toolName && !toolArguments) return ''
|
|
@@ -25,7 +24,6 @@ function formatInput (toolName, toolArguments) {
|
|
|
25
24
|
* MCP tool results contain a `content` array with items like:
|
|
26
25
|
* `[{ type: 'text', text: '...' }, { type: 'image', data: '...', mimeType: '...' }]`
|
|
27
26
|
* @param {object} result - The MCP CallToolResult
|
|
28
|
-
* @returns {string} JSON string of `{ content: Array<{type, text, annotations, meta}>, isError: boolean }`
|
|
29
27
|
*/
|
|
30
28
|
function formatOutput (result) {
|
|
31
29
|
if (!result) return ''
|
|
@@ -6,7 +6,6 @@ const INPUT_TYPE_TEXT = 'input_text'
|
|
|
6
6
|
|
|
7
7
|
const IMAGE_FALLBACK = '[image]'
|
|
8
8
|
const FILE_FALLBACK = '[file]'
|
|
9
|
-
const AUDIO_FALLBACK = '[audio]'
|
|
10
9
|
|
|
11
10
|
// OpenAI audio `format` values that don't map cleanly to `audio/<format>`.
|
|
12
11
|
const AUDIO_MIME_TYPES = {
|
|
@@ -19,6 +18,5 @@ module.exports = {
|
|
|
19
18
|
INPUT_TYPE_TEXT,
|
|
20
19
|
IMAGE_FALLBACK,
|
|
21
20
|
FILE_FALLBACK,
|
|
22
|
-
AUDIO_FALLBACK,
|
|
23
21
|
AUDIO_MIME_TYPES,
|
|
24
22
|
}
|
|
@@ -6,7 +6,8 @@ const {
|
|
|
6
6
|
PROMPT_MULTIMODAL,
|
|
7
7
|
INSTRUMENTATION_METHOD_AUTO,
|
|
8
8
|
} = require('../../constants/tags')
|
|
9
|
-
const { audioMimeTypeFromFormat, formatAudioPart
|
|
9
|
+
const { audioMimeTypeFromFormat, formatAudioPart } = require('../../audio-utils')
|
|
10
|
+
const { safeJsonParse } = require('../../util')
|
|
10
11
|
const { AUDIO_MIME_TYPES } = require('./constants')
|
|
11
12
|
const {
|
|
12
13
|
extractChatTemplateFromInstructions,
|
|
@@ -14,7 +15,7 @@ const {
|
|
|
14
15
|
extractTextFromContentItem,
|
|
15
16
|
extractContentParts,
|
|
16
17
|
hasMultimodalInputs,
|
|
17
|
-
|
|
18
|
+
getModelProviderAndClient,
|
|
18
19
|
} = require('./utils')
|
|
19
20
|
|
|
20
21
|
const allowedParamKeys = new Set([
|
|
@@ -108,10 +109,7 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
|
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
_getModelProviderAndClient (baseUrl = '') {
|
|
111
|
-
|
|
112
|
-
if (modelProvider === 'azure_openai') return { modelProvider, client: 'AzureOpenAI' }
|
|
113
|
-
if (modelProvider === 'deepseek') return { modelProvider, client: 'DeepSeek' }
|
|
114
|
-
return { modelProvider, client: 'OpenAI' }
|
|
112
|
+
return getModelProviderAndClient(baseUrl)
|
|
115
113
|
}
|
|
116
114
|
|
|
117
115
|
_extractMetrics (response) {
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
g711ToPcm16,
|
|
5
|
+
g711Variant,
|
|
6
|
+
isPcm16AudioMime,
|
|
7
|
+
pcm16ToWav,
|
|
8
|
+
} = require('../../audio-codec')
|
|
9
|
+
const { fitsInlineAudioBudget, formatAudioPartWithGuard } = require('../../audio-utils')
|
|
10
|
+
const {
|
|
11
|
+
AUDIO_FALLBACK,
|
|
12
|
+
G711_SAMPLE_RATE,
|
|
13
|
+
LLMOBS_AUDIO_INLINE_MAX_BYTES,
|
|
14
|
+
PCM16_BYTES_PER_SAMPLE,
|
|
15
|
+
WAV_HEADER_BYTES,
|
|
16
|
+
} = require('../../constants/audio')
|
|
17
|
+
const { storage: llmobsStorage } = require('../../storage')
|
|
18
|
+
const { safeJsonParse } = require('../../util')
|
|
19
|
+
const LLMObsPlugin = require('../base')
|
|
20
|
+
const { getModelProviderAndClient } = require('./utils')
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @typedef {import('../../audio-utils').AudioPart} AudioPart
|
|
24
|
+
* @typedef {{
|
|
25
|
+
* role: string,
|
|
26
|
+
* content: string,
|
|
27
|
+
* audioParts?: AudioPart[],
|
|
28
|
+
* toolCalls?: object[],
|
|
29
|
+
* toolResults?: object[],
|
|
30
|
+
* }} Message
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Turn a turn side's raw audio into a playable audio part.
|
|
35
|
+
*
|
|
36
|
+
* Realtime audio is raw PCM16 (24 kHz mono) by default, which the UI can't render, so it is wrapped
|
|
37
|
+
* in a WAV container — lossless, just a header. G.711 telephony audio, used by phone-call
|
|
38
|
+
* integrations, is decoded to PCM16 and likewise WAV-wrapped at its fixed 8 kHz rate. Anything else
|
|
39
|
+
* is passed to the size guard as-is, which keeps it only if it is renderable and within budget.
|
|
40
|
+
*
|
|
41
|
+
* Both conversions expand what they are given — G.711 doubles it, and either way a WAV header is
|
|
42
|
+
* prepended — so the budget is checked against the size the conversion *would* produce before
|
|
43
|
+
* running it. Checking afterwards, as the guard alone does, means decoding and copying megabytes
|
|
44
|
+
* only to discard them: the retention cap admits roughly twice what a G.711 turn can ever inline.
|
|
45
|
+
*
|
|
46
|
+
* @param {Buffer} audio
|
|
47
|
+
* @param {string} mimeType
|
|
48
|
+
* @param {number} sampleRate
|
|
49
|
+
* @param {number} maxBytes - Encoded bytes still available to this turn. See `setLLMObsTags`.
|
|
50
|
+
* @returns {AudioPart | undefined}
|
|
51
|
+
*/
|
|
52
|
+
function buildAudioPart (audio, mimeType, sampleRate, maxBytes) {
|
|
53
|
+
if (!audio.length) return
|
|
54
|
+
|
|
55
|
+
if (isPcm16AudioMime(mimeType)) {
|
|
56
|
+
if (!fitsInlineAudioBudget(audio.length + WAV_HEADER_BYTES, maxBytes)) return
|
|
57
|
+
return formatAudioPartWithGuard(pcm16ToWav(audio, sampleRate), 'audio/wav', maxBytes)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const variant = g711Variant(mimeType)
|
|
61
|
+
if (variant !== undefined) {
|
|
62
|
+
if (!fitsInlineAudioBudget(audio.length * PCM16_BYTES_PER_SAMPLE + WAV_HEADER_BYTES, maxBytes)) return
|
|
63
|
+
return formatAudioPartWithGuard(
|
|
64
|
+
pcm16ToWav(g711ToPcm16(audio, variant), G711_SAMPLE_RATE), 'audio/wav', maxBytes
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return formatAudioPartWithGuard(audio, mimeType, maxBytes)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Encoded size an already-built message contributes to the span event, which for audio is the
|
|
73
|
+
* base64 content itself.
|
|
74
|
+
*
|
|
75
|
+
* @param {Message | undefined} message
|
|
76
|
+
*/
|
|
77
|
+
function audioBytesOf (message) {
|
|
78
|
+
return message?.audioParts?.[0]?.content?.length ?? 0
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @param {object[]} toolCalls
|
|
83
|
+
* @returns {object[]}
|
|
84
|
+
*/
|
|
85
|
+
function parseToolCallArguments (toolCalls) {
|
|
86
|
+
return toolCalls.map(toolCall => ({ ...toolCall, arguments: safeJsonParse(toolCall.arguments ?? '', {}) }))
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Build the message for one side of a turn.
|
|
91
|
+
*
|
|
92
|
+
* @param {string} role
|
|
93
|
+
* @param {import('../../../../../datadog-instrumentations/src/openai-realtime/session').TurnSide} side
|
|
94
|
+
* @param {number} maxAudioBytes - Encoded audio bytes still available to this turn.
|
|
95
|
+
* @returns {Message | undefined}
|
|
96
|
+
*/
|
|
97
|
+
function buildMessage (role, side, maxAudioBytes) {
|
|
98
|
+
const audioPart = buildAudioPart(side.audio, side.mimeType, side.sampleRate, maxAudioBytes)
|
|
99
|
+
|
|
100
|
+
let content = side.transcript || side.text
|
|
101
|
+
if (!content && audioPart === undefined && side.audioPresent) {
|
|
102
|
+
// Audio was captured but couldn't be turned into a playable part — an unsupported format, or
|
|
103
|
+
// over the size budget — and there is no transcript, so surface a marker rather than let the
|
|
104
|
+
// turn look silently empty.
|
|
105
|
+
content = AUDIO_FALLBACK
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const toolCalls = side.toolCalls?.length ? parseToolCallArguments(side.toolCalls) : undefined
|
|
109
|
+
const toolResults = side.toolResults.length ? side.toolResults : undefined
|
|
110
|
+
|
|
111
|
+
if (!content && audioPart === undefined && toolCalls === undefined && toolResults === undefined) return
|
|
112
|
+
|
|
113
|
+
/** @type {Message} */
|
|
114
|
+
const message = { role, content: content || '' }
|
|
115
|
+
if (audioPart !== undefined) message.audioParts = [audioPart]
|
|
116
|
+
if (toolCalls !== undefined) message.toolCalls = toolCalls
|
|
117
|
+
if (toolResults !== undefined) message.toolResults = toolResults
|
|
118
|
+
|
|
119
|
+
return message
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* @param {{ input_tokens?: number, output_tokens?: number, total_tokens?: number } | undefined} usage
|
|
124
|
+
* @returns {Record<string, number> | undefined}
|
|
125
|
+
*/
|
|
126
|
+
function usageMetrics (usage) {
|
|
127
|
+
if (!usage) return
|
|
128
|
+
|
|
129
|
+
const { input_tokens: inputTokens, output_tokens: outputTokens } = usage
|
|
130
|
+
let { total_tokens: totalTokens } = usage
|
|
131
|
+
|
|
132
|
+
/** @type {Record<string, number>} */
|
|
133
|
+
const metrics = {}
|
|
134
|
+
if (inputTokens != null) metrics.input_tokens = inputTokens
|
|
135
|
+
if (outputTokens != null) metrics.output_tokens = outputTokens
|
|
136
|
+
// Mirror the chat and responses fallback.
|
|
137
|
+
if (totalTokens == null && inputTokens != null && outputTokens != null) {
|
|
138
|
+
totalTokens = inputTokens + outputTokens
|
|
139
|
+
}
|
|
140
|
+
if (totalTokens != null) metrics.total_tokens = totalTokens
|
|
141
|
+
|
|
142
|
+
return metrics.total_tokens === undefined && metrics.input_tokens === undefined ? undefined : metrics
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* LLM Observability spans for one OpenAI Realtime turn.
|
|
147
|
+
*
|
|
148
|
+
* The turn is modelled as a small tree — a `workflow` root with a `user speech` window, the `llm`
|
|
149
|
+
* generation span, and an `agent speech` window — so span duration is meaningful (the llm span
|
|
150
|
+
* measures model work, not the human's speaking time) and time-to-first-agent-audio falls out of the
|
|
151
|
+
* span boundaries. Consumers identify the phase spans by span kind and name, so those strings are a
|
|
152
|
+
* contract: renaming one is a breaking change for the UI and the backend metric alike.
|
|
153
|
+
*/
|
|
154
|
+
class RealtimeLLMObsPlugin extends LLMObsPlugin {
|
|
155
|
+
static integration = 'openai'
|
|
156
|
+
static system = 'openai'
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* The instrumentation replays the turn with `traceSync`, which never publishes `asyncEnd`, so tag
|
|
160
|
+
* on `end` instead — before the sibling tracing plugin's `end` finishes the span, since the LLM
|
|
161
|
+
* Observability plugins are registered first.
|
|
162
|
+
*
|
|
163
|
+
* @param {object} ctx
|
|
164
|
+
*/
|
|
165
|
+
end (ctx) {
|
|
166
|
+
super.end(ctx)
|
|
167
|
+
super.asyncEnd(ctx)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** The whole perceived turn. Carries the transcripts so the waterfall row reads as a conversation. */
|
|
172
|
+
class RealtimeTurnLLMObsPlugin extends RealtimeLLMObsPlugin {
|
|
173
|
+
static id = 'openai_realtime_turn_llmobs'
|
|
174
|
+
static prefix = 'tracing:apm:openai:realtime:turn'
|
|
175
|
+
|
|
176
|
+
constructor (...args) {
|
|
177
|
+
super(...args)
|
|
178
|
+
|
|
179
|
+
this.addSub('dd-trace:openai:realtime:capture-context', turn => {
|
|
180
|
+
if (!this._tracerConfig.llmobs.DD_LLMOBS_ENABLED) return
|
|
181
|
+
|
|
182
|
+
// Composed on top of the tracing plugin's, so the replayed tree restores both the APM and the
|
|
183
|
+
// LLM Observability context the caller had active when the turn began.
|
|
184
|
+
const store = llmobsStorage.getStore()
|
|
185
|
+
const previous = turn.runInContext ?? (fn => fn())
|
|
186
|
+
turn.runInContext = fn => previous(() => llmobsStorage.run(store, fn))
|
|
187
|
+
})
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
getLLMObsSpanRegisterOptions (ctx) {
|
|
191
|
+
return {
|
|
192
|
+
kind: 'workflow',
|
|
193
|
+
// Provider-agnostic `audio turn` marker, so a consumer can gate on "is this a voice turn?".
|
|
194
|
+
name: 'realtime audio turn',
|
|
195
|
+
sessionId: ctx.turn.sessionId,
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
setLLMObsTags (ctx) {
|
|
200
|
+
const span = ctx.currentStore?.span
|
|
201
|
+
if (!span) return
|
|
202
|
+
|
|
203
|
+
const { input, output } = ctx.turn
|
|
204
|
+
this._tagger.tagTextIO(span, input.transcript || input.text, output.transcript || output.text)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** The model's generation work. The turn's audio, tool calls and token usage ride here. */
|
|
209
|
+
class RealtimeResponseLLMObsPlugin extends RealtimeLLMObsPlugin {
|
|
210
|
+
static id = 'openai_realtime_response_llmobs'
|
|
211
|
+
static prefix = 'tracing:apm:openai:realtime:response'
|
|
212
|
+
|
|
213
|
+
constructor (...args) {
|
|
214
|
+
super(...args)
|
|
215
|
+
|
|
216
|
+
// This plugin is the only consumer of a turn's audio bytes, and the instrumentation keeps them
|
|
217
|
+
// only while something is subscribed here — a disabled plugin unsubscribes, so an APM-only
|
|
218
|
+
// process never buffers audio it cannot use. Nothing is ever published; the subscription is the
|
|
219
|
+
// signal. Keep it if `buildMessage` stops reading `side.audio`, and the buffering stops with it.
|
|
220
|
+
this.addSub('dd-trace:openai:realtime:audio', () => {})
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
getLLMObsSpanRegisterOptions (ctx) {
|
|
224
|
+
const { turn } = ctx
|
|
225
|
+
const { modelProvider, client } = getModelProviderAndClient(turn.basePath)
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
kind: 'llm',
|
|
229
|
+
name: `${client}.createRealtimeResponse`,
|
|
230
|
+
modelName: turn.model || 'unknown_model',
|
|
231
|
+
modelProvider,
|
|
232
|
+
sessionId: turn.sessionId,
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
setLLMObsTags (ctx) {
|
|
237
|
+
const span = ctx.currentStore?.span
|
|
238
|
+
if (!span) return
|
|
239
|
+
|
|
240
|
+
const { turn } = ctx
|
|
241
|
+
|
|
242
|
+
// One budget for the whole event, spent across both sides rather than offered to each.
|
|
243
|
+
// A turn normally carries audio on both sides, so a per-message budget lets two individually
|
|
244
|
+
// accepted clips add up to twice the limit — and `writers/spans.js` responds by truncating the
|
|
245
|
+
// event's *entire* input and output, losing the transcripts too. Input is served first and the
|
|
246
|
+
// output falls back to its transcript when nothing is left, which is the same graceful
|
|
247
|
+
// degradation an oversize single clip already gets.
|
|
248
|
+
const inputMessage = buildMessage('user', turn.input, LLMOBS_AUDIO_INLINE_MAX_BYTES)
|
|
249
|
+
const remainingAudioBytes = LLMOBS_AUDIO_INLINE_MAX_BYTES - audioBytesOf(inputMessage)
|
|
250
|
+
const outputMessage = buildMessage('assistant', turn.output, remainingAudioBytes)
|
|
251
|
+
|
|
252
|
+
this._tagger.tagLLMIO(span, inputMessage ? [inputMessage] : [], outputMessage ? [outputMessage] : [])
|
|
253
|
+
this._tagger.tagMetadata(span, turn.metadata)
|
|
254
|
+
|
|
255
|
+
const metrics = usageMetrics(turn.usage)
|
|
256
|
+
if (metrics !== undefined) this._tagger.tagMetrics(span, metrics)
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** A speaking window — the human's or the agent's. These are timing regions; the audio rides on the llm span. */
|
|
261
|
+
class RealtimeSpeechLLMObsPlugin extends RealtimeLLMObsPlugin {
|
|
262
|
+
static id = 'openai_realtime_speech_llmobs'
|
|
263
|
+
static prefix = 'tracing:apm:openai:realtime:speech'
|
|
264
|
+
|
|
265
|
+
getLLMObsSpanRegisterOptions (ctx) {
|
|
266
|
+
return {
|
|
267
|
+
kind: 'workflow',
|
|
268
|
+
name: ctx.phase.llmobsName,
|
|
269
|
+
sessionId: ctx.turn.sessionId,
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
setLLMObsTags (ctx) {
|
|
274
|
+
const span = ctx.currentStore?.span
|
|
275
|
+
if (!span) return
|
|
276
|
+
|
|
277
|
+
this._tagger.tagTextIO(span, undefined, ctx.transcript)
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
module.exports = [
|
|
282
|
+
RealtimeTurnLLMObsPlugin,
|
|
283
|
+
RealtimeResponseLLMObsPlugin,
|
|
284
|
+
RealtimeSpeechLLMObsPlugin,
|
|
285
|
+
]
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { audioMimeTypeFromFormat, formatAudioPart } = require('../../audio-utils')
|
|
4
|
+
const { AUDIO_FALLBACK } = require('../../constants/audio')
|
|
3
5
|
const { UNKNOWN_MODEL_PROVIDER } = require('../../constants/tags')
|
|
4
|
-
const { audioMimeTypeFromFormat, formatAudioPart } = require('../../util')
|
|
5
6
|
const {
|
|
6
7
|
INPUT_TYPE_IMAGE,
|
|
7
8
|
INPUT_TYPE_FILE,
|
|
8
9
|
IMAGE_FALLBACK,
|
|
9
10
|
FILE_FALLBACK,
|
|
10
|
-
AUDIO_FALLBACK,
|
|
11
11
|
AUDIO_MIME_TYPES,
|
|
12
12
|
} = require('./constants')
|
|
13
13
|
|
|
@@ -177,7 +177,6 @@ function extractContentParts (parts) {
|
|
|
177
177
|
* client baseURL convention.
|
|
178
178
|
*
|
|
179
179
|
* @param {string} baseUrl
|
|
180
|
-
* @returns {string}
|
|
181
180
|
*/
|
|
182
181
|
function getOpenAIModelProvider (baseUrl = '') {
|
|
183
182
|
if (baseUrl.includes('azure')) return 'azure_openai'
|
|
@@ -186,11 +185,25 @@ function getOpenAIModelProvider (baseUrl = '') {
|
|
|
186
185
|
return UNKNOWN_MODEL_PROVIDER
|
|
187
186
|
}
|
|
188
187
|
|
|
188
|
+
/**
|
|
189
|
+
* Pair an OpenAI-compatible base URL's model provider with the client label used in span names.
|
|
190
|
+
*
|
|
191
|
+
* @param {string} [baseUrl]
|
|
192
|
+
* @returns {{ modelProvider: string, client: string }}
|
|
193
|
+
*/
|
|
194
|
+
function getModelProviderAndClient (baseUrl = '') {
|
|
195
|
+
const modelProvider = getOpenAIModelProvider(baseUrl)
|
|
196
|
+
if (modelProvider === 'azure_openai') return { modelProvider, client: 'AzureOpenAI' }
|
|
197
|
+
if (modelProvider === 'deepseek') return { modelProvider, client: 'DeepSeek' }
|
|
198
|
+
return { modelProvider, client: 'OpenAI' }
|
|
199
|
+
}
|
|
200
|
+
|
|
189
201
|
module.exports = {
|
|
190
202
|
extractChatTemplateFromInstructions,
|
|
191
203
|
normalizePromptVariables,
|
|
192
204
|
extractTextFromContentItem,
|
|
193
205
|
extractContentParts,
|
|
194
206
|
hasMultimodalInputs,
|
|
207
|
+
getModelProviderAndClient,
|
|
195
208
|
getOpenAIModelProvider,
|
|
196
209
|
}
|