dd-trace 6.12.0 → 6.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE-3rdparty.csv +3 -1
- package/ci/init.js +8 -3
- package/ci/test-optimization-validation/ci-package-scripts.js +2 -2
- package/ci/test-optimization-validation/ci-remediation.js +6 -6
- package/ci/test-optimization-validation/command-blocker.js +7 -5
- package/ci/test-optimization-validation/framework-adapters/cucumber.js +1 -1
- package/ci/test-optimization-validation/framework-adapters/cypress.js +9 -11
- package/ci/test-optimization-validation/framework-adapters/vitest.js +3 -3
- package/ci/test-optimization-validation/package-check.js +1 -1
- package/ci/test-optimization-validation/report-writer.js +7 -5
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +5 -4
- package/ci/vitest-no-worker-init-runner-setup.mjs +8 -0
- package/ci/vitest-no-worker-init-setup.mjs +13 -13
- package/ci/vitest-no-worker-init-v5-setup.mjs +4 -0
- package/index.d.ts +127 -26
- package/package.json +22 -19
- package/packages/datadog-esbuild/index.js +3 -1
- package/packages/datadog-instrumentations/src/bunyan.js +4 -2
- package/packages/datadog-instrumentations/src/cypress-config.js +19 -17
- package/packages/datadog-instrumentations/src/fs.js +24 -20
- package/packages/datadog-instrumentations/src/google-cloud-vertexai.js +2 -2
- package/packages/datadog-instrumentations/src/graphql.js +16 -16
- package/packages/datadog-instrumentations/src/helpers/bunyan.js +12 -3
- package/packages/datadog-instrumentations/src/helpers/graphql-jit-runtime.js +504 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +266 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +1 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +95 -30
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +0 -25
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +83 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +85 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mercurius.js +30 -12
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +13 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +279 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +12 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +562 -27
- package/packages/datadog-instrumentations/src/jest.js +165 -38
- package/packages/datadog-instrumentations/src/knex.js +76 -0
- package/packages/datadog-instrumentations/src/mariadb-bundle.js +1597 -0
- package/packages/datadog-instrumentations/src/mariadb.js +13 -0
- package/packages/datadog-instrumentations/src/mocha/common.js +59 -2
- package/packages/datadog-instrumentations/src/mocha/main.js +25 -15
- package/packages/datadog-instrumentations/src/mocha/utils.js +2 -1
- package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +98 -0
- package/packages/datadog-instrumentations/src/mocha/worker.js +12 -8
- package/packages/datadog-instrumentations/src/net.js +14 -17
- package/packages/datadog-instrumentations/src/next.js +212 -62
- package/packages/datadog-instrumentations/src/openai.js +42 -40
- package/packages/datadog-instrumentations/src/pg.js +15 -0
- package/packages/datadog-instrumentations/src/pino.js +15 -4
- package/packages/datadog-instrumentations/src/playwright.js +137 -27
- package/packages/datadog-instrumentations/src/rum-browser-scripts.js +40 -0
- package/packages/datadog-instrumentations/src/sequelize.js +94 -2
- package/packages/datadog-instrumentations/src/undici.js +53 -4
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +44 -7
- package/packages/datadog-instrumentations/src/vitest-main.js +92 -9
- package/packages/datadog-instrumentations/src/vitest-worker.js +82 -14
- package/packages/datadog-instrumentations/src/webdriverio.js +986 -1
- package/packages/datadog-instrumentations/src/winston.js +6 -3
- package/packages/datadog-plugin-amqp10/src/consumer.js +3 -1
- package/packages/datadog-plugin-amqp10/src/producer.js +3 -1
- package/packages/datadog-plugin-amqp10/src/util.js +2 -1
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +6 -1
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +16 -5
- package/packages/datadog-plugin-cassandra-driver/src/index.js +8 -3
- package/packages/datadog-plugin-cucumber/src/index.js +0 -2
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +262 -48
- package/packages/datadog-plugin-cypress/src/support.js +1 -0
- package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +4 -1
- package/packages/datadog-plugin-graphql/src/execute.js +998 -196
- package/packages/datadog-plugin-graphql/src/index.js +27 -7
- package/packages/datadog-plugin-graphql/src/jit.js +136 -0
- package/packages/datadog-plugin-graphql/src/request.js +18 -27
- package/packages/datadog-plugin-graphql/src/resolve-error.js +108 -0
- package/packages/datadog-plugin-graphql/src/tools/signature.js +1 -2
- package/packages/datadog-plugin-graphql/src/tools/transforms.js +45 -3
- package/packages/datadog-plugin-graphql/src/utils.js +208 -120
- package/packages/datadog-plugin-graphql/src/validate.js +10 -23
- package/packages/datadog-plugin-http/src/client.js +2 -15
- package/packages/datadog-plugin-http2/src/client.js +2 -15
- package/packages/datadog-plugin-jest/src/index.js +0 -6
- package/packages/datadog-plugin-langchain/src/tracing.js +7 -0
- package/packages/datadog-plugin-mocha/src/index.js +403 -75
- package/packages/datadog-plugin-mongodb-core/src/query.js +6 -0
- package/packages/datadog-plugin-mysql/src/index.js +41 -15
- package/packages/datadog-plugin-net/src/tcp.js +3 -1
- package/packages/datadog-plugin-next/src/index.js +63 -29
- package/packages/datadog-plugin-openai-agents/src/integration.js +15 -11
- package/packages/datadog-plugin-playwright/src/index.js +176 -28
- package/packages/datadog-plugin-undici/src/index.js +29 -56
- package/packages/datadog-plugin-vitest/src/index.js +6 -12
- package/packages/dd-trace/src/aiguard/evaluation.js +96 -21
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -3
- package/packages/dd-trace/src/aiguard/messages/anthropic.js +29 -10
- package/packages/dd-trace/src/aiguard/messages/openai.js +8 -1
- package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +20 -6
- package/packages/dd-trace/src/aiguard/noop.js +9 -1
- package/packages/dd-trace/src/aiguard/redaction.js +183 -0
- package/packages/dd-trace/src/aiguard/sdk.js +7 -2
- package/packages/dd-trace/src/aiguard/tags.js +2 -0
- package/packages/dd-trace/src/appsec/api_security/index.js +23 -6
- package/packages/dd-trace/src/appsec/api_security/sampler.js +99 -40
- package/packages/dd-trace/src/appsec/graphql.js +6 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +3 -3
- package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +2 -1
- package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +8 -2
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +11 -36
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +10 -9
- package/packages/dd-trace/src/appsec/lambda.js +44 -26
- package/packages/dd-trace/src/appsec/rule_manager.js +2 -0
- package/packages/dd-trace/src/ci-visibility/encode/json-encoder.js +32 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +8 -8
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +6 -4
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +20 -4
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +7 -150
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +7 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agents.js +54 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/writer.js +1 -2
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +397 -234
- package/packages/dd-trace/src/ci-visibility/exporters/limits.js +9 -0
- package/packages/dd-trace/src/ci-visibility/exporters/request.js +323 -31
- package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +42 -0
- package/packages/dd-trace/src/ci-visibility/final-flush.js +21 -0
- package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +57 -18
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +0 -3
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +164 -71
- package/packages/dd-trace/src/ci-visibility/requests/video-request.js +176 -0
- package/packages/dd-trace/src/ci-visibility/telemetry.js +10 -7
- package/packages/dd-trace/src/ci-visibility/test-video.js +61 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +10 -0
- package/packages/dd-trace/src/config/index.js +75 -14
- package/packages/dd-trace/src/config/parsers.js +11 -1
- package/packages/dd-trace/src/config/stable.js +3 -3
- package/packages/dd-trace/src/config/supported-configurations.json +47 -2
- package/packages/dd-trace/src/crashtracking/crashtracker.js +82 -18
- package/packages/dd-trace/src/debugger/config.js +12 -1
- package/packages/dd-trace/src/debugger/constants.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +2 -1
- package/packages/dd-trace/src/debugger/devtools_client/request-options.js +33 -0
- package/packages/dd-trace/src/debugger/devtools_client/send.js +7 -39
- package/packages/dd-trace/src/debugger/devtools_client/status.js +11 -6
- package/packages/dd-trace/src/debugger/devtools_client/tags.js +41 -0
- package/packages/dd-trace/src/debugger/index.js +23 -3
- package/packages/dd-trace/src/dogstatsd.js +532 -71
- package/packages/dd-trace/src/encode/0.4-cross-payload.js +170 -0
- package/packages/dd-trace/src/encode/0.4.js +5 -5
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +9 -8
- package/packages/dd-trace/src/evp_proxy/direct.js +27 -23
- package/packages/dd-trace/src/exporters/agent/index.js +24 -3
- package/packages/dd-trace/src/exporters/agent/writer.js +28 -11
- package/packages/dd-trace/src/exporters/agentless/index.js +7 -9
- package/packages/dd-trace/src/exporters/agentless/writer.js +100 -118
- package/packages/dd-trace/src/exporters/common/agents.js +28 -6
- package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -2
- package/packages/dd-trace/src/exporters/common/final-flush-request-tracker.js +191 -0
- package/packages/dd-trace/src/exporters/common/request.js +31 -11
- package/packages/dd-trace/src/exporters/common/url.js +40 -1
- package/packages/dd-trace/src/exporters/common/writer.js +31 -7
- package/packages/dd-trace/src/exporters/span-stats/index.js +23 -4
- package/packages/dd-trace/src/external-logger/src/index.js +4 -3
- package/packages/dd-trace/src/flush.js +105 -0
- package/packages/dd-trace/src/id.js +14 -0
- package/packages/dd-trace/src/index.js +5 -2
- package/packages/dd-trace/src/llmobs/experiments/client.js +4 -0
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +52 -2
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +420 -105
- package/packages/dd-trace/src/llmobs/experiments/index.js +54 -42
- package/packages/dd-trace/src/llmobs/experiments/noop.js +39 -8
- package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
- package/packages/dd-trace/src/llmobs/experiments/util.js +21 -0
- package/packages/dd-trace/src/llmobs/index.js +111 -48
- package/packages/dd-trace/src/llmobs/noop.js +2 -1
- package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +22 -71
- package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +23 -13
- package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +8 -4
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +6 -3
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +7 -0
- package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +13 -6
- package/packages/dd-trace/src/llmobs/plugins/openai-agents/utils.js +5 -7
- package/packages/dd-trace/src/llmobs/util.js +0 -33
- package/packages/dd-trace/src/llmobs/writers/base.js +106 -53
- package/packages/dd-trace/src/log/index.js +7 -1
- package/packages/dd-trace/src/log-submission/log-submission-plugin.js +249 -0
- package/packages/dd-trace/src/noop/span.js +5 -0
- package/packages/dd-trace/src/openfeature/writers/base.js +14 -12
- package/packages/dd-trace/src/openfeature/writers/exposures.js +8 -1
- package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +45 -3
- package/packages/dd-trace/src/opentelemetry/logs/index.js +9 -25
- package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +7 -4
- package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +14 -4
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +18 -23
- package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +8 -0
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +18 -6
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +14 -2
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +25 -5
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +61 -25
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +19 -4
- package/packages/dd-trace/src/opentelemetry/resource-attributes.js +55 -0
- package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +10 -0
- package/packages/dd-trace/src/opentelemetry/tracer.js +26 -2
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +494 -351
- package/packages/dd-trace/src/opentracing/span.js +28 -0
- package/packages/dd-trace/src/otel-thread-ctx.js +23 -5
- package/packages/dd-trace/src/payload-tagging/index.js +2 -1
- package/packages/dd-trace/src/plugin_manager.js +2 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +1 -6
- package/packages/dd-trace/src/plugins/tracing.js +12 -10
- package/packages/dd-trace/src/plugins/util/ci.js +3 -1
- package/packages/dd-trace/src/plugins/util/inferred_proxy.js +16 -15
- package/packages/dd-trace/src/plugins/util/status-validator.js +98 -0
- package/packages/dd-trace/src/plugins/util/test.js +157 -41
- package/packages/dd-trace/src/plugins/util/url.js +2 -2
- package/packages/dd-trace/src/plugins/util/web.js +6 -74
- package/packages/dd-trace/src/profiling/config.js +1 -0
- package/packages/dd-trace/src/profiling/oom.js +11 -5
- package/packages/dd-trace/src/profiling/profiler.js +30 -13
- package/packages/dd-trace/src/profiling/profilers/events.js +3 -3
- package/packages/dd-trace/src/profiling/profilers/space.js +38 -11
- package/packages/dd-trace/src/profiling/profilers/wall.js +14 -14
- package/packages/dd-trace/src/profiling/webspan-utils.js +7 -4
- package/packages/dd-trace/src/proxy.js +108 -10
- package/packages/dd-trace/src/remote_config/index.js +319 -101
- package/packages/dd-trace/src/remote_config/scheduler.js +37 -12
- package/packages/dd-trace/src/runtime_metrics/client.js +37 -8
- package/packages/dd-trace/src/runtime_metrics/index.js +5 -0
- package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +10 -1
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +22 -7
- package/packages/dd-trace/src/sampling_rule.js +36 -9
- package/packages/dd-trace/src/serverless/telemetry-delivery-tracker.js +51 -0
- package/packages/dd-trace/src/serverless/vercel.js +155 -0
- package/packages/dd-trace/src/serverless.js +45 -21
- package/packages/dd-trace/src/service-naming/schemas/util.js +1 -1
- package/packages/dd-trace/src/span_stats.js +64 -31
- package/packages/dd-trace/src/startup-log.js +3 -2
- package/packages/dd-trace/src/telemetry/agentless-url.js +28 -0
- package/packages/dd-trace/src/telemetry/send-data.js +18 -16
- package/packages/dd-trace/src/telemetry/session-propagation.js +3 -2
- package/packages/dd-trace/src/tracer.js +26 -5
- package/packages/dd-trace/src/tracer_metadata.js +11 -3
- package/packages/dd-trace/src/web-tags-cache.js +67 -13
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/packages/datadog-instrumentations/src/playwright-browser-scripts.js +0 -27
- package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +0 -53
- package/packages/dd-trace/src/encode/agentless-json.js +0 -209
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
const log = require('../../log')
|
|
4
4
|
const { ExperimentsClient } = require('./client')
|
|
5
|
-
const { Dataset
|
|
5
|
+
const { Dataset } = require('./dataset')
|
|
6
6
|
const { Experiment, ExternalExperiment } = require('./experiment')
|
|
7
7
|
const { validateTagsList } = require('./util')
|
|
8
8
|
const NoopExperiments = require('./noop')
|
|
9
9
|
|
|
10
|
+
const DEFAULT_PROJECT_NAME = 'default-project'
|
|
11
|
+
|
|
10
12
|
// Poll `attempt` with exponential backoff until it returns true or the time
|
|
11
13
|
// budget is spent. Used for eventually-consistent reads (pullDataset).
|
|
12
14
|
async function retryWithBackoff (attempt, { maxTotalMs = 30_000, baseDelayMs = 250, maxDelayMs = 8000 } = {}) {
|
|
@@ -33,8 +35,8 @@ class Experiments {
|
|
|
33
35
|
|
|
34
36
|
constructor (config, llmobs) {
|
|
35
37
|
this.#config = config
|
|
36
|
-
this.#llmobs = llmobs
|
|
37
|
-
this.#projectName = config.llmobs?.
|
|
38
|
+
this.#llmobs = config.llmobs?.mlApp || config.service ? llmobs : undefined
|
|
39
|
+
this.#projectName = config.llmobs?.projectName || DEFAULT_PROJECT_NAME
|
|
38
40
|
this.#client = this.#clientForProject(this.#projectName)
|
|
39
41
|
}
|
|
40
42
|
|
|
@@ -51,27 +53,26 @@ class Experiments {
|
|
|
51
53
|
})
|
|
52
54
|
}
|
|
53
55
|
|
|
56
|
+
/**
|
|
57
|
+
* @param {string | undefined} projectName
|
|
58
|
+
* @returns {ExperimentsClient}
|
|
59
|
+
*/
|
|
60
|
+
#clientForOperation (projectName) {
|
|
61
|
+
if (projectName !== undefined && projectName !== this.#projectName) {
|
|
62
|
+
return this.#clientForProject(projectName)
|
|
63
|
+
}
|
|
64
|
+
if (this.#client === undefined) this.#client = this.#clientForProject(projectName)
|
|
65
|
+
return this.#client
|
|
66
|
+
}
|
|
67
|
+
|
|
54
68
|
// Create a local dataset buffer. Pushed remotely on first experiment run.
|
|
55
69
|
createDataset (name, descriptionOrOptions = '') {
|
|
56
70
|
const options = typeof descriptionOrOptions === 'string'
|
|
57
71
|
? { description: descriptionOrOptions }
|
|
58
72
|
: (descriptionOrOptions ?? {})
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
if ((options.records) != null)
|
|
62
|
-
for (const record of options.records) {
|
|
63
|
-
if (record.id !== undefined && (typeof record.id !== 'string' || record.id.length === 0)) {
|
|
64
|
-
throw new Error('record id must be a non-empty string')
|
|
65
|
-
}
|
|
66
|
-
if (record.id !== undefined) {
|
|
67
|
-
if (recordIds.has(record.id)) throw new Error(`Duplicate record id '${record.id}'`)
|
|
68
|
-
recordIds.add(record.id)
|
|
69
|
-
}
|
|
70
|
-
dataset.addRecord(
|
|
71
|
-
new DatasetRecord(record.inputData, record.expectedOutput, record.metadata, record.id, record.tags)
|
|
72
|
-
)
|
|
73
|
-
}
|
|
74
|
-
}
|
|
73
|
+
const client = this.#clientForOperation(options.projectName)
|
|
74
|
+
const dataset = new Dataset(client, name, options.description ?? '')
|
|
75
|
+
if ((options.records) != null) dataset.addRecords(options.records)
|
|
75
76
|
return dataset
|
|
76
77
|
}
|
|
77
78
|
|
|
@@ -80,9 +81,11 @@ class Experiments {
|
|
|
80
81
|
// wait until that many records are readable. Pass `tags` to filter records by
|
|
81
82
|
// dataset record tags.
|
|
82
83
|
async pullDataset (name, options = {}) {
|
|
83
|
-
const { expectedRecordCount, maxWaitMs = 30_000, tags, version } = options
|
|
84
|
+
const { expectedRecordCount, maxWaitMs = 30_000, projectName, tags, version } = options
|
|
84
85
|
const filterTags = validateTagsList(tags)
|
|
85
|
-
const
|
|
86
|
+
const client = this.#clientForOperation(projectName)
|
|
87
|
+
const resolvedProjectName = projectName ?? this.#projectName
|
|
88
|
+
const projectId = await client.ensureProjectId()
|
|
86
89
|
|
|
87
90
|
let pulledDataset = null
|
|
88
91
|
let records = []
|
|
@@ -93,7 +96,7 @@ class Experiments {
|
|
|
93
96
|
const succeeded = await retryWithBackoff(async () => {
|
|
94
97
|
try {
|
|
95
98
|
if (pulledDataset === null) {
|
|
96
|
-
const datasets = await
|
|
99
|
+
const datasets = await client.listDatasets(projectId, { name })
|
|
97
100
|
for (const dataset of datasets) {
|
|
98
101
|
if (dataset.name() === name) {
|
|
99
102
|
pulledDataset = dataset
|
|
@@ -109,7 +112,7 @@ class Experiments {
|
|
|
109
112
|
// Follow the meta.after / page[cursor] pagination until the last page.
|
|
110
113
|
for (;;) {
|
|
111
114
|
// eslint-disable-next-line no-await-in-loop
|
|
112
|
-
const page = await
|
|
115
|
+
const page = await client.listDatasetRecords(projectId, pulledDataset.id(), {
|
|
113
116
|
cursor,
|
|
114
117
|
tags: filterTags,
|
|
115
118
|
version: datasetVersion,
|
|
@@ -129,13 +132,13 @@ class Experiments {
|
|
|
129
132
|
}, { maxTotalMs: maxWaitMs })
|
|
130
133
|
|
|
131
134
|
if (pulledDataset === null && lastError) {
|
|
132
|
-
throw new Error(`Failed to list datasets in project '${
|
|
135
|
+
throw new Error(`Failed to list datasets in project '${resolvedProjectName}': ${lastError}`)
|
|
133
136
|
}
|
|
134
137
|
if (pulledDataset === null) {
|
|
135
|
-
throw new Error(`Dataset '${name}' not found in project '${
|
|
138
|
+
throw new Error(`Dataset '${name}' not found in project '${resolvedProjectName}' (after ${maxWaitMs}ms)`)
|
|
136
139
|
}
|
|
137
140
|
if (!succeeded && lastError) {
|
|
138
|
-
throw new Error(`Failed to fetch records for dataset '${name}' in project '${
|
|
141
|
+
throw new Error(`Failed to fetch records for dataset '${name}' in project '${resolvedProjectName}': ${lastError}`)
|
|
139
142
|
}
|
|
140
143
|
if (!succeeded && expectedRecordCount != null) {
|
|
141
144
|
throw new Error(
|
|
@@ -151,7 +154,7 @@ class Experiments {
|
|
|
151
154
|
}
|
|
152
155
|
|
|
153
156
|
return Dataset.fromExisting(
|
|
154
|
-
|
|
157
|
+
client,
|
|
155
158
|
name,
|
|
156
159
|
pulledDataset.description(),
|
|
157
160
|
pulledDataset.id(),
|
|
@@ -163,9 +166,26 @@ class Experiments {
|
|
|
163
166
|
)
|
|
164
167
|
}
|
|
165
168
|
|
|
166
|
-
// Build an experiment
|
|
169
|
+
// Build an experiment with a dataset, task, evaluators, and optional project/config/tags.
|
|
167
170
|
experiment (options) {
|
|
168
|
-
|
|
171
|
+
const datasetProjectName = options?.dataset?.projectName?.()
|
|
172
|
+
if (options?.projectName !== undefined &&
|
|
173
|
+
datasetProjectName !== undefined &&
|
|
174
|
+
options.projectName !== datasetProjectName) {
|
|
175
|
+
throw new Error(
|
|
176
|
+
`Experiment project '${options.projectName}' does not match dataset project '${datasetProjectName}'`
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
const projectName = options?.projectName ?? datasetProjectName
|
|
180
|
+
const client = this.#clientForOperation(projectName)
|
|
181
|
+
const usesDatasetOverride = datasetProjectName !== undefined && datasetProjectName !== this.#projectName
|
|
182
|
+
const resolvedProjectName = projectName ?? this.#config.llmobs?.projectName
|
|
183
|
+
const experimentOptions = options?.projectName === undefined &&
|
|
184
|
+
(usesDatasetOverride || this.#config.llmobs?.projectName !== undefined) &&
|
|
185
|
+
resolvedProjectName !== undefined
|
|
186
|
+
? { ...options, projectName: resolvedProjectName }
|
|
187
|
+
: options
|
|
188
|
+
return new Experiment(client, experimentOptions, this.#llmobs)
|
|
169
189
|
}
|
|
170
190
|
|
|
171
191
|
/**
|
|
@@ -177,10 +197,11 @@ class Experiments {
|
|
|
177
197
|
* @returns {Promise<ExternalExperiment>}
|
|
178
198
|
*/
|
|
179
199
|
startExperiment (options) {
|
|
180
|
-
const client = options?.projectName
|
|
181
|
-
|
|
182
|
-
: this.#
|
|
183
|
-
|
|
200
|
+
const client = this.#clientForOperation(options?.projectName)
|
|
201
|
+
const experimentOptions = options?.projectName === undefined && this.#config.llmobs?.projectName !== undefined
|
|
202
|
+
? { ...options, projectName: this.#config.llmobs.projectName }
|
|
203
|
+
: options
|
|
204
|
+
return new Experiment(client, { ...experimentOptions, external: true }).start()
|
|
184
205
|
.then(experiment => new ExternalExperiment(experiment))
|
|
185
206
|
}
|
|
186
207
|
}
|
|
@@ -195,15 +216,6 @@ function createExperiments (config, llmobs) {
|
|
|
195
216
|
log.warn('LLMObs experiments: missing api and/or app keys, set DD_API_KEY and DD_APP_KEY')
|
|
196
217
|
return new NoopExperiments('DD_API_KEY and DD_APP_KEY are required for experiments')
|
|
197
218
|
}
|
|
198
|
-
if (!config.llmobs?.mlApp && !config.service) {
|
|
199
|
-
const reason = 'no project name configured; set the DD_LLMOBS_ML_APP environment variable (or llmobs.mlApp in ' +
|
|
200
|
-
'tracer.init()) to name the LLM Obs project, or DD_SERVICE (or service in tracer.init()) as a fallback, ' +
|
|
201
|
-
'then retry'
|
|
202
|
-
const experiments = new Experiments(config, llmobs)
|
|
203
|
-
return new NoopExperiments(reason, {
|
|
204
|
-
startExperiment: (options) => experiments.startExperiment(options),
|
|
205
|
-
})
|
|
206
|
-
}
|
|
207
219
|
return new Experiments(config, llmobs)
|
|
208
220
|
}
|
|
209
221
|
|
|
@@ -7,6 +7,15 @@ const NOOP_EXPERIMENT_ID = '00000000-0000-0000-0000-000000000000'
|
|
|
7
7
|
const NOOP_SPAN_ID = '0000000000000000'
|
|
8
8
|
const NOOP_TRACE_ID = '00000000000000000000000000000000'
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {object} DatasetRecordNew
|
|
12
|
+
* @property {string} [id]
|
|
13
|
+
* @property {unknown} inputData
|
|
14
|
+
* @property {unknown} [expectedOutput]
|
|
15
|
+
* @property {Record<string, unknown>} [metadata]
|
|
16
|
+
* @property {string[]} [tags]
|
|
17
|
+
*/
|
|
18
|
+
|
|
10
19
|
class NoopDataset {
|
|
11
20
|
#name
|
|
12
21
|
#description
|
|
@@ -37,6 +46,24 @@ class NoopDataset {
|
|
|
37
46
|
return this
|
|
38
47
|
}
|
|
39
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Add multiple records to a dataset.
|
|
51
|
+
* @param {DatasetRecordNew[]} records
|
|
52
|
+
* @returns {NoopDataset} This dataset for chaining.
|
|
53
|
+
*/
|
|
54
|
+
addRecords (records) {
|
|
55
|
+
for (const record of records) {
|
|
56
|
+
this.#records.push({
|
|
57
|
+
id: record.id ?? null,
|
|
58
|
+
input: record.inputData,
|
|
59
|
+
expectedOutput: record.expectedOutput ?? null,
|
|
60
|
+
metadata: record.metadata ?? {},
|
|
61
|
+
tags: [...(record.tags ?? [])],
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
return this
|
|
65
|
+
}
|
|
66
|
+
|
|
40
67
|
update (index, fields) {
|
|
41
68
|
const record = this.#records[index]
|
|
42
69
|
if (record == null) return this
|
|
@@ -114,6 +141,10 @@ class NoopDataset {
|
|
|
114
141
|
return null
|
|
115
142
|
}
|
|
116
143
|
|
|
144
|
+
projectName () {
|
|
145
|
+
return null
|
|
146
|
+
}
|
|
147
|
+
|
|
117
148
|
version () {
|
|
118
149
|
return null
|
|
119
150
|
}
|
|
@@ -165,7 +196,13 @@ class NoopExperiment {
|
|
|
165
196
|
}
|
|
166
197
|
|
|
167
198
|
run () {
|
|
168
|
-
return Promise.resolve({
|
|
199
|
+
return Promise.resolve({
|
|
200
|
+
experimentId: null,
|
|
201
|
+
rows: [],
|
|
202
|
+
summaryEvaluations: {},
|
|
203
|
+
runs: [],
|
|
204
|
+
url: null,
|
|
205
|
+
})
|
|
169
206
|
}
|
|
170
207
|
|
|
171
208
|
/**
|
|
@@ -200,11 +237,9 @@ class NoopExperiment {
|
|
|
200
237
|
// throwing, so intentionally disabled experiments remain graceful.
|
|
201
238
|
class NoopExperiments {
|
|
202
239
|
#reason
|
|
203
|
-
#startExperiment
|
|
204
240
|
|
|
205
|
-
constructor (reason
|
|
241
|
+
constructor (reason) {
|
|
206
242
|
this.#reason = reason || 'LLMObs experiments are not available'
|
|
207
|
-
this.#startExperiment = options.startExperiment
|
|
208
243
|
}
|
|
209
244
|
|
|
210
245
|
#warn () {
|
|
@@ -231,10 +266,6 @@ class NoopExperiments {
|
|
|
231
266
|
* @returns {Promise<ExternalExperiment>}
|
|
232
267
|
*/
|
|
233
268
|
startExperiment (options = {}) {
|
|
234
|
-
if (this.#startExperiment !== undefined && options.projectName) {
|
|
235
|
-
return this.#startExperiment(options)
|
|
236
|
-
}
|
|
237
|
-
|
|
238
269
|
this.#warn()
|
|
239
270
|
return Promise.resolve(new ExternalExperiment(new NoopExperiment(options.name, true)))
|
|
240
271
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { randomUUID } = require('node:crypto')
|
|
4
|
+
|
|
3
5
|
const log = require('../../log')
|
|
4
6
|
|
|
5
7
|
// Matches the backend and dd-trace-py evaluator metric label contract.
|
|
@@ -42,6 +44,23 @@ function tagOperationsAreEmpty (operations) {
|
|
|
42
44
|
)
|
|
43
45
|
}
|
|
44
46
|
|
|
47
|
+
/**
|
|
48
|
+
* @param {unknown} value
|
|
49
|
+
* @param {string} name
|
|
50
|
+
* @returns {number}
|
|
51
|
+
*/
|
|
52
|
+
function normalizePositiveInteger (value, name) {
|
|
53
|
+
if (!Number.isInteger(value) || value < 1) throw new Error(`${name} must be a positive integer`)
|
|
54
|
+
return value
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @returns {string}
|
|
59
|
+
*/
|
|
60
|
+
function generateRunId () {
|
|
61
|
+
return randomUUID()
|
|
62
|
+
}
|
|
63
|
+
|
|
45
64
|
/**
|
|
46
65
|
* @param {string} name
|
|
47
66
|
*/
|
|
@@ -297,11 +316,13 @@ module.exports = {
|
|
|
297
316
|
buildSpanMetadata,
|
|
298
317
|
buildTags,
|
|
299
318
|
durationNs,
|
|
319
|
+
generateRunId,
|
|
300
320
|
hasEntries,
|
|
301
321
|
inferMetricType,
|
|
302
322
|
mergeTags,
|
|
303
323
|
normalizeEvaluators,
|
|
304
324
|
normalizeJsonMetricValue,
|
|
325
|
+
normalizePositiveInteger,
|
|
305
326
|
recordTagsToObject,
|
|
306
327
|
sleep,
|
|
307
328
|
stringify,
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
const { channel } = require('dc-polyfill')
|
|
4
4
|
|
|
5
|
-
const {
|
|
5
|
+
const { registerTelemetryFlusher } = require('../flush')
|
|
6
6
|
const log = require('../log')
|
|
7
|
+
const { createServerlessDeliveryTracker } = require('../serverless')
|
|
7
8
|
const { DD_MAJOR } = require('../../../../version')
|
|
8
9
|
const startupLogs = require('../startup-log')
|
|
9
10
|
const {
|
|
@@ -23,7 +24,7 @@ const {
|
|
|
23
24
|
PROPAGATED_TRACE_ID_KEY,
|
|
24
25
|
} = require('./constants/tags')
|
|
25
26
|
const { storage } = require('./storage')
|
|
26
|
-
const { agentNameWireSafe,
|
|
27
|
+
const { agentNameWireSafe, resolveAgentAttribution } = require('./util')
|
|
27
28
|
const telemetry = require('./telemetry')
|
|
28
29
|
const LLMObsSpanProcessor = require('./span_processor')
|
|
29
30
|
const LLMObsEvalMetricsWriter = require('./writers/evaluations')
|
|
@@ -57,32 +58,54 @@ let spanWriter
|
|
|
57
58
|
/** @type {LLMObsEvalMetricsWriter | null} */
|
|
58
59
|
let evalWriter
|
|
59
60
|
|
|
61
|
+
let unregisterTelemetryFlusher
|
|
62
|
+
|
|
60
63
|
/** @type {import('../config/config-base')} */
|
|
61
64
|
let globalTracerConfig
|
|
62
65
|
|
|
66
|
+
/**
|
|
67
|
+
* @typedef {object} TraceTagInjection
|
|
68
|
+
* @property {import('../opentracing/span_context')} spanContext
|
|
69
|
+
* @property {Array<string | undefined>} [traceTagReplacements]
|
|
70
|
+
* @property {number} [optionalTraceTagCount]
|
|
71
|
+
*/
|
|
72
|
+
|
|
63
73
|
/**
|
|
64
74
|
* @param {@type import('../config/config-base')} config
|
|
65
75
|
*/
|
|
66
76
|
function enable (config) {
|
|
67
77
|
globalTracerConfig = config
|
|
68
78
|
|
|
79
|
+
const retiredSpanWriter = spanWriter
|
|
80
|
+
const retiredEvalWriter = evalWriter
|
|
81
|
+
const isReinitializing = Boolean(retiredSpanWriter || retiredEvalWriter)
|
|
82
|
+
unregisterTelemetryFlusher?.()
|
|
83
|
+
retireWriters(retiredSpanWriter, retiredEvalWriter)
|
|
84
|
+
|
|
69
85
|
const startTime = performance.now()
|
|
70
86
|
// create writers and eval writer append and flush channels
|
|
71
87
|
// span writer append is handled by the span processor
|
|
72
88
|
evalWriter = new LLMObsEvalMetricsWriter(config)
|
|
73
89
|
spanWriter = new LLMObsSpanWriter(config)
|
|
90
|
+
const currentEvalWriter = evalWriter
|
|
91
|
+
const currentSpanWriter = spanWriter
|
|
92
|
+
unregisterTelemetryFlusher = registerTelemetryFlusher(done => {
|
|
93
|
+
flushWriters(done, currentSpanWriter, currentEvalWriter)
|
|
94
|
+
})
|
|
74
95
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
96
|
+
if (!isReinitializing) {
|
|
97
|
+
evalMetricAppendCh.subscribe(handleEvalMetricAppend)
|
|
98
|
+
flushCh.subscribe(handleFlush)
|
|
99
|
+
registerUserSpanProcessorCh.subscribe(handleRegisterProcessor)
|
|
100
|
+
}
|
|
78
101
|
|
|
79
102
|
// span processing
|
|
80
103
|
spanProcessor = new LLMObsSpanProcessor(config)
|
|
81
104
|
spanProcessor.setWriter(spanWriter)
|
|
82
|
-
spanFinishCh.subscribe(handleSpanProcess)
|
|
105
|
+
if (!isReinitializing) spanFinishCh.subscribe(handleSpanProcess)
|
|
83
106
|
|
|
84
107
|
// distributed tracing for llmobs
|
|
85
|
-
injectCh.subscribe(handleLLMObsInjection)
|
|
108
|
+
if (!isReinitializing) injectCh.subscribe(handleLLMObsInjection)
|
|
86
109
|
|
|
87
110
|
setAgentStrategy(config, useAgentless => {
|
|
88
111
|
if (useAgentless && !(config.DD_API_KEY && config.site)) {
|
|
@@ -94,8 +117,10 @@ function enable (config) {
|
|
|
94
117
|
}
|
|
95
118
|
}
|
|
96
119
|
|
|
97
|
-
|
|
98
|
-
|
|
120
|
+
// A disable can happen while transport selection is still pending. Keep
|
|
121
|
+
// configuring these writers so their queued lifecycle flushes can drain.
|
|
122
|
+
currentEvalWriter.setAgentless(useAgentless)
|
|
123
|
+
currentSpanWriter.setAgentless(useAgentless)
|
|
99
124
|
|
|
100
125
|
telemetry.recordLLMObsEnabled(startTime, config)
|
|
101
126
|
log.debug('[LLMObs] Enabled LLM Observability with configuration: %o', config.llmobs)
|
|
@@ -109,19 +134,43 @@ function disable () {
|
|
|
109
134
|
if (injectCh.hasSubscribers) injectCh.unsubscribe(handleLLMObsInjection)
|
|
110
135
|
if (registerUserSpanProcessorCh.hasSubscribers) registerUserSpanProcessorCh.unsubscribe(handleRegisterProcessor)
|
|
111
136
|
|
|
112
|
-
spanWriter
|
|
113
|
-
evalWriter
|
|
137
|
+
const retiredSpanWriter = spanWriter
|
|
138
|
+
const retiredEvalWriter = evalWriter
|
|
114
139
|
spanProcessor?.setWriter(null)
|
|
140
|
+
unregisterTelemetryFlusher?.()
|
|
141
|
+
unregisterTelemetryFlusher = undefined
|
|
115
142
|
|
|
116
143
|
spanWriter = null
|
|
117
144
|
evalWriter = null
|
|
118
145
|
|
|
146
|
+
retireWriters(retiredSpanWriter, retiredEvalWriter)
|
|
147
|
+
|
|
119
148
|
log.debug('[LLMObs] Disabled LLM Observability')
|
|
120
149
|
}
|
|
121
150
|
|
|
151
|
+
/**
|
|
152
|
+
* Keeps retired writers reachable until their destroy-triggered deliveries complete.
|
|
153
|
+
* @param {LLMObsSpanWriter | null} retiredSpanWriter
|
|
154
|
+
* @param {LLMObsEvalMetricsWriter | null} retiredEvalWriter
|
|
155
|
+
* @returns {void}
|
|
156
|
+
*/
|
|
157
|
+
function retireWriters (retiredSpanWriter, retiredEvalWriter) {
|
|
158
|
+
const retiredWriters = [retiredSpanWriter, retiredEvalWriter].filter(Boolean)
|
|
159
|
+
if (retiredWriters.length === 0) return
|
|
160
|
+
let remainingWriters = retiredWriters.length
|
|
161
|
+
const unregisterRetiredFlusher = registerTelemetryFlusher(done => {
|
|
162
|
+
flushWriters(done, retiredSpanWriter, retiredEvalWriter)
|
|
163
|
+
})
|
|
164
|
+
function onWriterDestroyed () {
|
|
165
|
+
if (--remainingWriters === 0) unregisterRetiredFlusher?.()
|
|
166
|
+
}
|
|
167
|
+
for (const writer of retiredWriters) writer.destroy(onWriterDestroyed)
|
|
168
|
+
}
|
|
169
|
+
|
|
122
170
|
// since LLMObs traces can extend between services and be the same trace,
|
|
123
171
|
// we need to propagate the parent id, mlApp, session id, and sampling rate/decision.
|
|
124
|
-
|
|
172
|
+
/** @param {TraceTagInjection} injection */
|
|
173
|
+
function handleLLMObsInjection (injection) {
|
|
125
174
|
// Respect the standard propagator's gate: when trace tag propagation is
|
|
126
175
|
// disabled, don't write `x-datadog-tags` for LLMObs either.
|
|
127
176
|
if (globalTracerConfig.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH === 0) return
|
|
@@ -159,48 +208,62 @@ function handleLLMObsInjection ({ carrier }) {
|
|
|
159
208
|
mlObsSpanTags, parent
|
|
160
209
|
)
|
|
161
210
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
if (
|
|
167
|
-
if (
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
if (
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
// `_injectTags` may have already written into the carrier (it propagates all `_dd.p.*` from
|
|
174
|
-
// `_trace.tags`). This ensures the downstream sees a consistent id-only or id+name pair
|
|
175
|
-
// rather than a mix from different hops. The id is always digit-safe; an unsafe name is wiped.
|
|
211
|
+
const traceTagReplacements = []
|
|
212
|
+
if (parentId) traceTagReplacements.push(PROPAGATED_PARENT_ID_KEY, parentId)
|
|
213
|
+
if (mlApp) traceTagReplacements.push(PROPAGATED_ML_APP_KEY, mlApp)
|
|
214
|
+
if (sessionId) traceTagReplacements.push(PROPAGATED_SESSION_ID_KEY, sessionId)
|
|
215
|
+
if (sampleRate != null) traceTagReplacements.push(PROPAGATED_SAMPLE_RATE_KEY, sampleRate.toString())
|
|
216
|
+
if (samplingDecision != null) {
|
|
217
|
+
traceTagReplacements.push(PROPAGATED_SAMPLING_DECISION_KEY, samplingDecision.toString())
|
|
218
|
+
}
|
|
219
|
+
if (propagatedTraceId != null) traceTagReplacements.push(PROPAGATED_TRACE_ID_KEY, propagatedTraceId)
|
|
220
|
+
|
|
221
|
+
let optionalTraceTagCount = 0
|
|
176
222
|
if (parentAgentSpanId) {
|
|
177
|
-
|
|
178
|
-
|
|
223
|
+
traceTagReplacements.push(PROPAGATED_PARENT_AGENT_ID_KEY, parentAgentSpanId)
|
|
224
|
+
optionalTraceTagCount++
|
|
225
|
+
if (parentAgentName && agentNameWireSafe(parentAgentName)) {
|
|
226
|
+
traceTagReplacements.push(PROPAGATED_PARENT_AGENT_NAME_KEY, parentAgentName)
|
|
227
|
+
} else {
|
|
228
|
+
traceTagReplacements.push(PROPAGATED_PARENT_AGENT_NAME_KEY, undefined)
|
|
229
|
+
}
|
|
230
|
+
optionalTraceTagCount++
|
|
179
231
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
232
|
+
|
|
233
|
+
injection.traceTagReplacements = traceTagReplacements
|
|
234
|
+
injection.optionalTraceTagCount = optionalTraceTagCount
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Flushes the specified LLMObs writers and joins deliveries active at the boundary.
|
|
239
|
+
* @param {Function} [done]
|
|
240
|
+
* @param {LLMObsSpanWriter | null} [currentSpanWriter]
|
|
241
|
+
* @param {LLMObsEvalMetricsWriter | null} [currentEvalWriter]
|
|
242
|
+
* @returns {boolean} `true` when a writer throws synchronously.
|
|
243
|
+
*/
|
|
244
|
+
function flushWriters (done, currentSpanWriter = spanWriter, currentEvalWriter = evalWriter) {
|
|
245
|
+
let failed = false
|
|
246
|
+
const deliveryTracker = createServerlessDeliveryTracker()
|
|
247
|
+
const flush = writer => {
|
|
248
|
+
try {
|
|
249
|
+
if (deliveryTracker && writer) deliveryTracker.track(complete => writer.flush(complete))
|
|
250
|
+
// Non-serverless flushes retain the existing writer behavior.
|
|
251
|
+
else writer?.flush()
|
|
252
|
+
} catch (error) {
|
|
253
|
+
failed = true
|
|
254
|
+
log.warn('Failed to flush LLMObs writer:', error.message)
|
|
255
|
+
}
|
|
191
256
|
}
|
|
192
|
-
|
|
257
|
+
|
|
258
|
+
flush(currentSpanWriter)
|
|
259
|
+
flush(currentEvalWriter)
|
|
260
|
+
deliveryTracker?.waitForIdle(done)
|
|
261
|
+
if (!deliveryTracker) done?.()
|
|
262
|
+
return failed
|
|
193
263
|
}
|
|
194
264
|
|
|
195
265
|
function handleFlush () {
|
|
196
|
-
|
|
197
|
-
try {
|
|
198
|
-
spanWriter.flush()
|
|
199
|
-
evalWriter.flush()
|
|
200
|
-
} catch (e) {
|
|
201
|
-
err = 'writer_flush_error'
|
|
202
|
-
log.warn('Failed to flush LLMObs spans and evaluation metrics:', e.message)
|
|
203
|
-
}
|
|
266
|
+
const err = flushWriters() ? 'writer_flush_error' : ''
|
|
204
267
|
telemetry.recordUserFlush(err)
|
|
205
268
|
}
|
|
206
269
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
let NoopExperiments
|
|
4
4
|
|
|
5
5
|
class NoopLLMObs {
|
|
6
6
|
constructor (noopTracer) {
|
|
@@ -12,6 +12,7 @@ class NoopLLMObs {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
get experiments () {
|
|
15
|
+
NoopExperiments ??= require('./experiments/noop')
|
|
15
16
|
return new NoopExperiments('LLM Observability is not enabled')
|
|
16
17
|
}
|
|
17
18
|
|