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
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { randomUUID } = require('node:crypto')
|
|
4
|
+
|
|
5
|
+
const log = require('../../../dd-trace/src/log')
|
|
6
|
+
const { InputAudio, OutputAudio } = require('./audio')
|
|
7
|
+
|
|
8
|
+
const MAX_TEXT = 65_536
|
|
9
|
+
// Replayed history can exceed 16 messages after only eight exchanges. Allow longer resumed
|
|
10
|
+
// conversations while bounding tiny/empty message overhead independently of the total text budget.
|
|
11
|
+
const MAX_HISTORY_MESSAGES = 128
|
|
12
|
+
const MAX_BLOCKS = 256
|
|
13
|
+
const MAX_TOOLS = 16
|
|
14
|
+
const MAX_TOOL_TEXT = 4096
|
|
15
|
+
|
|
16
|
+
class Turn {
|
|
17
|
+
windows = []
|
|
18
|
+
userText = ''
|
|
19
|
+
finalText = ''
|
|
20
|
+
speculativeText = ''
|
|
21
|
+
tools = []
|
|
22
|
+
toolResults = []
|
|
23
|
+
inputPcm
|
|
24
|
+
inputRate = 0
|
|
25
|
+
inputStart
|
|
26
|
+
inputEnd
|
|
27
|
+
inputAudioObserved = false
|
|
28
|
+
outputAudioObserved = false
|
|
29
|
+
audioStarted = false
|
|
30
|
+
output = new OutputAudio()
|
|
31
|
+
started
|
|
32
|
+
generationEnd
|
|
33
|
+
textEnd
|
|
34
|
+
completionId
|
|
35
|
+
metrics = { input_tokens: 0, output_tokens: 0, total_tokens: 0 }
|
|
36
|
+
missingAudioMetrics = new Set()
|
|
37
|
+
emitted = false
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** @param {Turn} turn */
|
|
41
|
+
function hasInput (turn) {
|
|
42
|
+
return turn.windows.length || turn.userText || turn.toolResults.length
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** @param {Turn} turn */
|
|
46
|
+
function completionTime (turn) {
|
|
47
|
+
// Text can finish before audio starts. Keep its boundary as a fallback, without completing
|
|
48
|
+
// an unfinished or interrupted audio response when its final transcript arrives.
|
|
49
|
+
return turn.generationEnd ?? (!turn.audioStarted && turn.output.interrupted === undefined ? turn.textEnd : undefined)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** @param {unknown} value */
|
|
53
|
+
function object (value) {
|
|
54
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @param {string} text
|
|
59
|
+
*/
|
|
60
|
+
function parse (text) {
|
|
61
|
+
try {
|
|
62
|
+
return JSON.parse(text)
|
|
63
|
+
} catch {
|
|
64
|
+
return {}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Nova's completion ID can cover the entire connection. Local response ownership is driven by
|
|
70
|
+
* pending input and new generation, with FINAL text staying on the preceding response.
|
|
71
|
+
*/
|
|
72
|
+
class SonicSession {
|
|
73
|
+
#emit
|
|
74
|
+
#audio = new InputAudio()
|
|
75
|
+
#pending = new Turn()
|
|
76
|
+
#current
|
|
77
|
+
#outbound = new Map()
|
|
78
|
+
#blocks = new Map()
|
|
79
|
+
#completed = new Set()
|
|
80
|
+
#prompt
|
|
81
|
+
#configuration = {}
|
|
82
|
+
#outputConfiguration = {}
|
|
83
|
+
#history = []
|
|
84
|
+
#closed = false
|
|
85
|
+
#turnIndex = 0
|
|
86
|
+
#sessionId = randomUUID()
|
|
87
|
+
#providerSessionId
|
|
88
|
+
#totals = { input_tokens: 0, output_tokens: 0 }
|
|
89
|
+
#audioTotals = { input: 0, output: 0 }
|
|
90
|
+
|
|
91
|
+
/** @param {(descriptor: object) => void} emit */
|
|
92
|
+
constructor (emit) {
|
|
93
|
+
this.#emit = emit
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Observe decoded SDK union members without consuming either iterable ahead of its owner.
|
|
98
|
+
* @param {{ chunk?: { bytes?: Uint8Array } }} event
|
|
99
|
+
* @param {boolean} outbound
|
|
100
|
+
* @param {number} [now]
|
|
101
|
+
*/
|
|
102
|
+
observe (event, outbound, now = Date.now()) {
|
|
103
|
+
if (this.#closed) return
|
|
104
|
+
try {
|
|
105
|
+
const bytes = event?.chunk?.bytes
|
|
106
|
+
if (!ArrayBuffer.isView(bytes)) return
|
|
107
|
+
const payload = parse(Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString('utf8'))
|
|
108
|
+
if (!object(payload?.event)) return
|
|
109
|
+
for (const [name, data] of Object.entries(payload.event)) {
|
|
110
|
+
if (!object(data)) continue
|
|
111
|
+
if (outbound) {
|
|
112
|
+
this.#sent(name, data, now)
|
|
113
|
+
} else {
|
|
114
|
+
this.#received(name, data, now)
|
|
115
|
+
}
|
|
116
|
+
if (this.#closed) break
|
|
117
|
+
}
|
|
118
|
+
} catch {
|
|
119
|
+
// Malformed telemetry never changes transport behavior or logs captured conversation data.
|
|
120
|
+
log.debug('Cannot process Nova Sonic telemetry')
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* @param {string} name
|
|
126
|
+
* @param {object} data
|
|
127
|
+
* @param {number} now
|
|
128
|
+
*/
|
|
129
|
+
#sent (name, data, now) {
|
|
130
|
+
const key = data.contentName ?? ''
|
|
131
|
+
switch (name) {
|
|
132
|
+
case 'sessionStart':
|
|
133
|
+
for (const name of ['inferenceConfiguration', 'turnDetectionConfiguration']) {
|
|
134
|
+
const config = this.#configuration[name] = {}
|
|
135
|
+
if (!object(data[name])) continue
|
|
136
|
+
for (const key of ['maxTokens', 'topP', 'temperature', 'endpointingSensitivity']) {
|
|
137
|
+
const value = data[name][key]
|
|
138
|
+
if ((typeof value === 'number' || typeof value === 'string') && String(value).length < 128) {
|
|
139
|
+
config[key] = value
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
break
|
|
144
|
+
case 'promptStart':
|
|
145
|
+
if (this.#prompt !== undefined && this.#prompt !== data.promptName) {
|
|
146
|
+
this.finish(undefined, now)
|
|
147
|
+
return
|
|
148
|
+
}
|
|
149
|
+
this.#prompt = data.promptName
|
|
150
|
+
this.#outputConfiguration = data.audioOutputConfiguration ?? {}
|
|
151
|
+
break
|
|
152
|
+
case 'contentStart':
|
|
153
|
+
this.#outbound.set(key, { ...data, text: '' })
|
|
154
|
+
if (data.type === 'AUDIO' && data.role === 'USER') this.#audio.configure(data.audioInputConfiguration)
|
|
155
|
+
this.#bound(this.#outbound)
|
|
156
|
+
break
|
|
157
|
+
case 'audioInput': {
|
|
158
|
+
const block = this.#outbound.get(key)
|
|
159
|
+
if (block?.type === 'AUDIO' && block.role === 'USER') {
|
|
160
|
+
if (typeof data.content === 'string' && data.content.length) this.#pending.inputAudioObserved = true
|
|
161
|
+
this.#audio.append(data.content, now)
|
|
162
|
+
}
|
|
163
|
+
break
|
|
164
|
+
}
|
|
165
|
+
case 'textInput': {
|
|
166
|
+
const block = this.#outbound.get(key)
|
|
167
|
+
if (block && typeof data.content === 'string') block.text = (block.text + data.content).slice(0, MAX_TEXT)
|
|
168
|
+
break
|
|
169
|
+
}
|
|
170
|
+
case 'toolResult': {
|
|
171
|
+
const block = this.#outbound.get(key)
|
|
172
|
+
const target = this.#current ?? this.#pending
|
|
173
|
+
if (target.toolResults.length < MAX_TOOLS && typeof data.content === 'string') {
|
|
174
|
+
target.toolResults.push({
|
|
175
|
+
role: 'tool',
|
|
176
|
+
content: '',
|
|
177
|
+
toolResults: [{
|
|
178
|
+
result: data.content.slice(0, MAX_TOOL_TEXT),
|
|
179
|
+
toolId: block?.toolResultInputConfiguration?.toolUseId ?? '',
|
|
180
|
+
type: 'tool_result',
|
|
181
|
+
}],
|
|
182
|
+
})
|
|
183
|
+
}
|
|
184
|
+
break
|
|
185
|
+
}
|
|
186
|
+
case 'contentEnd': {
|
|
187
|
+
const block = this.#outbound.get(key)
|
|
188
|
+
this.#outbound.delete(key)
|
|
189
|
+
if (block?.type !== 'TEXT') break
|
|
190
|
+
if (block.role === 'USER' && block.interactive) {
|
|
191
|
+
this.#pending.userText = (this.#pending.userText + block.text).slice(0, MAX_TEXT)
|
|
192
|
+
} else if (this.#history.length < MAX_HISTORY_MESSAGES) {
|
|
193
|
+
const remaining = MAX_TEXT - this.#history.reduce((n, message) => n + message.content.length, 0)
|
|
194
|
+
this.#history.push({
|
|
195
|
+
role: String(block.role ?? 'USER').toLowerCase(), content: block.text.slice(0, remaining),
|
|
196
|
+
})
|
|
197
|
+
}
|
|
198
|
+
break
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* @param {Map<string, object>} map
|
|
205
|
+
*/
|
|
206
|
+
#bound (map) {
|
|
207
|
+
while (map.size > MAX_BLOCKS) map.delete(map.keys().next().value)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
#hasPendingInput () {
|
|
211
|
+
if (hasInput(this.#pending)) return true
|
|
212
|
+
// ASR text may precede contentEnd, even when speech boundary events are absent.
|
|
213
|
+
for (const block of this.#blocks.values()) {
|
|
214
|
+
if (block.turn === this.#pending && block.data.role === 'USER' && block.text) return true
|
|
215
|
+
}
|
|
216
|
+
return false
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* @param {object} data
|
|
221
|
+
* @param {number} now
|
|
222
|
+
*/
|
|
223
|
+
#startResponse (data, now) {
|
|
224
|
+
if (!this.#current || this.#hasPendingInput()) {
|
|
225
|
+
if (this.#current) {
|
|
226
|
+
// A new response can arrive before the previous response's text blocks close.
|
|
227
|
+
for (const block of this.#blocks.values()) {
|
|
228
|
+
if (block.turn === this.#current) this.#text(block)
|
|
229
|
+
}
|
|
230
|
+
this.#emitTurn(this.#current, now)
|
|
231
|
+
}
|
|
232
|
+
const turn = this.#pending
|
|
233
|
+
this.#pending = new Turn()
|
|
234
|
+
turn.started = now
|
|
235
|
+
turn.completionId = data.completionId
|
|
236
|
+
this.#snapshotInput(turn)
|
|
237
|
+
this.#current = turn
|
|
238
|
+
for (const [key, block] of this.#blocks) {
|
|
239
|
+
if (block.turn !== turn) this.#blocks.delete(key)
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return this.#current
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/** @param {Turn} turn */
|
|
246
|
+
#snapshotInput (turn) {
|
|
247
|
+
if (turn.windows.length && !turn.inputPcm) {
|
|
248
|
+
turn.inputRate = this.#audio.rate
|
|
249
|
+
turn.inputPcm = this.#audio.clip(turn.windows[0].start_ms, turn.windows.at(-1).end_ms)
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* @param {string} name
|
|
255
|
+
* @param {object} data
|
|
256
|
+
* @param {number} now
|
|
257
|
+
*/
|
|
258
|
+
#received (name, data, now) {
|
|
259
|
+
// Once any turn has been emitted, its conversation identity must never change.
|
|
260
|
+
if (!this.#providerSessionId && typeof data.sessionId === 'string' && data.sessionId) {
|
|
261
|
+
this.#providerSessionId = data.sessionId
|
|
262
|
+
if (!this.#turnIndex) this.#sessionId = data.sessionId
|
|
263
|
+
}
|
|
264
|
+
switch (name) {
|
|
265
|
+
case 'userSpeechStart': {
|
|
266
|
+
const offset = data.inputAudioOffsetMs
|
|
267
|
+
if (!Number.isFinite(offset) || offset < 0 || this.#pending.windows.length >= MAX_BLOCKS) return
|
|
268
|
+
this.#pending.windows.push({ start_ms: offset })
|
|
269
|
+
this.#pending.inputAudioObserved = true
|
|
270
|
+
this.#pending.inputEnd = undefined
|
|
271
|
+
this.#pending.inputStart ??= this.#audio.anchor === undefined
|
|
272
|
+
? now
|
|
273
|
+
: Math.min(now, this.#audio.anchor + offset)
|
|
274
|
+
break
|
|
275
|
+
}
|
|
276
|
+
case 'userSpeechEnd': {
|
|
277
|
+
const window = this.#pending.windows.at(-1)
|
|
278
|
+
const end = data.inputAudioOffsetMs
|
|
279
|
+
if (!window || !Number.isFinite(end) || end <= window.start_ms) return
|
|
280
|
+
window.end_ms = end
|
|
281
|
+
if (Number.isFinite(data.inputAudioDetectionOffsetMs)) window.detection_ms = data.inputAudioDetectionOffsetMs
|
|
282
|
+
this.#pending.inputEnd = now
|
|
283
|
+
break
|
|
284
|
+
}
|
|
285
|
+
case 'contentStart': {
|
|
286
|
+
const key = data.contentId ?? ''
|
|
287
|
+
if (this.#blocks.has(key) || this.#completed.has(key)) return
|
|
288
|
+
const fields = object(data.additionalModelFields)
|
|
289
|
+
? data.additionalModelFields
|
|
290
|
+
: parse(data.additionalModelFields)
|
|
291
|
+
const stage = object(fields) ? fields.generationStage : undefined
|
|
292
|
+
let turn
|
|
293
|
+
if (data.role === 'ASSISTANT' || data.type === 'TOOL') {
|
|
294
|
+
const newTextResponse = this.#current && !this.#current.audioStarted &&
|
|
295
|
+
completionTime(this.#current) !== undefined && this.#hasPendingInput()
|
|
296
|
+
turn = stage === 'FINAL' && this.#current && !newTextResponse
|
|
297
|
+
? this.#current
|
|
298
|
+
: this.#startResponse(data, now)
|
|
299
|
+
} else {
|
|
300
|
+
turn = this.#hasPendingInput() || !this.#current || completionTime(this.#current) !== undefined
|
|
301
|
+
? this.#pending
|
|
302
|
+
: this.#current
|
|
303
|
+
}
|
|
304
|
+
if (data.role === 'ASSISTANT' && data.type === 'AUDIO') turn.audioStarted = true
|
|
305
|
+
this.#blocks.set(key, { data, stage, turn, text: '', control: false })
|
|
306
|
+
this.#bound(this.#blocks)
|
|
307
|
+
break
|
|
308
|
+
}
|
|
309
|
+
case 'textOutput':
|
|
310
|
+
case 'audioOutput':
|
|
311
|
+
case 'toolUse':
|
|
312
|
+
case 'contentEnd':
|
|
313
|
+
this.#content(name, data, now)
|
|
314
|
+
break
|
|
315
|
+
case 'usageEvent':
|
|
316
|
+
this.#usage(data)
|
|
317
|
+
break
|
|
318
|
+
case 'completionEnd':
|
|
319
|
+
this.finish(undefined, now)
|
|
320
|
+
break
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* @param {string} name
|
|
326
|
+
* @param {object} data
|
|
327
|
+
* @param {number} now
|
|
328
|
+
*/
|
|
329
|
+
#content (name, data, now) {
|
|
330
|
+
const key = data.contentId ?? ''
|
|
331
|
+
const block = this.#blocks.get(key)
|
|
332
|
+
if (!block) return
|
|
333
|
+
const { turn } = block
|
|
334
|
+
if (name === 'textOutput' && typeof data.content === 'string') {
|
|
335
|
+
block.text = (block.text + data.content).slice(0, MAX_TEXT)
|
|
336
|
+
if (block.data.role === 'ASSISTANT' && parse(block.text)?.interrupted === true) {
|
|
337
|
+
// Keep literal JSON until contentEnd confirms protocol control. Retain its
|
|
338
|
+
// receipt time so confirmation does not move the projected playback cut.
|
|
339
|
+
block.interruptionTime ??= now
|
|
340
|
+
} else {
|
|
341
|
+
block.interruptionTime = undefined
|
|
342
|
+
}
|
|
343
|
+
} else if (name === 'audioOutput' && block.data.role === 'ASSISTANT') {
|
|
344
|
+
if (typeof data.content === 'string' && data.content.length) turn.outputAudioObserved = true
|
|
345
|
+
turn.output.append(data.content, block.data.audioOutputConfiguration ?? this.#outputConfiguration, now)
|
|
346
|
+
} else if (name === 'toolUse' && turn.tools.length < MAX_TOOLS) {
|
|
347
|
+
const args = typeof data.content === 'string' && data.content.length <= MAX_TOOL_TEXT ? parse(data.content) : {}
|
|
348
|
+
turn.tools.push({
|
|
349
|
+
name: data.toolName ?? '',
|
|
350
|
+
arguments: object(args) ? args : {},
|
|
351
|
+
toolId: data.toolUseId ?? '',
|
|
352
|
+
type: 'function',
|
|
353
|
+
})
|
|
354
|
+
} else if (name === 'contentEnd') {
|
|
355
|
+
if (block.data.role === 'ASSISTANT' && data.stopReason === 'INTERRUPTED') {
|
|
356
|
+
block.control = block.interruptionTime !== undefined
|
|
357
|
+
turn.output.interrupt(block.interruptionTime ?? now)
|
|
358
|
+
}
|
|
359
|
+
this.#text(block)
|
|
360
|
+
if (block.data.type === 'AUDIO' && data.stopReason === 'END_TURN') turn.generationEnd = now
|
|
361
|
+
if (block.data.role === 'ASSISTANT' && block.data.type === 'TEXT' &&
|
|
362
|
+
!block.control && data.stopReason === 'END_TURN') turn.textEnd = now
|
|
363
|
+
this.#blocks.delete(key)
|
|
364
|
+
this.#completed.add(key)
|
|
365
|
+
if (this.#completed.size > MAX_BLOCKS) this.#completed.delete(this.#completed.values().next().value)
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/** @param {{ control: boolean, data: object, stage?: string, text: string, turn: object }} block */
|
|
370
|
+
#text (block) {
|
|
371
|
+
if (block.control) return
|
|
372
|
+
const field = block.data.role === 'USER'
|
|
373
|
+
? 'userText'
|
|
374
|
+
: block.stage === 'FINAL' ? 'finalText' : 'speculativeText'
|
|
375
|
+
block.turn[field] = (block.turn[field] + block.text).slice(0, MAX_TEXT)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/** @param {object} data */
|
|
379
|
+
#usage (data) {
|
|
380
|
+
for (const direction of ['input', 'output']) {
|
|
381
|
+
const key = `${direction}_tokens`
|
|
382
|
+
const total = data[direction === 'input' ? 'totalInputTokens' : 'totalOutputTokens']
|
|
383
|
+
if (!Number.isSafeInteger(total) || total < this.#totals[key]) continue
|
|
384
|
+
const delta = total - this.#totals[key]
|
|
385
|
+
this.#totals[key] = total
|
|
386
|
+
const target = !this.#current || (direction === 'input' && this.#hasPendingInput())
|
|
387
|
+
? this.#pending
|
|
388
|
+
: this.#current
|
|
389
|
+
target.metrics[key] += delta
|
|
390
|
+
target.metrics.total_tokens += delta
|
|
391
|
+
|
|
392
|
+
// Speech usage is a subset of these totals, never another charge to total_tokens.
|
|
393
|
+
const audioKey = `${direction}_audio_tokens`
|
|
394
|
+
const audioTotal = data.details?.total?.[direction]?.speechTokens
|
|
395
|
+
const previous = this.#audioTotals[direction]
|
|
396
|
+
const valid = Number.isSafeInteger(audioTotal) && audioTotal >= 0 && audioTotal <= total &&
|
|
397
|
+
(previous === undefined || audioTotal >= previous)
|
|
398
|
+
const audioDelta = valid && previous !== undefined ? audioTotal - previous : undefined
|
|
399
|
+
this.#audioTotals[direction] = valid ? audioTotal : delta ? undefined : previous
|
|
400
|
+
if (audioDelta === undefined || audioDelta > delta) {
|
|
401
|
+
if (delta || audioDelta > delta) {
|
|
402
|
+
target.missingAudioMetrics.add(audioKey)
|
|
403
|
+
delete target.metrics[audioKey]
|
|
404
|
+
}
|
|
405
|
+
} else if (!target.missingAudioMetrics.has(audioKey)) {
|
|
406
|
+
target.metrics[audioKey] = (target.metrics[audioKey] ?? 0) + audioDelta
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* @param {Turn} turn
|
|
413
|
+
* @param {number} now
|
|
414
|
+
* @param {Error} [error]
|
|
415
|
+
*/
|
|
416
|
+
#emitTurn (turn, now, error) {
|
|
417
|
+
if (turn.emitted) return
|
|
418
|
+
turn.emitted = true
|
|
419
|
+
this.#turnIndex++
|
|
420
|
+
try {
|
|
421
|
+
this.#snapshotInput(turn)
|
|
422
|
+
const start = turn.inputStart ?? turn.started ?? now
|
|
423
|
+
const llmStart = turn.inputEnd ?? turn.started ?? start
|
|
424
|
+
const completed = completionTime(turn)
|
|
425
|
+
const responseEnd = Math.max(llmStart, completed ?? turn.output.interrupted ?? now)
|
|
426
|
+
this.#emit({
|
|
427
|
+
turn,
|
|
428
|
+
error,
|
|
429
|
+
sessionId: this.#sessionId,
|
|
430
|
+
history: this.#history,
|
|
431
|
+
startTime: start,
|
|
432
|
+
finishTime: Math.max(responseEnd, turn.output.end ?? 0, turn.inputEnd ?? 0),
|
|
433
|
+
llmStart,
|
|
434
|
+
responseEnd,
|
|
435
|
+
metadata: {
|
|
436
|
+
turn_index: this.#turnIndex,
|
|
437
|
+
completion_id: turn.completionId,
|
|
438
|
+
speech_windows: turn.windows,
|
|
439
|
+
ttfa_boundary: 'speech_end_event_receipt',
|
|
440
|
+
output_timing: 'projected_playback',
|
|
441
|
+
interrupted: turn.output.interrupted !== undefined,
|
|
442
|
+
partial: completed === undefined,
|
|
443
|
+
transcript_stage: turn.finalText ? 'FINAL' : 'SPECULATIVE',
|
|
444
|
+
usage_attribution: 'event_receipt',
|
|
445
|
+
generated_output_bytes: turn.output.generatedBytes,
|
|
446
|
+
session_configuration: this.#configuration,
|
|
447
|
+
},
|
|
448
|
+
})
|
|
449
|
+
} catch {
|
|
450
|
+
log.debug('Cannot emit Nova Sonic turn')
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Input EOF is deliberately not a finish. Only output completion, cancellation or failure flushes.
|
|
456
|
+
* @param {Error} [error]
|
|
457
|
+
* @param {number} [now]
|
|
458
|
+
*/
|
|
459
|
+
finish (error, now = Date.now()) {
|
|
460
|
+
if (this.#closed) return
|
|
461
|
+
this.#closed = true
|
|
462
|
+
for (const block of this.#blocks.values()) this.#text(block)
|
|
463
|
+
if (this.#current) {
|
|
464
|
+
// A failure before the next response starts belongs to its pending utterance,
|
|
465
|
+
// not to the response that has already completed.
|
|
466
|
+
const laterTurnFailed = completionTime(this.#current) !== undefined && this.#hasPendingInput()
|
|
467
|
+
this.#emitTurn(this.#current, now, laterTurnFailed ? undefined : error)
|
|
468
|
+
}
|
|
469
|
+
if (hasInput(this.#pending) || this.#pending.metrics.total_tokens || (error && !this.#current)) {
|
|
470
|
+
this.#emitTurn(this.#pending, now, error)
|
|
471
|
+
}
|
|
472
|
+
this.#current = undefined
|
|
473
|
+
this.#pending = new Turn()
|
|
474
|
+
this.#blocks.clear()
|
|
475
|
+
this.#outbound.clear()
|
|
476
|
+
this.#completed.clear()
|
|
477
|
+
this.#history = []
|
|
478
|
+
this.#audio.clear()
|
|
479
|
+
this.#emit = undefined
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
module.exports = SonicSession
|
|
@@ -10,6 +10,11 @@ const RealtimeSession = require('./session')
|
|
|
10
10
|
/**
|
|
11
11
|
* @typedef {import('./session').TurnDescriptor} TurnDescriptor
|
|
12
12
|
* @typedef {import('./turns').ResponseTurn} ResponseTurn
|
|
13
|
+
* @typedef {{
|
|
14
|
+
* session: RealtimeSession,
|
|
15
|
+
* socket: object | undefined,
|
|
16
|
+
* finalize: (failed?: boolean) => void,
|
|
17
|
+
* }} Connection
|
|
13
18
|
*/
|
|
14
19
|
|
|
15
20
|
// One span per phase of a turn. `user speech` and `agent speech` share a channel: they differ only
|
|
@@ -36,7 +41,7 @@ const audioChannel = dc.channel('dd-trace:openai:realtime:audio')
|
|
|
36
41
|
const USER_SPEECH = { operation: 'createRealtimeUserSpeech', llmobsName: 'user speech' }
|
|
37
42
|
const AGENT_SPEECH = { operation: 'createRealtimeAgentSpeech', llmobsName: 'agent speech' }
|
|
38
43
|
|
|
39
|
-
/** @type {WeakMap<object,
|
|
44
|
+
/** @type {WeakMap<object, Connection>} */
|
|
40
45
|
const connections = new WeakMap()
|
|
41
46
|
|
|
42
47
|
/**
|
|
@@ -92,7 +97,7 @@ function captureContext (turn) {
|
|
|
92
97
|
* Start tracking a connection, reading the model and base path off the URL the SDK already built.
|
|
93
98
|
*
|
|
94
99
|
* @param {object} emitter
|
|
95
|
-
* @returns {
|
|
100
|
+
* @returns {Connection}
|
|
96
101
|
*/
|
|
97
102
|
function createConnection (emitter) {
|
|
98
103
|
let model
|
|
@@ -135,7 +140,7 @@ function createConnection (emitter) {
|
|
|
135
140
|
session.finishSession(Date.now(), failed)
|
|
136
141
|
}
|
|
137
142
|
|
|
138
|
-
const connection = { session, finalize }
|
|
143
|
+
const connection = { session, socket: emitter.socket, finalize }
|
|
139
144
|
attachSocketClose(emitter, finalize)
|
|
140
145
|
return connection
|
|
141
146
|
}
|
|
@@ -181,14 +186,21 @@ function attachSocketClose (emitter, finalize) {
|
|
|
181
186
|
|
|
182
187
|
/**
|
|
183
188
|
* @param {object} emitter
|
|
184
|
-
* @returns {
|
|
189
|
+
* @returns {Connection | undefined}
|
|
185
190
|
*/
|
|
186
191
|
function getConnection (emitter) {
|
|
187
192
|
const connection = connections.get(emitter)
|
|
188
|
-
if (connection !== undefined)
|
|
193
|
+
if (connection !== undefined) {
|
|
194
|
+
if (connection.socket === emitter.socket) return connection
|
|
195
|
+
|
|
196
|
+
// Opt-in SDK recovery keeps the emitter but replaces its socket with a new server session.
|
|
197
|
+
// Retire the old state before attaching close and exit cleanup to the replacement.
|
|
198
|
+
connection.finalize(true)
|
|
199
|
+
connections.delete(emitter)
|
|
200
|
+
}
|
|
189
201
|
|
|
190
202
|
// Nothing is listening, so never allocate. Checked only on the miss path, so an established
|
|
191
|
-
// connection
|
|
203
|
+
// connection reuses its state while the socket is unchanged.
|
|
192
204
|
if (!turnChannel.start.hasSubscribers) return
|
|
193
205
|
|
|
194
206
|
const created = createConnection(emitter)
|
|
@@ -829,7 +829,7 @@ class RealtimeSession {
|
|
|
829
829
|
input.audio.totalDecodedBytes, inputFormat.mimeType, inputFormat.sampleRate
|
|
830
830
|
)
|
|
831
831
|
const userEnd = input.speechEndTime ??
|
|
832
|
-
(userStart
|
|
832
|
+
(userStart === undefined || inputDurationMs === undefined ? undefined : userStart + inputDurationMs)
|
|
833
833
|
|
|
834
834
|
const agentStart = turn.audio.startTime
|
|
835
835
|
const playbackMs = segmentDurationMs(
|
|
@@ -58,6 +58,13 @@ const V4_PACKAGE_SHIMS = [
|
|
|
58
58
|
versions: ['>=4.87.0'],
|
|
59
59
|
interceptChannel: responsesInterceptChannel,
|
|
60
60
|
},
|
|
61
|
+
{
|
|
62
|
+
file: 'resources/decisions',
|
|
63
|
+
targetClass: 'Decisions',
|
|
64
|
+
baseResource: 'decisions',
|
|
65
|
+
methods: ['create'],
|
|
66
|
+
versions: ['>=7.30.0'],
|
|
67
|
+
},
|
|
61
68
|
{
|
|
62
69
|
file: 'resources/embeddings',
|
|
63
70
|
targetClass: 'Embeddings',
|
|
@@ -10,10 +10,9 @@ const logSubmissionCh = channel('ci:log-submission:log')
|
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* @param {string} symbol
|
|
13
|
-
* @param {(original: Function) => Function} wrapper
|
|
14
13
|
* @param {Function} pino
|
|
15
14
|
*/
|
|
16
|
-
function wrapPino (symbol,
|
|
15
|
+
function wrapPino (symbol, pino) {
|
|
17
16
|
/**
|
|
18
17
|
* @param {unknown[]} args
|
|
19
18
|
* @returns {unknown}
|
|
@@ -25,7 +24,7 @@ function wrapPino (symbol, wrapper, pino) {
|
|
|
25
24
|
configurable: true,
|
|
26
25
|
enumerable: true,
|
|
27
26
|
writable: true,
|
|
28
|
-
value:
|
|
27
|
+
value: wrapAsJson(instance[symbol]),
|
|
29
28
|
})
|
|
30
29
|
|
|
31
30
|
return instance
|
|
@@ -83,19 +82,19 @@ function wrapPrettyFactory (prettyFactory) {
|
|
|
83
82
|
addHook({ name: 'pino', versions: ['2 - 3', '4'], patchDefault: true }, (pino) => {
|
|
84
83
|
const asJsonSym = (pino.symbols && pino.symbols.asJsonSym) || 'asJson'
|
|
85
84
|
|
|
86
|
-
return shimmer.wrapFunction(pino, pino => wrapPino(asJsonSym,
|
|
85
|
+
return shimmer.wrapFunction(pino, pino => wrapPino(asJsonSym, pino))
|
|
87
86
|
})
|
|
88
87
|
|
|
89
88
|
addHook({ name: 'pino', versions: ['>=5 <6.8.0'], patchDefault: true }, (pino) => {
|
|
90
89
|
const asJsonSym = ((pino.default || pino)?.symbols.asJsonSym) || 'asJson'
|
|
91
90
|
|
|
92
|
-
return shimmer.wrapFunction(pino, pino => wrapPino(asJsonSym,
|
|
91
|
+
return shimmer.wrapFunction(pino, pino => wrapPino(asJsonSym, pino.default || pino))
|
|
93
92
|
})
|
|
94
93
|
|
|
95
94
|
addHook({ name: 'pino', versions: ['>=6.8.0'], patchDefault: false }, (pino) => {
|
|
96
95
|
const asJsonSym = pino.symbols.asJsonSym
|
|
97
96
|
|
|
98
|
-
const wrapped = shimmer.wrapFunction(pino, pino => wrapPino(asJsonSym,
|
|
97
|
+
const wrapped = shimmer.wrapFunction(pino, pino => wrapPino(asJsonSym, pino))
|
|
99
98
|
wrapped.pino = wrapped
|
|
100
99
|
wrapped.default = wrapped
|
|
101
100
|
|
|
@@ -2657,11 +2657,14 @@ function instrumentWorkerMainMethods (workerMain) {
|
|
|
2657
2657
|
}
|
|
2658
2658
|
|
|
2659
2659
|
// In cases where there is no afterEach hook with _ddHook, we need to add one
|
|
2660
|
+
// Playwright 1.64 builds the hook step stack from hook.location, so an undefined
|
|
2661
|
+
// location ends up as [undefined] and breaks trace writing
|
|
2660
2662
|
if (!existAfterEachHook) {
|
|
2661
2663
|
test.parent._hooks.push({
|
|
2662
2664
|
type: 'afterEach',
|
|
2663
2665
|
fn: cleanupRumPages,
|
|
2664
2666
|
title: 'afterEach hook',
|
|
2667
|
+
location: test.location,
|
|
2665
2668
|
_ddHook: true,
|
|
2666
2669
|
})
|
|
2667
2670
|
}
|