dd-trace 6.20.1 → 6.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ci/diagnose.js +9 -14
- package/ci/init.js +1 -2
- package/ci/test-optimization-validation/ci-discovery.js +9 -4
- package/ci/test-optimization-validation/executable.js +1 -5
- package/ci/test-optimization-validation/framework-adapters/cypress.js +1 -4
- package/ci/test-optimization-validation/framework-adapters/vitest.js +1 -1
- package/ci/test-optimization-validation/generated-files.js +6 -4
- package/ci/test-optimization-validation/generated-test-contract.js +7 -3
- package/ci/test-optimization-validation/manifest-scaffold.js +7 -2
- package/ci/test-optimization-validation/manifest-schema.js +6 -7
- package/ci/test-optimization-validation/report-writer.js +6 -2
- package/ci/test-optimization-validation/runner-command.js +2 -4
- package/ci/test-optimization-validation/runner-contract.js +1 -2
- package/ci/test-optimization-validation/scenarios/auto-test-retries.js +1 -15
- package/ci/vitest-no-worker-init-setup.mjs +10 -7
- package/index.d.ts +116 -17
- package/initialize.mjs +1 -3
- package/openfeature/rules-based.d.ts +11 -0
- package/openfeature/rules-based.js +7 -0
- package/package.json +13 -11
- package/packages/datadog-core/src/storage.js +16 -0
- package/packages/datadog-esbuild/index.js +18 -12
- package/packages/datadog-instrumentations/src/aerospike.js +3 -3
- package/packages/datadog-instrumentations/src/ai.js +3 -3
- package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +0 -6
- package/packages/datadog-instrumentations/src/aws-sdk.js +21 -13
- package/packages/datadog-instrumentations/src/cassandra-driver.js +25 -20
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +9 -23
- package/packages/datadog-instrumentations/src/cucumber.js +48 -33
- package/packages/datadog-instrumentations/src/elasticsearch.js +1 -5
- package/packages/datadog-instrumentations/src/electron.js +6 -6
- package/packages/datadog-instrumentations/src/fastify.js +13 -4
- package/packages/datadog-instrumentations/src/fs.js +2 -2
- package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +9 -5
- package/packages/datadog-instrumentations/src/grpc/client.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/bundler-register.js +2 -2
- package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +1 -2
- package/packages/datadog-instrumentations/src/helpers/instrument.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/register.js +36 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentation-registry.js +87 -6
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +5 -6
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/postgres.js +48 -31
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +64 -11
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms/postgres.js +42 -4
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +7 -10
- package/packages/datadog-instrumentations/src/ioredis.js +7 -5
- package/packages/datadog-instrumentations/src/iovalkey.js +7 -5
- package/packages/datadog-instrumentations/src/jest.js +122 -11
- package/packages/datadog-instrumentations/src/knex.js +9 -5
- package/packages/datadog-instrumentations/src/mariadb-bundle.js +3 -3
- package/packages/datadog-instrumentations/src/mocha/common.js +20 -8
- package/packages/datadog-instrumentations/src/mocha/main.js +140 -43
- package/packages/datadog-instrumentations/src/mocha/utils.js +127 -38
- package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +61 -34
- package/packages/datadog-instrumentations/src/mocha/worker.js +7 -5
- package/packages/datadog-instrumentations/src/mongodb-core.js +3 -3
- package/packages/datadog-instrumentations/src/mongoose.js +3 -3
- package/packages/datadog-instrumentations/src/nova-sonic/audio.js +173 -0
- package/packages/datadog-instrumentations/src/nova-sonic/index.js +209 -0
- package/packages/datadog-instrumentations/src/nova-sonic/session.js +483 -0
- package/packages/datadog-instrumentations/src/openai-realtime/index.js +18 -6
- package/packages/datadog-instrumentations/src/openai-realtime/session.js +1 -1
- package/packages/datadog-instrumentations/src/openai.js +7 -0
- package/packages/datadog-instrumentations/src/pino.js +5 -6
- package/packages/datadog-instrumentations/src/playwright.js +3 -0
- package/packages/datadog-instrumentations/src/redis.js +27 -13
- package/packages/datadog-instrumentations/src/router.js +30 -0
- package/packages/datadog-instrumentations/src/rum-browser-scripts.js +2 -6
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +7 -5
- package/packages/datadog-instrumentations/src/vitest-main.js +9 -17
- package/packages/datadog-instrumentations/src/vitest-util.js +2 -2
- package/packages/datadog-instrumentations/src/vitest-worker.js +1 -2
- package/packages/datadog-instrumentations/src/vitest.js +2 -2
- package/packages/datadog-instrumentations/src/webdriverio.js +118 -21
- package/packages/datadog-instrumentations/src/ws.js +1 -4
- package/packages/datadog-plugin-amqplib/src/client.js +1 -2
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -5
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +1 -2
- package/packages/datadog-plugin-aws-sdk/src/base.js +5 -8
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/index.js +18 -0
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/sonic.js +45 -0
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +9 -3
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +2 -5
- package/packages/datadog-plugin-azure-durable-functions/src/index.js +5 -4
- package/packages/datadog-plugin-azure-functions/src/index.js +5 -3
- package/packages/datadog-plugin-claude-agent-sdk/src/index.js +0 -2
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +18 -5
- package/packages/datadog-plugin-cypress/src/index.js +6 -6
- package/packages/datadog-plugin-cypress/src/source-map-utils.js +1 -2
- package/packages/datadog-plugin-cypress/src/support.js +1 -4
- package/packages/datadog-plugin-express/src/code_origin.js +1 -2
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -2
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +1 -2
- package/packages/datadog-plugin-graphql/src/execute.js +5 -5
- package/packages/datadog-plugin-grpc/src/util.js +1 -2
- package/packages/datadog-plugin-kafkajs/src/producer.js +1 -2
- package/packages/datadog-plugin-mocha/src/index.js +223 -39
- package/packages/datadog-plugin-mocha/src/webdriverio-video.js +453 -0
- package/packages/datadog-plugin-mongodb-core/src/query.js +8 -5
- package/packages/datadog-plugin-next/src/index.js +1 -2
- package/packages/datadog-plugin-openai/src/tracing.js +5 -2
- package/packages/datadog-plugin-openai-agents/src/integration.js +6 -2
- package/packages/datadog-plugin-openai-agents/src/processor.js +2 -4
- package/packages/datadog-plugin-postgres/src/index.js +24 -0
- package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -5
- package/packages/datadog-plugin-rhea/src/consumer.js +1 -4
- package/packages/datadog-plugin-vitest/src/index.js +5 -2
- package/packages/datadog-shimmer/src/shimmer.js +5 -4
- package/packages/datadog-webpack/index.js +55 -43
- package/packages/datadog-webpack/src/loader.js +56 -8
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -4
- package/packages/dd-trace/src/aiguard/messages/utils.js +1 -2
- package/packages/dd-trace/src/aiguard/redaction.js +8 -2
- package/packages/dd-trace/src/appsec/api_security/normalized-route.js +3 -2
- package/packages/dd-trace/src/appsec/api_security/sampler.js +2 -6
- package/packages/dd-trace/src/appsec/downstream_requests.js +1 -9
- package/packages/dd-trace/src/appsec/handlers/http-request.js +3 -9
- package/packages/dd-trace/src/appsec/handlers/http-response.js +7 -2
- package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +4 -3
- package/packages/dd-trace/src/appsec/iast/analyzers/set-cookies-header-interceptor.js +1 -4
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +7 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +1 -3
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-esm.mjs +1 -3
- package/packages/dd-trace/src/appsec/iast/telemetry/iast-metric.js +1 -6
- package/packages/dd-trace/src/appsec/lambda.js +1 -3
- package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +37 -27
- package/packages/dd-trace/src/appsec/rasp/lfi.js +9 -5
- package/packages/dd-trace/src/appsec/reporter.js +21 -1
- package/packages/dd-trace/src/appsec/stack_trace.js +1 -2
- package/packages/dd-trace/src/ci-visibility/dynamic-atr-retries.js +3 -6
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +13 -7
- package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +1 -2
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +2 -8
- package/packages/dd-trace/src/ci-visibility/resolve-runfile-path.js +54 -0
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +5 -8
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +2 -53
- package/packages/dd-trace/src/config/config-types.d.ts +2 -2
- package/packages/dd-trace/src/config/defaults.js +5 -5
- package/packages/dd-trace/src/config/generated-config-types.d.ts +8 -0
- package/packages/dd-trace/src/config/helper.js +34 -3
- package/packages/dd-trace/src/config/index.js +10 -9
- package/packages/dd-trace/src/config/normalize-service.js +1 -1
- package/packages/dd-trace/src/config/parsers.js +2 -2
- package/packages/dd-trace/src/config/supported-configurations.json +35 -0
- package/packages/dd-trace/src/datastreams/processor.js +3 -2
- package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +1 -4
- package/packages/dd-trace/src/debugger/constants.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/index.js +5 -3
- package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +17 -12
- package/packages/dd-trace/src/debugger/devtools_client/send.js +7 -2
- package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/worker-error.js +21 -0
- package/packages/dd-trace/src/debugger/guardrail-metrics.js +2 -2
- package/packages/dd-trace/src/debugger/index.js +24 -2
- package/packages/dd-trace/src/debugger/probe_sampler.js +108 -11
- package/packages/dd-trace/src/dogstatsd.js +1 -1
- package/packages/dd-trace/src/encode/tags-processors.js +2 -2
- package/packages/dd-trace/src/exporters/common/request.js +2 -5
- package/packages/dd-trace/src/exporters/common/url.js +1 -1
- package/packages/dd-trace/src/id.js +15 -5
- package/packages/dd-trace/src/lambda/handler.js +179 -19
- package/packages/dd-trace/src/lambda/index.js +15 -1
- package/packages/dd-trace/src/lambda/runtime/patch.js +34 -7
- package/packages/dd-trace/src/llmobs/agent-manifest.js +296 -0
- package/packages/dd-trace/src/llmobs/audio-utils.js +3 -2
- package/packages/dd-trace/src/llmobs/constants/tags.js +13 -0
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +15 -0
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +1 -2
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chat_model.js +1 -3
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/nova-sonic.js +92 -0
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +56 -2
- package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +38 -0
- package/packages/dd-trace/src/llmobs/prompts/manager.js +5 -7
- package/packages/dd-trace/src/llmobs/prompts/prompt.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +23 -6
- package/packages/dd-trace/src/llmobs/span_processor.js +18 -8
- package/packages/dd-trace/src/llmobs/tagger.js +56 -3
- package/packages/dd-trace/src/llmobs/util.js +1 -2
- package/packages/dd-trace/src/llmobs/writers/base.js +7 -6
- package/packages/dd-trace/src/llmobs/writers/util.js +3 -1
- package/packages/dd-trace/src/log/log.js +1 -5
- package/packages/dd-trace/src/openfeature/agentless_configuration_source.js +30 -1
- package/packages/dd-trace/src/openfeature/configuration_source.js +17 -2
- package/packages/dd-trace/src/openfeature/debug-logging-hook.js +25 -0
- package/packages/dd-trace/src/openfeature/evaluation-telemetry-opt-out-hook.js +51 -0
- package/packages/dd-trace/src/openfeature/flagging_provider.js +59 -3
- package/packages/dd-trace/src/openfeature/remote_config.js +9 -0
- package/packages/dd-trace/src/openfeature/writers/util.js +43 -4
- package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -2
- package/packages/dd-trace/src/opentelemetry/otlp/common.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/resource-attributes.js +1 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +6 -23
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +53 -13
- package/packages/dd-trace/src/otel-sampling.js +4 -5
- package/packages/dd-trace/src/otel-thread-ctx.js +81 -15
- package/packages/dd-trace/src/plugins/database.js +7 -16
- package/packages/dd-trace/src/plugins/util/git.js +9 -25
- package/packages/dd-trace/src/plugins/util/inferred_proxy.js +1 -5
- package/packages/dd-trace/src/plugins/util/test.js +48 -4
- package/packages/dd-trace/src/priority_sampler.js +1 -2
- package/packages/dd-trace/src/profiling/profiler.js +4 -6
- package/packages/dd-trace/src/profiling/profilers/wall.js +4 -10
- package/packages/dd-trace/src/proxy.js +3 -2
- package/packages/dd-trace/src/ritm.js +4 -2
- package/packages/dd-trace/src/serverless.js +10 -0
- package/packages/dd-trace/src/span_processor.js +13 -17
- package/packages/dd-trace/src/storage-channels.js +112 -46
- package/packages/dd-trace/src/telemetry/endpoints.js +1 -3
- package/packages/dd-trace/src/util.js +29 -0
- package/packages/dd-trace/src/web-tags-cache.js +6 -2
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
- package/vendor/dist/@datadog/openfeature-node-server/rules-based/LICENSE +201 -0
- package/vendor/dist/@datadog/openfeature-node-server/rules-based/index.d.ts +925 -0
- package/vendor/dist/@datadog/openfeature-node-server/rules-based/index.js +1 -0
- 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/vendor/dist/shell-quote/index.js +1 -1
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const { types: { isProxy } } = require('node:util')
|
|
4
|
+
|
|
4
5
|
const { channel } = require('../../../datadog-instrumentations/src/helpers/instrument')
|
|
5
6
|
const { ERROR_MESSAGE, ERROR_TYPE } = require('../constants')
|
|
7
|
+
const log = require('../log')
|
|
6
8
|
const { ImpendingTimeout } = require('./runtime/errors')
|
|
7
9
|
const { extractContext } = require('./context')
|
|
8
10
|
|
|
11
|
+
const HANDLER_STREAMING = Symbol.for('aws.lambda.runtime.handler.streaming')
|
|
12
|
+
const HANDLER_HIGHWATERMARK = Symbol.for('aws.lambda.runtime.handler.streaming.highWaterMark')
|
|
13
|
+
const BEFORE_EXIT = Symbol.for('aws.lambda.beforeExit')
|
|
9
14
|
const timeoutChannel = channel('apm:aws:lambda:timeout')
|
|
15
|
+
const monitoredContexts = new WeakSet()
|
|
10
16
|
// Always crash the flushes when a message is received
|
|
11
17
|
// from this channel.
|
|
12
18
|
timeoutChannel.subscribe(_ => {
|
|
13
19
|
crashFlush()
|
|
14
20
|
})
|
|
15
21
|
|
|
16
|
-
let __lambdaTimeout
|
|
17
|
-
|
|
18
22
|
/**
|
|
19
23
|
* Publishes to the `apm:aws:lambda:timeout` channel when
|
|
20
24
|
* the AWS Lambda run time is about to end.
|
|
@@ -23,11 +27,15 @@ let __lambdaTimeout
|
|
|
23
27
|
*/
|
|
24
28
|
function checkTimeout (context) {
|
|
25
29
|
const remainingTimeInMillis = context.getRemainingTimeInMillis()
|
|
26
|
-
const apmFlushDeadline = global._ddtrace
|
|
30
|
+
const apmFlushDeadline = global._ddtrace?._tracer?._config?.DD_APM_FLUSH_DEADLINE_MILLISECONDS
|
|
31
|
+
if (!Number.isFinite(remainingTimeInMillis) || !Number.isFinite(apmFlushDeadline)) return
|
|
27
32
|
|
|
28
|
-
|
|
33
|
+
const timeout = setTimeout(() => {
|
|
29
34
|
timeoutChannel.publish()
|
|
30
|
-
}, remainingTimeInMillis - apmFlushDeadline)
|
|
35
|
+
}, Math.max(0, remainingTimeInMillis - apmFlushDeadline))
|
|
36
|
+
// eslint-disable-next-line eslint-rules/eslint-timer-unref -- AWS Lambda uses Node.js timers.
|
|
37
|
+
timeout.unref()
|
|
38
|
+
return timeout
|
|
31
39
|
}
|
|
32
40
|
|
|
33
41
|
/**
|
|
@@ -58,26 +66,178 @@ function crashFlush () {
|
|
|
58
66
|
}
|
|
59
67
|
|
|
60
68
|
/**
|
|
61
|
-
*
|
|
69
|
+
* Monitors an AWS Lambda handler for impending timeouts without creating a span.
|
|
62
70
|
*
|
|
63
71
|
* @param {Function} lambdaHandler a Lambda handler function.
|
|
72
|
+
* @param {boolean} normalized whether datadog-lambda-js owns completion selection.
|
|
64
73
|
*/
|
|
65
|
-
exports.datadog = function datadog (lambdaHandler) {
|
|
66
|
-
|
|
74
|
+
exports.datadog = function datadog (lambdaHandler, normalized = false) {
|
|
75
|
+
function monitored (...args) {
|
|
67
76
|
const context = extractContext(args)
|
|
77
|
+
if (!context || monitoredContexts.has(context)) return Reflect.apply(lambdaHandler, this, args)
|
|
68
78
|
|
|
69
|
-
|
|
70
|
-
|
|
79
|
+
const timeout = checkTimeout(context)
|
|
80
|
+
if (timeout === undefined) return Reflect.apply(lambdaHandler, this, args)
|
|
81
|
+
|
|
82
|
+
let completed = false
|
|
83
|
+
let callbackPending = false
|
|
84
|
+
/** @type {Array<[object, string | symbol, Function, Function]>} */
|
|
85
|
+
const completionMethods = []
|
|
86
|
+
const clear = () => {
|
|
87
|
+
if (completed) return
|
|
88
|
+
completed = true
|
|
89
|
+
clearTimeout(timeout)
|
|
90
|
+
for (const [target, name, original, wrapped] of completionMethods) {
|
|
91
|
+
try {
|
|
92
|
+
if (target[name] === wrapped) Reflect.set(target, name, original)
|
|
93
|
+
} catch (error) {
|
|
94
|
+
log.error('Error restoring Lambda completion %s: %s', name, error)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const clearOnHandlerCompletion = normalized
|
|
99
|
+
? clear
|
|
100
|
+
: () => {
|
|
101
|
+
// Once a native callback selects event-loop drain, later handler signals are ignored by RIC.
|
|
102
|
+
if (!callbackPending) clear()
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const contextIndex = args.indexOf(context)
|
|
106
|
+
const callbackObservable = contextIndex !== 2 && typeof args[2] === 'function'
|
|
107
|
+
if (!normalized) {
|
|
108
|
+
for (const name of ['done', 'succeed', 'fail']) {
|
|
109
|
+
wrapCompletion(context, name, clearOnHandlerCompletion, completionMethods)
|
|
110
|
+
}
|
|
71
111
|
}
|
|
112
|
+
// Legacy Lambda runtimes complete idle invocations through this callback instead of the handler's callback.
|
|
113
|
+
wrapCompletion(globalThis, BEFORE_EXIT, clear, completionMethods)
|
|
72
114
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
115
|
+
if (callbackObservable) {
|
|
116
|
+
const callback = args[2]
|
|
117
|
+
const beforeExitObservable = completionMethods.at(-1)?.[1] === BEFORE_EXIT
|
|
118
|
+
let callbackInvoked = false
|
|
119
|
+
args[2] = function (...callbackArgs) {
|
|
120
|
+
if (completed || callbackInvoked) return Reflect.apply(callback, this, callbackArgs)
|
|
121
|
+
callbackInvoked = true
|
|
122
|
+
let beforeExit
|
|
123
|
+
try {
|
|
124
|
+
if (beforeExitObservable && callbackArgs[0] == null && context.callbackWaitsForEmptyEventLoop) {
|
|
125
|
+
beforeExit = getDataProperty(globalThis, BEFORE_EXIT)
|
|
126
|
+
}
|
|
127
|
+
} catch (error) {
|
|
128
|
+
log.error('Error reading Lambda callback completion: %s', error)
|
|
129
|
+
}
|
|
130
|
+
if (typeof beforeExit !== 'function') clear()
|
|
131
|
+
try {
|
|
132
|
+
const result = Reflect.apply(callback, this, callbackArgs)
|
|
133
|
+
if (!completed) {
|
|
134
|
+
// A successful legacy callback installs a new listener while waiting for event-loop drain.
|
|
135
|
+
const replacement = getDataProperty(globalThis, BEFORE_EXIT)
|
|
136
|
+
if (typeof replacement !== 'function' || replacement === beforeExit ||
|
|
137
|
+
!wrapCompletion(globalThis, BEFORE_EXIT, clear, completionMethods)) clear()
|
|
138
|
+
else callbackPending = true
|
|
139
|
+
}
|
|
140
|
+
return result
|
|
141
|
+
} catch (error) {
|
|
142
|
+
clear()
|
|
143
|
+
throw error
|
|
144
|
+
}
|
|
145
|
+
}
|
|
79
146
|
}
|
|
80
|
-
|
|
81
|
-
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
// The outer completion adapter owns this context during synchronous dispatch to nested wrappers.
|
|
150
|
+
monitoredContexts.add(context)
|
|
151
|
+
const result = Reflect.apply(lambdaHandler, this, args)
|
|
152
|
+
if (result && typeof result.then === 'function') {
|
|
153
|
+
return result.then(value => {
|
|
154
|
+
clearOnHandlerCompletion()
|
|
155
|
+
return value
|
|
156
|
+
}, error => {
|
|
157
|
+
clearOnHandlerCompletion()
|
|
158
|
+
throw error
|
|
159
|
+
})
|
|
160
|
+
}
|
|
161
|
+
// Legacy RIC supplies a callback; newer runtimes complete from the return value.
|
|
162
|
+
if (!callbackObservable) clear()
|
|
163
|
+
return result
|
|
164
|
+
} catch (error) {
|
|
165
|
+
clearOnHandlerCompletion()
|
|
166
|
+
throw error
|
|
167
|
+
} finally {
|
|
168
|
+
monitoredContexts.delete(context)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (normalized) return monitored
|
|
173
|
+
|
|
174
|
+
// datadog-lambda-js uses arity and the runtime's streaming metadata to choose how to invoke the handler.
|
|
175
|
+
Object.defineProperty(monitored, 'length', { value: getDataProperty(lambdaHandler, 'length') ?? 0 })
|
|
176
|
+
for (const name of [HANDLER_STREAMING, HANDLER_HIGHWATERMARK, '_ddWrapped']) {
|
|
177
|
+
const value = getDataProperty(lambdaHandler, name)
|
|
178
|
+
if (value !== undefined) monitored[name] = value
|
|
179
|
+
}
|
|
180
|
+
if (!isProxy(lambdaHandler)) {
|
|
181
|
+
for (const name of Reflect.ownKeys(lambdaHandler)) {
|
|
182
|
+
if (name === 'arguments' || name === 'caller') continue
|
|
183
|
+
const descriptor = Object.getOwnPropertyDescriptor(lambdaHandler, name)
|
|
184
|
+
if (!Object.hasOwn(descriptor, 'value')) continue
|
|
185
|
+
const existing = Object.getOwnPropertyDescriptor(monitored, name)
|
|
186
|
+
if (existing?.configurable === false) {
|
|
187
|
+
// Intrinsic function slots such as prototype cannot change their descriptor shape.
|
|
188
|
+
if (existing.writable) {
|
|
189
|
+
Object.defineProperty(monitored, name, { value: descriptor.value, writable: descriptor.writable })
|
|
190
|
+
}
|
|
191
|
+
} else {
|
|
192
|
+
Object.defineProperty(monitored, name, descriptor)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return monitored
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* @param {object} target
|
|
201
|
+
* @param {string | symbol} name
|
|
202
|
+
* @param {() => void} clear
|
|
203
|
+
* @param {Array<[object, string | symbol, Function, Function]>} completionMethods
|
|
204
|
+
*/
|
|
205
|
+
function wrapCompletion (target, name, clear, completionMethods) {
|
|
206
|
+
try {
|
|
207
|
+
const original = target[name]
|
|
208
|
+
if (typeof original !== 'function') return true
|
|
209
|
+
const wrapped = onCompletion(original, clear)
|
|
210
|
+
if (!Reflect.set(target, name, wrapped) || target[name] !== wrapped) return false
|
|
211
|
+
completionMethods.push([target, name, original, wrapped])
|
|
212
|
+
return true
|
|
213
|
+
} catch (error) {
|
|
214
|
+
log.error('Error wrapping Lambda completion %s: %s', name, error)
|
|
215
|
+
return false
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* @param {Function} callback
|
|
221
|
+
* @param {() => void} clear
|
|
222
|
+
*/
|
|
223
|
+
function onCompletion (callback, clear) {
|
|
224
|
+
return function (...args) {
|
|
225
|
+
clear()
|
|
226
|
+
return Reflect.apply(callback, this, args)
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Reads metadata without executing application getters or Proxy traps.
|
|
232
|
+
*
|
|
233
|
+
* @param {unknown} target
|
|
234
|
+
* @param {string | symbol} name
|
|
235
|
+
*/
|
|
236
|
+
function getDataProperty (target, name) {
|
|
237
|
+
if (target === null || (typeof target !== 'object' && typeof target !== 'function')) return
|
|
238
|
+
while (target !== null && !isProxy(target)) {
|
|
239
|
+
const descriptor = Object.getOwnPropertyDescriptor(target, name)
|
|
240
|
+
if (descriptor) return descriptor.value
|
|
241
|
+
target = Object.getPrototypeOf(target)
|
|
82
242
|
}
|
|
83
243
|
}
|
|
@@ -41,7 +41,6 @@ if (!getValueFromEnvSources('DD_TRACE_DISABLED_INSTRUMENTATIONS')?.split(',').in
|
|
|
41
41
|
|
|
42
42
|
return moduleExports
|
|
43
43
|
})
|
|
44
|
-
return
|
|
45
44
|
}
|
|
46
45
|
|
|
47
46
|
const moduleToPatch = 'datadog-lambda-js'
|
|
@@ -62,4 +61,19 @@ if (!getValueFromEnvSources('DD_TRACE_DISABLED_INSTRUMENTATIONS')?.split(',').in
|
|
|
62
61
|
|
|
63
62
|
return moduleExports
|
|
64
63
|
})
|
|
64
|
+
|
|
65
|
+
// The published datadog-lambda-js ESM entrypoint imports this helper before initializing the tracer.
|
|
66
|
+
// Revisit cached copies through the registered hook without loading datadog-lambda-js early.
|
|
67
|
+
const helperSuffixes = [
|
|
68
|
+
path.join('datadog-lambda-js', 'dist', 'utils', 'handler.js'),
|
|
69
|
+
path.join('datadog-lambda-js', 'utils', 'handler.js'),
|
|
70
|
+
]
|
|
71
|
+
for (const filename of Object.keys(require.cache)) {
|
|
72
|
+
if (!helperSuffixes.some(suffix => filename.endsWith(path.sep + suffix))) continue
|
|
73
|
+
try {
|
|
74
|
+
require(filename)
|
|
75
|
+
} catch (error) {
|
|
76
|
+
log.error('Error executing cached lambda hook for datadog-lambda-js: %s', error)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
65
79
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const path = require('path')
|
|
4
|
+
const { types: { isModuleNamespaceObject } } = require('node:util')
|
|
4
5
|
|
|
5
6
|
const { datadog } = require('../handler')
|
|
6
7
|
const { addHook } = require('../../../../datadog-instrumentations/src/helpers/instrument')
|
|
@@ -23,22 +24,48 @@ function patchDatadogLambdaHandler (datadogHandler) {
|
|
|
23
24
|
return userHandler => datadogHandler(datadog(userHandler))
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Wraps datadog-lambda-js's promisifiedHandler factory, which wraps the customer's handler.
|
|
29
|
+
* The timeout monitor surrounds that adapter to observe the completion Promise selected by datadog-lambda-js.
|
|
30
|
+
* Legacy RIC completes returned Promises through context.succeed without waiting for event-loop drain.
|
|
31
|
+
*
|
|
32
|
+
* @param {object} moduleExports
|
|
33
|
+
*/
|
|
34
|
+
function patchPromisifiedHandlerModule (moduleExports) {
|
|
35
|
+
shimmer.wrap(moduleExports, 'promisifiedHandler', promisify => {
|
|
36
|
+
return handler => datadog(promisify(handler), true)
|
|
37
|
+
})
|
|
38
|
+
return moduleExports
|
|
39
|
+
}
|
|
40
|
+
|
|
26
41
|
/** @param {string} handlerPath */
|
|
27
42
|
function patchLambdaModule (handlerPath) {
|
|
28
43
|
return lambdaModule => {
|
|
29
|
-
|
|
30
|
-
|
|
44
|
+
const descriptor = Object.getOwnPropertyDescriptor(lambdaModule, handlerPath)
|
|
45
|
+
if (typeof descriptor?.value === 'function') {
|
|
46
|
+
if (!descriptor.configurable && !descriptor.writable) return lambdaModule
|
|
47
|
+
// The monitor copies Lambda metadata without invoking application accessors or Proxy traps.
|
|
48
|
+
// Generic shimmer wrapping would copy the original properties back onto that safe wrapper.
|
|
49
|
+
descriptor.value = datadog(descriptor.value)
|
|
50
|
+
// Native ESM namespaces reject replacement even when their export reports writable: true.
|
|
51
|
+
const exports = isModuleNamespaceObject(lambdaModule)
|
|
52
|
+
? Object.create(lambdaModule, Object.getOwnPropertyDescriptors(lambdaModule))
|
|
53
|
+
: lambdaModule
|
|
54
|
+
Object.defineProperty(exports, handlerPath, descriptor)
|
|
55
|
+
return exports
|
|
56
|
+
}
|
|
57
|
+
return shimmer.wrap(lambdaModule, handlerPath, datadog)
|
|
31
58
|
}
|
|
32
59
|
}
|
|
33
60
|
|
|
34
|
-
/** @param {Function} lambdaHandler */
|
|
35
|
-
function patchLambdaHandler (lambdaHandler) {
|
|
36
|
-
return datadog(lambdaHandler)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
61
|
const lambdaTaskRoot = getEnvironmentVariable('LAMBDA_TASK_ROOT')
|
|
40
62
|
const originalLambdaHandler = getValueFromEnvSources('DD_LAMBDA_HANDLER')
|
|
41
63
|
|
|
64
|
+
// Observe the completion Promise selected by datadog-lambda-js inside the invocation span's async scope.
|
|
65
|
+
// The npm package keeps dist/; Lambda layer builds place its contents directly in the package root.
|
|
66
|
+
addHook({ name: 'datadog-lambda-js', file: 'dist/utils/handler.js' }, patchPromisifiedHandlerModule)
|
|
67
|
+
addHook({ name: 'datadog-lambda-js', file: 'utils/handler.js' }, patchPromisifiedHandlerModule)
|
|
68
|
+
|
|
42
69
|
if (originalLambdaHandler === undefined) {
|
|
43
70
|
addHook({ name: 'datadog-lambda-js' }, patchDatadogLambdaModule)
|
|
44
71
|
} else {
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const log = require('../log')
|
|
4
|
+
|
|
5
|
+
// Set by the SDK so a consumer can tell a hand-declared manifest from one read off a framework object.
|
|
6
|
+
const MANUAL_FRAMEWORK_NAME = 'manual'
|
|
7
|
+
|
|
8
|
+
// Provider-specific keys such as `extra_headers` can carry secrets, so only these are reported.
|
|
9
|
+
const ALLOWED_MODEL_SETTINGS_KEYS = new Set([
|
|
10
|
+
'frequency_penalty',
|
|
11
|
+
'logit_bias',
|
|
12
|
+
'logprobs',
|
|
13
|
+
'max_tokens',
|
|
14
|
+
'parallel_tool_calls',
|
|
15
|
+
'presence_penalty',
|
|
16
|
+
'seed',
|
|
17
|
+
'stop_sequences',
|
|
18
|
+
'temperature',
|
|
19
|
+
'timeout',
|
|
20
|
+
'tool_choice',
|
|
21
|
+
'top_k',
|
|
22
|
+
'top_logprobs',
|
|
23
|
+
'top_p',
|
|
24
|
+
])
|
|
25
|
+
|
|
26
|
+
const LABEL_FIELDS = ['name', 'instructions', 'model']
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @typedef {{ type?: string, required?: true }} AgentToolParameter
|
|
30
|
+
* @typedef {{ name: string, description?: string, parameters?: Record<string, AgentToolParameter> }} AgentTool
|
|
31
|
+
* @typedef {{
|
|
32
|
+
* name?: string,
|
|
33
|
+
* instructions?: string,
|
|
34
|
+
* model?: string,
|
|
35
|
+
* model_settings?: Record<string, unknown>,
|
|
36
|
+
* tools?: AgentTool[],
|
|
37
|
+
* }} AgentManifestFields
|
|
38
|
+
* @typedef {{ manifest: AgentManifestFields }} AgentDeclaration
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Reads the agent a caller declared through `annotate({ agent })` or `annotationContext({ agent })` into an
|
|
43
|
+
* immutable snapshot. Never throws, since the caller's object may carry throwing getters or be a revoked Proxy,
|
|
44
|
+
* and this runs while an LLMObs span is being registered.
|
|
45
|
+
*
|
|
46
|
+
* @param {unknown} agent
|
|
47
|
+
* @returns {AgentDeclaration | undefined} undefined when the agent declares nothing reportable
|
|
48
|
+
*/
|
|
49
|
+
function buildAgentDeclaration (agent) {
|
|
50
|
+
let declaration
|
|
51
|
+
try {
|
|
52
|
+
if (isPlainObject(agent)) {
|
|
53
|
+
const manifest = buildAgentManifest(agent)
|
|
54
|
+
if (manifest !== undefined) declaration = { manifest }
|
|
55
|
+
} else {
|
|
56
|
+
log.warn('Dropping agent annotation, the agent must be a plain object.')
|
|
57
|
+
}
|
|
58
|
+
} catch {
|
|
59
|
+
log.warn('Dropping agent annotation, its fields could not be read.')
|
|
60
|
+
}
|
|
61
|
+
return declaration
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Validates each manifest field on its own, so one bad field cannot blank the rest. Unset values (`undefined`,
|
|
66
|
+
* `null`, `''`, `[]`, `{}`) are omitted so they declare nothing rather than erase an earlier declaration. Fields
|
|
67
|
+
* that are set but unreportable are dropped and logged by name, never by value.
|
|
68
|
+
*
|
|
69
|
+
* @param {Record<string, unknown>} agent
|
|
70
|
+
* @returns {AgentManifestFields | undefined}
|
|
71
|
+
*/
|
|
72
|
+
function buildAgentManifest (agent) {
|
|
73
|
+
/** @type {string[]} */
|
|
74
|
+
const dropped = []
|
|
75
|
+
/** @type {AgentManifestFields | undefined} */
|
|
76
|
+
let manifest
|
|
77
|
+
|
|
78
|
+
for (const key of LABEL_FIELDS) {
|
|
79
|
+
const value = agent[key]
|
|
80
|
+
if (isUnset(value)) continue
|
|
81
|
+
if (typeof value === 'string') {
|
|
82
|
+
manifest ??= {}
|
|
83
|
+
manifest[key] = value
|
|
84
|
+
} else {
|
|
85
|
+
dropped.push(key)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const modelSettings = buildModelSettings(agent.modelSettings, dropped)
|
|
90
|
+
if (modelSettings) {
|
|
91
|
+
manifest ??= {}
|
|
92
|
+
manifest.model_settings = modelSettings
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const tools = buildTools(agent.tools, dropped)
|
|
96
|
+
if (tools) {
|
|
97
|
+
manifest ??= {}
|
|
98
|
+
manifest.tools = tools
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (dropped.length > 0) {
|
|
102
|
+
log.warn('Dropping unsupported agent manifest fields: %s', dropped.join(', '))
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return manifest
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @param {unknown} settings
|
|
110
|
+
* @param {string[]} dropped
|
|
111
|
+
* @returns {Record<string, unknown> | undefined}
|
|
112
|
+
*/
|
|
113
|
+
function buildModelSettings (settings, dropped) {
|
|
114
|
+
if (isUnset(settings)) return
|
|
115
|
+
if (!isPlainObject(settings)) {
|
|
116
|
+
dropped.push('modelSettings')
|
|
117
|
+
return
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
let allowed
|
|
121
|
+
for (const [key, value] of Object.entries(settings)) {
|
|
122
|
+
const wireKey = toSnakeCase(key)
|
|
123
|
+
if (!ALLOWED_MODEL_SETTINGS_KEYS.has(wireKey)) {
|
|
124
|
+
dropped.push(`modelSettings.${key}`)
|
|
125
|
+
continue
|
|
126
|
+
}
|
|
127
|
+
if (isUnset(value)) continue
|
|
128
|
+
const wireValue = toFlatScalarValue(value)
|
|
129
|
+
if (wireValue === null) continue
|
|
130
|
+
if (wireValue === undefined) {
|
|
131
|
+
dropped.push(`modelSettings.${key}`)
|
|
132
|
+
continue
|
|
133
|
+
}
|
|
134
|
+
allowed ??= {}
|
|
135
|
+
allowed[wireKey] = wireValue
|
|
136
|
+
}
|
|
137
|
+
return allowed
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* A JSON scalar, a flat array of scalars, or a flat object of numbers (`logit_bias`). No allowed setting nests, so
|
|
142
|
+
* anything deeper is dropped rather than coerced. Returns undefined for a value that cannot be reported, and null
|
|
143
|
+
* for an empty object, which declares nothing.
|
|
144
|
+
*
|
|
145
|
+
* @param {unknown} value
|
|
146
|
+
*/
|
|
147
|
+
function toFlatScalarValue (value) {
|
|
148
|
+
if (isScalar(value)) return value
|
|
149
|
+
if (Array.isArray(value)) {
|
|
150
|
+
const items = [...value]
|
|
151
|
+
for (const item of items) {
|
|
152
|
+
if (!isScalar(item)) return
|
|
153
|
+
}
|
|
154
|
+
return items
|
|
155
|
+
}
|
|
156
|
+
if (!isPlainObject(value)) return
|
|
157
|
+
|
|
158
|
+
const entries = Object.entries(value)
|
|
159
|
+
if (entries.length === 0) return null
|
|
160
|
+
for (const [, item] of entries) {
|
|
161
|
+
if (!isFiniteNumber(item)) return
|
|
162
|
+
}
|
|
163
|
+
// Built from entries so a key such as `__proto__` becomes an own property rather than setting the prototype.
|
|
164
|
+
return Object.fromEntries(entries)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* @param {unknown} declared
|
|
169
|
+
* @param {string[]} dropped
|
|
170
|
+
* @returns {AgentTool[] | undefined}
|
|
171
|
+
*/
|
|
172
|
+
function buildTools (declared, dropped) {
|
|
173
|
+
if (isUnset(declared)) return
|
|
174
|
+
if (!Array.isArray(declared)) {
|
|
175
|
+
dropped.push('tools')
|
|
176
|
+
return
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const tools = []
|
|
180
|
+
for (let index = 0; index < declared.length; index++) {
|
|
181
|
+
const tool = declared[index]
|
|
182
|
+
const name = isPlainObject(tool) ? tool.name : undefined
|
|
183
|
+
// An unnamed tool is unidentifiable once it ships.
|
|
184
|
+
if (typeof name !== 'string' || name === '') {
|
|
185
|
+
dropped.push(`tools[${index}]`)
|
|
186
|
+
continue
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** @type {AgentTool} */
|
|
190
|
+
const built = { name }
|
|
191
|
+
const description = tool.description
|
|
192
|
+
if (typeof description === 'string' && description !== '') built.description = description
|
|
193
|
+
const parameters = buildToolParameters(tool.parameters)
|
|
194
|
+
if (parameters === null) {
|
|
195
|
+
dropped.push(`tools[${index}].parameters`)
|
|
196
|
+
} else if (parameters) {
|
|
197
|
+
built.parameters = parameters
|
|
198
|
+
}
|
|
199
|
+
tools.push(built)
|
|
200
|
+
}
|
|
201
|
+
return tools.length > 0 ? tools : undefined
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Flattens tool parameters to `{ param: { type?, required? } }`, the shape the Python framework integrations emit.
|
|
206
|
+
* `required` is omitted rather than reported false. Returns null for parameters that are set but not a plain
|
|
207
|
+
* object, such as a schema-library instance, so the caller can report them as dropped.
|
|
208
|
+
*
|
|
209
|
+
* @param {unknown} parameters
|
|
210
|
+
* @returns {Record<string, AgentToolParameter> | null | undefined}
|
|
211
|
+
*/
|
|
212
|
+
function buildToolParameters (parameters) {
|
|
213
|
+
if (isUnset(parameters)) return
|
|
214
|
+
if (!isPlainObject(parameters)) return null
|
|
215
|
+
|
|
216
|
+
const { specs, required } = toolParameterSpecs(parameters)
|
|
217
|
+
const flattened = []
|
|
218
|
+
for (const [param, spec] of Object.entries(specs)) {
|
|
219
|
+
/** @type {AgentToolParameter} */
|
|
220
|
+
const entry = {}
|
|
221
|
+
if (isPlainObject(spec)) {
|
|
222
|
+
const { type } = spec
|
|
223
|
+
if (typeof type === 'string' && type !== '') entry.type = type
|
|
224
|
+
if (spec.required === true) entry.required = true
|
|
225
|
+
}
|
|
226
|
+
if (required?.has(param)) entry.required = true
|
|
227
|
+
if (entry.type === undefined && entry.required === undefined) continue
|
|
228
|
+
flattened.push([param, entry])
|
|
229
|
+
}
|
|
230
|
+
// Built from entries so a parameter named `__proto__` becomes an own property rather than setting the prototype.
|
|
231
|
+
return flattened.length > 0 ? Object.fromEntries(flattened) : undefined
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Accepts a JSON Schema object as well as the documented `{ param: { type, required } }` mapping, since a JSON
|
|
236
|
+
* Schema is what OpenAI, Gemini and MCP tool definitions carry. Two markers are required rather than `properties`
|
|
237
|
+
* alone, so a mapping that declares a parameter named `properties` is still read as a mapping.
|
|
238
|
+
*
|
|
239
|
+
* @param {Record<string, unknown>} parameters
|
|
240
|
+
* @returns {{ specs: Record<string, unknown>, required?: Set<unknown> }}
|
|
241
|
+
*/
|
|
242
|
+
function toolParameterSpecs (parameters) {
|
|
243
|
+
const { properties, required } = parameters
|
|
244
|
+
const hasRequiredList = Array.isArray(required)
|
|
245
|
+
if (!isPlainObject(properties) || !(parameters.type === 'object' || hasRequiredList)) {
|
|
246
|
+
return { specs: parameters }
|
|
247
|
+
}
|
|
248
|
+
return { specs: properties, required: hasRequiredList ? new Set(required) : undefined }
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* @param {string} key
|
|
253
|
+
*/
|
|
254
|
+
function toSnakeCase (key) {
|
|
255
|
+
return key.replaceAll(/[A-Z]/g, letter => `_${letter.toLowerCase()}`)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Class instances are rejected so a schema-library object (Zod, the AI SDK `jsonSchema()` wrapper) is dropped rather
|
|
260
|
+
* than read field by field into a misleading manifest.
|
|
261
|
+
*
|
|
262
|
+
* @param {unknown} value
|
|
263
|
+
* @returns {value is Record<string, unknown>}
|
|
264
|
+
*/
|
|
265
|
+
function isPlainObject (value) {
|
|
266
|
+
if (value === null || typeof value !== 'object') return false
|
|
267
|
+
const prototype = Object.getPrototypeOf(value)
|
|
268
|
+
return prototype === Object.prototype || prototype === null
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* @param {unknown} value
|
|
273
|
+
*/
|
|
274
|
+
function isUnset (value) {
|
|
275
|
+
if (value === undefined || value === null || value === '') return true
|
|
276
|
+
return Array.isArray(value) && value.length === 0
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* @param {unknown} value
|
|
281
|
+
*/
|
|
282
|
+
function isFiniteNumber (value) {
|
|
283
|
+
return typeof value === 'number' && Number.isFinite(value)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* @param {unknown} value
|
|
288
|
+
*/
|
|
289
|
+
function isScalar (value) {
|
|
290
|
+
return typeof value === 'string' || typeof value === 'boolean' || isFiniteNumber(value)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
module.exports = {
|
|
294
|
+
MANUAL_FRAMEWORK_NAME,
|
|
295
|
+
buildAgentDeclaration,
|
|
296
|
+
}
|
|
@@ -106,8 +106,9 @@ function fitsInlineAudioBudget (byteLength, maxBytes = LLMOBS_AUDIO_INLINE_MAX_B
|
|
|
106
106
|
* @returns {AudioPart | undefined}
|
|
107
107
|
*/
|
|
108
108
|
function formatAudioPartWithGuard (audioBytes, mimeType, maxBytes = LLMOBS_AUDIO_INLINE_MAX_BYTES) {
|
|
109
|
-
if (!audioBytes?.length || !isRenderableAudioMime(mimeType))
|
|
110
|
-
|
|
109
|
+
if (!audioBytes?.length || !isRenderableAudioMime(mimeType) || !fitsInlineAudioBudget(audioBytes.length, maxBytes)) {
|
|
110
|
+
return
|
|
111
|
+
}
|
|
111
112
|
|
|
112
113
|
return formatAudioPart(audioBytes, mimeType)
|
|
113
114
|
}
|
|
@@ -7,7 +7,11 @@ const CACHE_READ_INPUT_TOKENS_METRIC_KEY = 'cache_read_input_tokens'
|
|
|
7
7
|
const CACHE_WRITE_INPUT_TOKENS_METRIC_KEY = 'cache_write_input_tokens'
|
|
8
8
|
const CACHE_WRITE_5M_INPUT_TOKENS_METRIC_KEY = 'ephemeral_5m_input_tokens'
|
|
9
9
|
const CACHE_WRITE_1H_INPUT_TOKENS_METRIC_KEY = 'ephemeral_1h_input_tokens'
|
|
10
|
+
const CACHE_WRITE_30M_INPUT_TOKENS_METRIC_KEY = 'ephemeral_30m_input_tokens'
|
|
11
|
+
const CACHE_WRITE_12H_INPUT_TOKENS_METRIC_KEY = 'ephemeral_12h_input_tokens'
|
|
10
12
|
const REASONING_OUTPUT_TOKENS_METRIC_KEY = 'reasoning_output_tokens'
|
|
13
|
+
const WEB_SEARCH_COUNT_METRIC_KEY = 'web_search_count'
|
|
14
|
+
const STORAGE_SEARCH_COUNT_METRIC_KEY = 'storage_search_count'
|
|
11
15
|
|
|
12
16
|
module.exports = {
|
|
13
17
|
SPAN_KINDS: ['llm', 'agent', 'workflow', 'task', 'tool', 'embedding', 'retrieval', 'experiment'],
|
|
@@ -18,6 +22,7 @@ module.exports = {
|
|
|
18
22
|
INTEGRATION: '_ml_obs.integration',
|
|
19
23
|
METADATA: '_ml_obs.meta.metadata',
|
|
20
24
|
COST_TAGS: '_ml_obs.meta.metadata._dd.cost_tags',
|
|
25
|
+
AGENT_MANIFEST: '_ml_obs.meta.metadata._dd.agent_manifest',
|
|
21
26
|
METRICS: '_ml_obs.metrics',
|
|
22
27
|
TOOL_DEFINITIONS: '_ml_obs.meta.tool_definitions',
|
|
23
28
|
ML_APP: '_ml_obs.meta.ml_app',
|
|
@@ -71,7 +76,11 @@ module.exports = {
|
|
|
71
76
|
CACHE_WRITE_INPUT_TOKENS_METRIC_KEY,
|
|
72
77
|
CACHE_WRITE_5M_INPUT_TOKENS_METRIC_KEY,
|
|
73
78
|
CACHE_WRITE_1H_INPUT_TOKENS_METRIC_KEY,
|
|
79
|
+
CACHE_WRITE_30M_INPUT_TOKENS_METRIC_KEY,
|
|
80
|
+
CACHE_WRITE_12H_INPUT_TOKENS_METRIC_KEY,
|
|
74
81
|
REASONING_OUTPUT_TOKENS_METRIC_KEY,
|
|
82
|
+
WEB_SEARCH_COUNT_METRIC_KEY,
|
|
83
|
+
STORAGE_SEARCH_COUNT_METRIC_KEY,
|
|
75
84
|
|
|
76
85
|
// integrations build metric objects with these camelCase spellings. Null prototype: a metric
|
|
77
86
|
// named after an `Object.prototype` member must not resolve to an inherited property.
|
|
@@ -83,7 +92,11 @@ module.exports = {
|
|
|
83
92
|
cacheWriteTokens: CACHE_WRITE_INPUT_TOKENS_METRIC_KEY,
|
|
84
93
|
cacheWrite5mTokens: CACHE_WRITE_5M_INPUT_TOKENS_METRIC_KEY,
|
|
85
94
|
cacheWrite1hTokens: CACHE_WRITE_1H_INPUT_TOKENS_METRIC_KEY,
|
|
95
|
+
cacheWrite30mTokens: CACHE_WRITE_30M_INPUT_TOKENS_METRIC_KEY,
|
|
96
|
+
cacheWrite12hTokens: CACHE_WRITE_12H_INPUT_TOKENS_METRIC_KEY,
|
|
86
97
|
reasoningOutputTokens: REASONING_OUTPUT_TOKENS_METRIC_KEY,
|
|
98
|
+
webSearchCount: WEB_SEARCH_COUNT_METRIC_KEY,
|
|
99
|
+
storageSearchCount: STORAGE_SEARCH_COUNT_METRIC_KEY,
|
|
87
100
|
}),
|
|
88
101
|
|
|
89
102
|
DROPPED_IO_COLLECTION_ERROR: 'dropped_io',
|
|
@@ -253,7 +253,7 @@ class Experiment {
|
|
|
253
253
|
this.#external = options.external === true
|
|
254
254
|
if (!this.#external) {
|
|
255
255
|
if (!options.dataset) throw new Error('Experiment dataset is required')
|
|
256
|
-
if (typeof options.task !== 'function') throw new
|
|
256
|
+
if (typeof options.task !== 'function') throw new TypeError('Experiment task is required')
|
|
257
257
|
}
|
|
258
258
|
|
|
259
259
|
this.#client = client
|