dd-trace 5.110.0 → 5.112.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 +1 -2
- package/README.md +13 -8
- package/ci/init.js +21 -2
- package/ci/vitest-no-worker-init-setup.mjs +430 -0
- package/ext/tags.js +2 -0
- package/index.d.ts +34 -1
- package/initialize.mjs +5 -6
- package/loader-hook.mjs +76 -55
- package/package.json +37 -34
- package/packages/datadog-instrumentations/src/ai.js +45 -0
- package/packages/datadog-instrumentations/src/apollo-server.js +5 -13
- package/packages/datadog-instrumentations/src/child_process.js +3 -3
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +587 -0
- package/packages/datadog-instrumentations/src/cucumber.js +102 -43
- package/packages/datadog-instrumentations/src/cypress-config.js +11 -3
- package/packages/datadog-instrumentations/src/fastify.js +27 -8
- package/packages/datadog-instrumentations/src/fs.js +8 -6
- package/packages/datadog-instrumentations/src/graphql.js +26 -495
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +7 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +6 -6
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/claude-agent-sdk.js +17 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +201 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -0
- package/packages/datadog-instrumentations/src/jest.js +713 -63
- package/packages/datadog-instrumentations/src/mocha/main.js +24 -3
- package/packages/datadog-instrumentations/src/mocha/utils.js +128 -22
- package/packages/datadog-instrumentations/src/mocha/worker.js +13 -0
- package/packages/datadog-instrumentations/src/mongodb.js +74 -0
- package/packages/datadog-instrumentations/src/mongoose.js +13 -2
- package/packages/datadog-instrumentations/src/playwright.js +13 -9
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +1297 -0
- package/packages/datadog-instrumentations/src/vitest-main.js +1594 -0
- package/packages/datadog-instrumentations/src/vitest-util.js +254 -0
- package/packages/datadog-instrumentations/src/vitest-worker.js +823 -0
- package/packages/datadog-instrumentations/src/vitest.js +11 -1855
- package/packages/datadog-plugin-ai/src/index.js +1 -1
- package/packages/datadog-plugin-ai/src/tracing.js +66 -3
- package/packages/datadog-plugin-ai/src/utils.js +17 -4
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/client.js +2 -2
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +19 -10
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +1 -0
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +47 -11
- package/packages/datadog-plugin-child_process/src/index.js +13 -2
- package/packages/datadog-plugin-claude-agent-sdk/src/index.js +31 -0
- package/packages/datadog-plugin-claude-agent-sdk/src/tracing.js +107 -0
- package/packages/datadog-plugin-claude-agent-sdk/src/util.js +15 -0
- package/packages/datadog-plugin-cucumber/src/index.js +15 -24
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +360 -14
- package/packages/datadog-plugin-cypress/src/index.js +47 -2
- package/packages/datadog-plugin-cypress/src/plugin.js +1 -0
- package/packages/datadog-plugin-cypress/src/support.js +45 -1
- package/packages/datadog-plugin-express/src/code_origin.js +1 -1
- package/packages/datadog-plugin-fastify/src/code_origin.js +1 -1
- package/packages/datadog-plugin-graphql/src/execute.js +639 -12
- package/packages/datadog-plugin-graphql/src/index.js +37 -9
- package/packages/datadog-plugin-graphql/src/parse.js +24 -4
- package/packages/datadog-plugin-graphql/src/utils.js +9 -2
- package/packages/datadog-plugin-graphql/src/validate.js +17 -6
- package/packages/datadog-plugin-http/src/index.js +6 -8
- package/packages/datadog-plugin-jest/src/index.js +31 -15
- package/packages/datadog-plugin-mocha/src/index.js +40 -15
- package/packages/datadog-plugin-mongodb-core/src/bulk-write.js +43 -0
- package/packages/datadog-plugin-mongodb-core/src/index.js +8 -443
- package/packages/datadog-plugin-mongodb-core/src/query.js +452 -0
- package/packages/datadog-plugin-openai/src/services.js +2 -2
- package/packages/datadog-plugin-playwright/src/index.js +4 -3
- package/packages/datadog-plugin-vitest/src/index.js +120 -72
- package/packages/datadog-shimmer/src/shimmer.js +37 -16
- package/packages/dd-trace/src/aiguard/index.js +9 -14
- package/packages/dd-trace/src/aiguard/integrations/index.js +34 -0
- package/packages/dd-trace/src/aiguard/integrations/openai.js +47 -33
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +42 -28
- package/packages/dd-trace/src/aiguard/sdk.js +2 -2
- package/packages/dd-trace/src/appsec/api_security/sampler.js +3 -3
- package/packages/dd-trace/src/appsec/channels.js +3 -1
- package/packages/dd-trace/src/appsec/downstream_requests.js +4 -4
- package/packages/dd-trace/src/appsec/graphql.js +14 -11
- package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +23 -23
- package/packages/dd-trace/src/appsec/iast/security-controls/index.js +2 -2
- package/packages/dd-trace/src/appsec/iast/telemetry/index.js +2 -1
- package/packages/dd-trace/src/appsec/index.js +10 -1
- package/packages/dd-trace/src/appsec/lambda.js +135 -0
- package/packages/dd-trace/src/appsec/reporter.js +49 -10
- package/packages/dd-trace/src/appsec/telemetry/index.js +1 -1
- package/packages/dd-trace/src/appsec/waf/index.js +16 -4
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +4 -4
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +26 -1
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +115 -24
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +21 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +5 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +6 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +130 -20
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +17 -0
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +47 -26
- package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +2 -2
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +64 -53
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +3 -3
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +156 -0
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +23 -5
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +128 -0
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +287 -0
- package/packages/dd-trace/src/config/defaults.js +3 -2
- package/packages/dd-trace/src/config/generated-config-types.d.ts +71 -57
- package/packages/dd-trace/src/config/helper.js +1 -0
- package/packages/dd-trace/src/config/index.js +35 -22
- package/packages/dd-trace/src/config/major-overrides.js +4 -2
- package/packages/dd-trace/src/config/remote_config.js +1 -1
- package/packages/dd-trace/src/config/supported-configurations.json +127 -56
- package/packages/dd-trace/src/constants.js +7 -0
- package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -2
- package/packages/dd-trace/src/datastreams/processor.js +11 -3
- package/packages/dd-trace/src/exporters/agent/index.js +1 -1
- package/packages/dd-trace/src/exporters/agentless/writer.js +4 -4
- package/packages/dd-trace/src/llmobs/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +403 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/index.js +8 -395
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +27 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +363 -0
- package/packages/dd-trace/src/llmobs/plugins/base.js +4 -4
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +270 -0
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/utils.js +10 -0
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +3 -3
- package/packages/dd-trace/src/llmobs/span_processor.js +1 -1
- package/packages/dd-trace/src/llmobs/tagger.js +3 -3
- package/packages/dd-trace/src/llmobs/writers/base.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +54 -5
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +40 -0
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +169 -0
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +38 -39
- package/packages/dd-trace/src/opentelemetry/span.js +5 -0
- package/packages/dd-trace/src/opentelemetry/trace/index.js +4 -0
- package/packages/dd-trace/src/opentelemetry/tracer_provider.js +1 -1
- package/packages/dd-trace/src/opentracing/tracer.js +6 -1
- package/packages/dd-trace/src/plugin_manager.js +23 -7
- package/packages/dd-trace/src/plugins/ci_plugin.js +159 -10
- package/packages/dd-trace/src/plugins/index.js +2 -0
- package/packages/dd-trace/src/plugins/util/llm.js +6 -1
- package/packages/dd-trace/src/plugins/util/test.js +15 -9
- package/packages/dd-trace/src/profiling/exporter_cli.js +2 -2
- package/packages/dd-trace/src/profiling/exporters/agent.js +28 -3
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +3 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +1 -1
- package/packages/dd-trace/src/proxy.js +8 -8
- package/packages/dd-trace/src/span_processor.js +5 -6
- package/packages/dd-trace/src/span_stats.js +96 -78
- package/packages/dd-trace/src/startup-log.js +2 -1
- package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
- package/packages/dd-trace/src/telemetry/endpoints.js +6 -4
- package/packages/dd-trace/src/telemetry/index.js +2 -2
- package/packages/dd-trace/src/telemetry/logs/index.js +1 -1
- package/packages/dd-trace/src/telemetry/send-data.js +8 -8
- package/packages/dd-trace/src/telemetry/session-propagation.js +1 -1
- package/packages/dd-trace/src/telemetry/telemetry.js +9 -9
- 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
- package/packages/datadog-plugin-graphql/src/resolve.js +0 -170
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { parseModelProvider } = require('../../../../../datadog-plugin-ai/src/utils')
|
|
4
|
+
const BaseLLMObsPlugin = require('../base')
|
|
5
|
+
const {
|
|
6
|
+
getLlmObsSpanName,
|
|
7
|
+
getGenerationMetadataFromEvent,
|
|
8
|
+
getJsonStringValue,
|
|
9
|
+
getToolCallResultContent,
|
|
10
|
+
} = require('./util')
|
|
11
|
+
|
|
12
|
+
// TODO: add in embedMany once it has tracingChannel support
|
|
13
|
+
const SPAN_NAME_TO_KIND_MAPPING = {
|
|
14
|
+
// embeddings
|
|
15
|
+
embed: 'embedding',
|
|
16
|
+
// text generation
|
|
17
|
+
generateText: 'workflow',
|
|
18
|
+
streamText: 'workflow',
|
|
19
|
+
// llm operations
|
|
20
|
+
languageModelCall: 'llm',
|
|
21
|
+
// steps
|
|
22
|
+
step: 'step', // TODO: support step spans for manual instrumentation as well
|
|
23
|
+
// tools
|
|
24
|
+
executeTool: 'tool',
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function nameFromOperation (operation, event) {
|
|
28
|
+
if (operation === 'executeTool') {
|
|
29
|
+
return event.toolCall?.toolName
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return operation
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function formatLanguageModelInputMessages (instructions, messages) {
|
|
36
|
+
if (!Array.isArray(messages)) return
|
|
37
|
+
const inputMessages = []
|
|
38
|
+
|
|
39
|
+
if (instructions) {
|
|
40
|
+
const systemPrompt = typeof instructions === 'string'
|
|
41
|
+
? instructions
|
|
42
|
+
: Array.isArray(instructions)
|
|
43
|
+
? instructions.map(instruction => instruction.content).join('')
|
|
44
|
+
: instructions.content
|
|
45
|
+
|
|
46
|
+
inputMessages.push({ role: 'system', content: systemPrompt })
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
for (const message of messages) {
|
|
50
|
+
const { role, content } = message
|
|
51
|
+
|
|
52
|
+
if (role === 'system') {
|
|
53
|
+
inputMessages.push({ role, content })
|
|
54
|
+
} else if (role === 'user') {
|
|
55
|
+
const userMessageContent =
|
|
56
|
+
typeof content === 'string'
|
|
57
|
+
? content
|
|
58
|
+
: content
|
|
59
|
+
.filter(part => part.type === 'text')
|
|
60
|
+
.map(part => part.text)
|
|
61
|
+
.join('')
|
|
62
|
+
|
|
63
|
+
inputMessages.push({ role, content: userMessageContent })
|
|
64
|
+
} else if (role === 'assistant') {
|
|
65
|
+
if (typeof content === 'string') {
|
|
66
|
+
inputMessages.push({ role, content })
|
|
67
|
+
} else {
|
|
68
|
+
// re-use existing output message formatting
|
|
69
|
+
inputMessages.push(...formatLanguageModelOutputMessages(content))
|
|
70
|
+
}
|
|
71
|
+
} else if (role === 'tool') {
|
|
72
|
+
for (const part of content) {
|
|
73
|
+
if (part.type === 'tool-result') { // TODO: support tool approvals
|
|
74
|
+
const safeResult = getToolCallResultContent(part)
|
|
75
|
+
|
|
76
|
+
inputMessages.push({
|
|
77
|
+
role,
|
|
78
|
+
content: safeResult,
|
|
79
|
+
toolId: part.toolCallId,
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return inputMessages
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function formatLanguageModelOutputMessages (content) {
|
|
90
|
+
if (!Array.isArray(content)) return
|
|
91
|
+
|
|
92
|
+
const outputMessages = []
|
|
93
|
+
const toolCalls = []
|
|
94
|
+
|
|
95
|
+
let textContent = ''
|
|
96
|
+
let reasoningContent = ''
|
|
97
|
+
|
|
98
|
+
for (const part of content) {
|
|
99
|
+
const { type } = part
|
|
100
|
+
|
|
101
|
+
if (type === 'text') {
|
|
102
|
+
textContent += part.text
|
|
103
|
+
} else if (type === 'reasoning') {
|
|
104
|
+
reasoningContent += part.text
|
|
105
|
+
} else if (type === 'tool-call') {
|
|
106
|
+
const toolCallArguments = typeof part.input === 'string'
|
|
107
|
+
? getJsonStringValue(part.input, {})
|
|
108
|
+
: part.input
|
|
109
|
+
|
|
110
|
+
toolCalls.push({
|
|
111
|
+
arguments: toolCallArguments,
|
|
112
|
+
name: part.toolName,
|
|
113
|
+
type: 'function',
|
|
114
|
+
toolId: part.toolCallId,
|
|
115
|
+
})
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (reasoningContent) {
|
|
120
|
+
outputMessages.push({ role: 'reasoning', content: reasoningContent })
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const finalTextMessage = { role: 'assistant' }
|
|
124
|
+
|
|
125
|
+
if (textContent) {
|
|
126
|
+
finalTextMessage.content = textContent
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (toolCalls.length) {
|
|
130
|
+
finalTextMessage.toolCalls = toolCalls
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
outputMessages.push(finalTextMessage)
|
|
134
|
+
|
|
135
|
+
return outputMessages
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
class VercelAiTelemetryPlugin extends BaseLLMObsPlugin {
|
|
139
|
+
static id = 'ai_llmobs_vercel_telemetry'
|
|
140
|
+
static integration = 'ai'
|
|
141
|
+
static prefix = 'tracing:ai:telemetry'
|
|
142
|
+
|
|
143
|
+
/** @type {Map<string, Array<Record<string, unknown>>>} */
|
|
144
|
+
#lastOutputContentByCallId = new Map()
|
|
145
|
+
|
|
146
|
+
constructor () {
|
|
147
|
+
super(...arguments)
|
|
148
|
+
|
|
149
|
+
this.addSub('dd-trace:vercel-ai:chunk', ({ ctx, chunk, done }) => {
|
|
150
|
+
ctx.chunks ??= []
|
|
151
|
+
const chunks = ctx.chunks
|
|
152
|
+
if (chunk) chunks.push(chunk)
|
|
153
|
+
|
|
154
|
+
ctx.streamConsumed = done
|
|
155
|
+
|
|
156
|
+
if (!done) return
|
|
157
|
+
|
|
158
|
+
ctx.result ??= {}
|
|
159
|
+
const contentById = {}
|
|
160
|
+
|
|
161
|
+
for (const chunk of chunks) {
|
|
162
|
+
if (chunk.type === 'finish') {
|
|
163
|
+
ctx.result.usage = chunk.usage
|
|
164
|
+
} else if (chunk.type === 'reasoning-start') {
|
|
165
|
+
contentById[chunk.id] = { type: 'reasoning', text: '' }
|
|
166
|
+
} else if (chunk.type === 'text-start') {
|
|
167
|
+
contentById[chunk.id] = { type: 'text', text: '' }
|
|
168
|
+
} else if (chunk.type === 'reasoning-delta' || chunk.type === 'text-delta') {
|
|
169
|
+
if (!contentById[chunk.id]) {
|
|
170
|
+
// special-case handling when the start event did not come through as a chunk
|
|
171
|
+
contentById[chunk.id] = {
|
|
172
|
+
type: chunk.type === 'reasoning-delta' ? 'reasoning' : 'text',
|
|
173
|
+
text: '',
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
contentById[chunk.id].text += chunk.delta
|
|
177
|
+
} else if (chunk.type === 'tool-call') {
|
|
178
|
+
contentById[chunk.toolCallId] = chunk
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const content = Object.values(contentById)
|
|
183
|
+
ctx.result.content = content
|
|
184
|
+
|
|
185
|
+
this.#lastOutputContentByCallId.set(ctx.event.callId, content)
|
|
186
|
+
})
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* @override
|
|
191
|
+
*/
|
|
192
|
+
asyncEnd (ctx) {
|
|
193
|
+
// check if isStreamed and stream resolved
|
|
194
|
+
// this event will fire multiple times for the same channel
|
|
195
|
+
if (ctx.isStream && ctx.result?.stream && !ctx.streamConsumed) return
|
|
196
|
+
|
|
197
|
+
super.asyncEnd(ctx)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* @override
|
|
202
|
+
*/
|
|
203
|
+
getLLMObsSpanRegisterOptions (ctx) {
|
|
204
|
+
const span = ctx.currentStore?.span
|
|
205
|
+
if (!span) return
|
|
206
|
+
|
|
207
|
+
const { type: operation, event } = ctx
|
|
208
|
+
const kind = SPAN_NAME_TO_KIND_MAPPING[operation]
|
|
209
|
+
if (!kind) return
|
|
210
|
+
|
|
211
|
+
const normalizedName = nameFromOperation(operation, event) || operation
|
|
212
|
+
|
|
213
|
+
const options = {
|
|
214
|
+
kind, name: getLlmObsSpanName(normalizedName, event.functionId),
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (kind === 'llm' || kind === 'embedding') {
|
|
218
|
+
const modelName = event.modelId
|
|
219
|
+
|
|
220
|
+
options.modelName = modelName
|
|
221
|
+
options.modelProvider = parseModelProvider(event.provider, modelName)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return options
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* @override
|
|
229
|
+
*/
|
|
230
|
+
setLLMObsTags (ctx) {
|
|
231
|
+
const span = ctx.currentStore?.span
|
|
232
|
+
if (!span) return
|
|
233
|
+
|
|
234
|
+
const { type: operation } = ctx
|
|
235
|
+
const kind = SPAN_NAME_TO_KIND_MAPPING[operation]
|
|
236
|
+
if (!kind) return
|
|
237
|
+
|
|
238
|
+
switch (operation) {
|
|
239
|
+
case 'embed':
|
|
240
|
+
this.setEmbeddingTags(span, ctx)
|
|
241
|
+
break
|
|
242
|
+
case 'generateText':
|
|
243
|
+
case 'streamText':
|
|
244
|
+
this.setTextGenerationTags(span, ctx)
|
|
245
|
+
break
|
|
246
|
+
case 'languageModelCall':
|
|
247
|
+
this.setLanguageModelCallTags(span, ctx)
|
|
248
|
+
break
|
|
249
|
+
case 'step':
|
|
250
|
+
this.setStepTags(span, ctx)
|
|
251
|
+
break
|
|
252
|
+
case 'executeTool':
|
|
253
|
+
this.setToolTags(span, ctx)
|
|
254
|
+
break
|
|
255
|
+
default:
|
|
256
|
+
break
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
setEmbeddingTags (span, ctx) {
|
|
261
|
+
const { event, result } = ctx
|
|
262
|
+
|
|
263
|
+
const input = event.value
|
|
264
|
+
const embedding = result?.embedding
|
|
265
|
+
const embeddingTextResult = embedding ? `[1 embedding(s) returned with size ${embedding.length}]` : ''
|
|
266
|
+
|
|
267
|
+
this._tagger.tagEmbeddingIO(span, input, embeddingTextResult)
|
|
268
|
+
|
|
269
|
+
this._tagger.tagMetrics(span, {
|
|
270
|
+
inputTokens: result?.usage?.tokens,
|
|
271
|
+
})
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
setTextGenerationTags (span, ctx) {
|
|
275
|
+
const { event, result } = ctx
|
|
276
|
+
|
|
277
|
+
const lastUserPrompt = event.messages.findLast(message => message.role === 'user')?.content
|
|
278
|
+
const input = Array.isArray(lastUserPrompt) ? lastUserPrompt.map(part => part.text ?? '').join('') : lastUserPrompt
|
|
279
|
+
|
|
280
|
+
const output =
|
|
281
|
+
ctx.isStream
|
|
282
|
+
? this.#lastOutputContentByCallId.get(event.callId)?.find(part => part.type === 'text')?.text
|
|
283
|
+
: result?._output
|
|
284
|
+
|
|
285
|
+
this._tagger.tagTextIO(span, input, output)
|
|
286
|
+
|
|
287
|
+
const metadata = getGenerationMetadataFromEvent(event)
|
|
288
|
+
this._tagger.tagMetadata(span, metadata)
|
|
289
|
+
|
|
290
|
+
if (ctx.isStream) {
|
|
291
|
+
this.#lastOutputContentByCallId.delete(event.callId)
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
setStepTags (span, ctx) {
|
|
296
|
+
const { result, event } = ctx
|
|
297
|
+
|
|
298
|
+
const content = ctx.isStream ? this.#lastOutputContentByCallId.get(event.callId) : result?.content
|
|
299
|
+
|
|
300
|
+
// capture reasoning if applicable
|
|
301
|
+
const reasoning = content?.find(part => part.type === 'reasoning')?.text
|
|
302
|
+
if (reasoning) {
|
|
303
|
+
this._tagger.tagTextIO(span, reasoning)
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
setLanguageModelCallTags (span, ctx) {
|
|
308
|
+
const { event, result } = ctx
|
|
309
|
+
|
|
310
|
+
// input messages
|
|
311
|
+
const { instructions, messages } = event
|
|
312
|
+
const inputMessages = formatLanguageModelInputMessages(instructions, messages)
|
|
313
|
+
|
|
314
|
+
// output messages
|
|
315
|
+
const outputMessages =
|
|
316
|
+
result ? formatLanguageModelOutputMessages(result.content) : [{ role: 'assistant', content: '' }]
|
|
317
|
+
|
|
318
|
+
this._tagger.tagLLMIO(span, inputMessages, outputMessages)
|
|
319
|
+
|
|
320
|
+
// tool definitions
|
|
321
|
+
const { tools } = event
|
|
322
|
+
if (Array.isArray(tools)) {
|
|
323
|
+
this._tagger.tagToolDefinitions(
|
|
324
|
+
span,
|
|
325
|
+
tools.map(({ inputSchema, ...rest }) => ({
|
|
326
|
+
...rest,
|
|
327
|
+
schema: {
|
|
328
|
+
properties: inputSchema?.properties ?? {},
|
|
329
|
+
required: inputSchema?.required ?? [],
|
|
330
|
+
type: inputSchema?.type ?? '',
|
|
331
|
+
},
|
|
332
|
+
}))
|
|
333
|
+
)
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// metadata
|
|
337
|
+
const metadata = getGenerationMetadataFromEvent(event)
|
|
338
|
+
this._tagger.tagMetadata(span, metadata)
|
|
339
|
+
|
|
340
|
+
if (!result) return
|
|
341
|
+
|
|
342
|
+
// metrics
|
|
343
|
+
const { usage } = result
|
|
344
|
+
this._tagger.tagMetrics(span, {
|
|
345
|
+
inputTokens: usage?.inputTokens?.total,
|
|
346
|
+
cacheWriteTokens: usage?.inputTokens?.cacheWrite ?? 0,
|
|
347
|
+
cacheReadTokens: usage?.inputTokens?.cacheRead ?? 0,
|
|
348
|
+
outputTokens: usage?.outputTokens?.total,
|
|
349
|
+
reasoningOutputTokens: usage?.outputTokens?.reasoning ?? 0,
|
|
350
|
+
})
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
setToolTags (span, ctx) {
|
|
354
|
+
const { event, result } = ctx
|
|
355
|
+
|
|
356
|
+
const { toolCall } = event
|
|
357
|
+
if (!toolCall) return
|
|
358
|
+
|
|
359
|
+
this._tagger.tagTextIO(span, toolCall.input, result?.output?.output)
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
module.exports = VercelAiTelemetryPlugin
|
|
@@ -25,7 +25,7 @@ class LLMObsPlugin extends TracingPlugin {
|
|
|
25
25
|
start (ctx) {
|
|
26
26
|
// even though llmobs span events won't be enqueued if llmobs is disabled
|
|
27
27
|
// we should avoid doing any computations here (these listeners aren't disabled)
|
|
28
|
-
const enabled = this._tracerConfig.llmobs.
|
|
28
|
+
const enabled = this._tracerConfig.llmobs.DD_LLMOBS_ENABLED
|
|
29
29
|
if (!enabled) return
|
|
30
30
|
|
|
31
31
|
const parentStore = llmobsStorage.getStore()
|
|
@@ -52,7 +52,7 @@ class LLMObsPlugin extends TracingPlugin {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
end (ctx) {
|
|
55
|
-
const enabled = this._tracerConfig.llmobs.
|
|
55
|
+
const enabled = this._tracerConfig.llmobs.DD_LLMOBS_ENABLED
|
|
56
56
|
if (!enabled) return
|
|
57
57
|
|
|
58
58
|
// only attempt to restore the context if the current span was an LLMObs span
|
|
@@ -67,7 +67,7 @@ class LLMObsPlugin extends TracingPlugin {
|
|
|
67
67
|
asyncEnd (ctx) {
|
|
68
68
|
// even though llmobs span events won't be enqueued if llmobs is disabled
|
|
69
69
|
// we should avoid doing any computations here (these listeners aren't disabled)
|
|
70
|
-
const enabled = this._tracerConfig.llmobs.
|
|
70
|
+
const enabled = this._tracerConfig.llmobs.DD_LLMOBS_ENABLED
|
|
71
71
|
if (!enabled) return
|
|
72
72
|
|
|
73
73
|
const apmStore = ctx.currentStore
|
|
@@ -85,7 +85,7 @@ class LLMObsPlugin extends TracingPlugin {
|
|
|
85
85
|
|
|
86
86
|
configure (config) {
|
|
87
87
|
// we do not want to enable any LLMObs plugins if it is disabled on the tracer
|
|
88
|
-
const llmobsEnabled = this._tracerConfig.llmobs.
|
|
88
|
+
const llmobsEnabled = this._tracerConfig.llmobs.DD_LLMOBS_ENABLED
|
|
89
89
|
if (llmobsEnabled === false) {
|
|
90
90
|
config = typeof config === 'boolean' ? false : { ...config, enabled: false } // override to false
|
|
91
91
|
}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const LLMObsPlugin = require('../base')
|
|
4
|
+
const { storage: llmobsStorage } = require('../../storage')
|
|
5
|
+
const { NAME, SESSION_ID } = require('../../constants/tags')
|
|
6
|
+
const { splitModel } = require('../../../../../datadog-plugin-claude-agent-sdk/src/util')
|
|
7
|
+
|
|
8
|
+
const subagentToolIds = new Set()
|
|
9
|
+
|
|
10
|
+
function normalizeToolOutputString (raw) {
|
|
11
|
+
const footerIndex = raw.search(/\s*agentId: [^\s]+ \(use SendMessage\b/)
|
|
12
|
+
if (footerIndex === -1) return raw
|
|
13
|
+
|
|
14
|
+
return raw.slice(0, footerIndex).trimEnd()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function getToolOutputText (raw) {
|
|
18
|
+
if (raw == null) return
|
|
19
|
+
|
|
20
|
+
if (Array.isArray(raw)) {
|
|
21
|
+
const output = []
|
|
22
|
+
for (const block of raw) {
|
|
23
|
+
const text = getToolOutputText(block)
|
|
24
|
+
if (text) output.push(text)
|
|
25
|
+
}
|
|
26
|
+
return output.join('\n') || undefined
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (raw.type === 'tool_result') return getToolOutputText(raw.content)
|
|
30
|
+
if (raw.type === 'text') return normalizeToolOutputString(raw.text)
|
|
31
|
+
if (raw.type === 'tool_reference') return raw.tool_name
|
|
32
|
+
if (raw.content !== undefined) return getToolOutputText(raw.content)
|
|
33
|
+
|
|
34
|
+
if (typeof raw === 'string') return normalizeToolOutputString(raw)
|
|
35
|
+
|
|
36
|
+
return JSON.stringify(raw)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function buildOutputMessages (chunks, llmStartIdx, llmEndIdx) {
|
|
40
|
+
let thinking = ''
|
|
41
|
+
let text = ''
|
|
42
|
+
const toolCalls = []
|
|
43
|
+
|
|
44
|
+
for (let i = llmStartIdx; i < llmEndIdx; i++) {
|
|
45
|
+
const c = chunks[i]
|
|
46
|
+
if (c.type !== 'assistant') continue
|
|
47
|
+
const block = c.message?.content?.[0]
|
|
48
|
+
if (block?.type === 'thinking') thinking += block.thinking ?? ''
|
|
49
|
+
else if (block?.type === 'text') text += block.text ?? ''
|
|
50
|
+
else if (block?.type === 'tool_use') {
|
|
51
|
+
toolCalls.push({ name: block.name, arguments: block.input ?? {}, toolId: block.id, type: block.type })
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const messages = []
|
|
56
|
+
if (thinking) messages.push({ role: 'thinking', content: thinking })
|
|
57
|
+
const msg = { role: 'assistant', content: text }
|
|
58
|
+
if (toolCalls.length) msg.toolCalls = toolCalls
|
|
59
|
+
messages.push(msg)
|
|
60
|
+
return messages
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
class QueryLLMObsPlugin extends LLMObsPlugin {
|
|
64
|
+
static integration = 'claude-agent-sdk'
|
|
65
|
+
static id = 'llmobs_claude_agent_sdk_query'
|
|
66
|
+
static prefix = 'tracing:orchestrion:@anthropic-ai/claude-agent-sdk:query'
|
|
67
|
+
|
|
68
|
+
getLLMObsSpanRegisterOptions (ctx) {
|
|
69
|
+
return { kind: 'agent' }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
start (ctx) {
|
|
73
|
+
super.start(ctx)
|
|
74
|
+
if (!this._tracerConfig.llmobs.DD_LLMOBS_ENABLED) return
|
|
75
|
+
const store = llmobsStorage.getStore()
|
|
76
|
+
const prev = ctx.runInContext ?? (fn => fn())
|
|
77
|
+
ctx.runInContext = fn => prev(() => llmobsStorage.run(store, fn))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
asyncEnd (ctx) {
|
|
81
|
+
if (!ctx.streamResolved) return
|
|
82
|
+
super.asyncEnd(ctx)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
setLLMObsTags (ctx) {
|
|
86
|
+
const span = ctx.currentStore?.span
|
|
87
|
+
if (!span) return
|
|
88
|
+
|
|
89
|
+
// post-populate session_id
|
|
90
|
+
if (ctx.session_id) this._tagger._setTag(span, SESSION_ID, ctx.session_id)
|
|
91
|
+
this._tagger.tagTextIO(span, ctx.arguments?.[0]?.prompt, ctx.output)
|
|
92
|
+
|
|
93
|
+
// metadata
|
|
94
|
+
const { cwd, permissionMode } = ctx
|
|
95
|
+
const metadata = {}
|
|
96
|
+
|
|
97
|
+
if (cwd) metadata.cwd = cwd
|
|
98
|
+
if (permissionMode) metadata.permissionMode = permissionMode
|
|
99
|
+
|
|
100
|
+
this._tagger.tagMetadata(span, metadata)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
class StepLlmObsPlugin extends LLMObsPlugin {
|
|
105
|
+
static integration = 'claude-agent-sdk'
|
|
106
|
+
static id = 'claude_agent_sdk_step_llmobs'
|
|
107
|
+
static system = 'claude-agent-sdk'
|
|
108
|
+
static prefix = 'tracing:apm:claude-agent-sdk:step'
|
|
109
|
+
|
|
110
|
+
getLLMObsSpanRegisterOptions (ctx) {
|
|
111
|
+
if (ctx.parentToolUseId) subagentToolIds.add(ctx.parentToolUseId)
|
|
112
|
+
return { kind: 'step', name: `step-${ctx.stepIndex}`, sessionId: ctx.sessionId }
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
end (ctx) {
|
|
116
|
+
super.end(ctx)
|
|
117
|
+
super.asyncEnd(ctx)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
setLLMObsTags (ctx) {
|
|
121
|
+
const span = ctx.currentStore?.span
|
|
122
|
+
if (!span) return
|
|
123
|
+
|
|
124
|
+
const { chunks, llmStartIdx, llmEndIdx, toolOutputs } = ctx
|
|
125
|
+
if (!chunks) return
|
|
126
|
+
|
|
127
|
+
const outputMessages = buildOutputMessages(chunks, llmStartIdx, llmEndIdx)
|
|
128
|
+
const thinking = outputMessages.find(m => m.role === 'thinking')?.content ?? ''
|
|
129
|
+
|
|
130
|
+
const output = toolOutputs?.length
|
|
131
|
+
? getToolOutputText(toolOutputs)
|
|
132
|
+
: outputMessages.find(m => m.role === 'assistant')?.content ?? ''
|
|
133
|
+
|
|
134
|
+
this._tagger.tagTextIO(span, thinking, output)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
class LlmLlmObsPlugin extends LLMObsPlugin {
|
|
139
|
+
static integration = 'claude-agent-sdk'
|
|
140
|
+
static id = 'claude_agent_sdk_llm_llmobs'
|
|
141
|
+
static system = 'claude-agent-sdk'
|
|
142
|
+
static prefix = 'tracing:apm:claude-agent-sdk:llm'
|
|
143
|
+
|
|
144
|
+
getLLMObsSpanRegisterOptions (ctx) {
|
|
145
|
+
const { modelName, modelProvider } = splitModel(ctx.model)
|
|
146
|
+
return { kind: 'llm', name: ctx.model, modelName, modelProvider, sessionId: ctx.sessionId }
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
end (ctx) {
|
|
150
|
+
super.end(ctx)
|
|
151
|
+
super.asyncEnd(ctx)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
setLLMObsTags (ctx) {
|
|
155
|
+
const span = ctx.currentStore?.span
|
|
156
|
+
if (!span) return
|
|
157
|
+
|
|
158
|
+
const { chunks, llmStartIdx, llmEndIdx, parentToolUseId, initialPrompt, usage } = ctx
|
|
159
|
+
|
|
160
|
+
if (chunks) {
|
|
161
|
+
const inputMessages = this.#buildInputMessages(chunks, llmStartIdx, parentToolUseId, initialPrompt)
|
|
162
|
+
const outputMessages = buildOutputMessages(chunks, llmStartIdx, llmEndIdx)
|
|
163
|
+
this._tagger.tagLLMIO(span, inputMessages, outputMessages)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (usage) {
|
|
167
|
+
const cacheWriteTokens = usage.cache_creation_input_tokens ?? 0
|
|
168
|
+
const cacheReadTokens = usage.cache_read_input_tokens ?? 0
|
|
169
|
+
const inputTokens = (usage.input_tokens ?? 0) + cacheWriteTokens + cacheReadTokens
|
|
170
|
+
const outputTokens = usage.output_tokens ?? 0
|
|
171
|
+
this._tagger.tagMetrics(span, {
|
|
172
|
+
input_tokens: inputTokens,
|
|
173
|
+
output_tokens: outputTokens,
|
|
174
|
+
cache_read_input_tokens: cacheReadTokens,
|
|
175
|
+
cache_write_input_tokens: cacheWriteTokens,
|
|
176
|
+
total_tokens: inputTokens + outputTokens,
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
#buildInputMessages (chunks, llmStartIdx, parentToolUseId, initialPrompt) {
|
|
182
|
+
const messages = []
|
|
183
|
+
if (initialPrompt) messages.push({ role: 'user', content: initialPrompt })
|
|
184
|
+
const seenIds = new Set()
|
|
185
|
+
|
|
186
|
+
for (let i = 0; i < llmStartIdx; i++) {
|
|
187
|
+
const c = chunks[i]
|
|
188
|
+
if (c.parent_tool_use_id !== parentToolUseId) continue
|
|
189
|
+
|
|
190
|
+
if (c.type === 'assistant') {
|
|
191
|
+
const msgId = c.message?.id
|
|
192
|
+
if (!msgId || seenIds.has(msgId)) continue
|
|
193
|
+
seenIds.add(msgId)
|
|
194
|
+
|
|
195
|
+
let thinking = ''
|
|
196
|
+
let text = ''
|
|
197
|
+
const toolCalls = []
|
|
198
|
+
for (let j = i; j < llmStartIdx; j++) {
|
|
199
|
+
const cc = chunks[j]
|
|
200
|
+
if (cc.type !== 'assistant' || cc.message?.id !== msgId) break
|
|
201
|
+
const block = cc.message?.content?.[0]
|
|
202
|
+
if (block?.type === 'thinking') thinking += block.thinking ?? ''
|
|
203
|
+
else if (block?.type === 'text') text += block.text ?? ''
|
|
204
|
+
else if (block?.type === 'tool_use') {
|
|
205
|
+
toolCalls.push({ name: block.name, arguments: block.input ?? {}, toolId: block.id, type: block.type })
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (thinking) messages.push({ role: 'thinking', content: thinking })
|
|
209
|
+
const msg = { role: 'assistant', content: text }
|
|
210
|
+
if (toolCalls.length) msg.toolCalls = toolCalls
|
|
211
|
+
messages.push(msg)
|
|
212
|
+
} else if (c.type === 'user') {
|
|
213
|
+
const content = c.message?.content
|
|
214
|
+
if (!content) continue
|
|
215
|
+
for (const block of content) {
|
|
216
|
+
if (block.type === 'text') {
|
|
217
|
+
messages.push({ role: 'user', content: block.text ?? '' })
|
|
218
|
+
} else if (block.type === 'tool_result') {
|
|
219
|
+
const text = getToolOutputText(block.content) ?? ''
|
|
220
|
+
messages.push({ role: 'tool', content: text })
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return messages
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
class ToolLlmObsPlugin extends LLMObsPlugin {
|
|
231
|
+
static integration = 'claude-agent-sdk'
|
|
232
|
+
static id = 'claude_agent_sdk_tool_llmobs'
|
|
233
|
+
static system = 'claude-agent-sdk'
|
|
234
|
+
static prefix = 'tracing:apm:claude-agent-sdk:tool'
|
|
235
|
+
|
|
236
|
+
getLLMObsSpanRegisterOptions (ctx) {
|
|
237
|
+
return { kind: 'tool', name: ctx.name, sessionId: ctx.sessionId }
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
end (ctx) {
|
|
241
|
+
super.end(ctx)
|
|
242
|
+
super.asyncEnd(ctx)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
setLLMObsTags (ctx) {
|
|
246
|
+
const span = ctx.currentStore?.span
|
|
247
|
+
if (!span) return
|
|
248
|
+
|
|
249
|
+
if (subagentToolIds.has(ctx.id)) {
|
|
250
|
+
subagentToolIds.delete(ctx.id)
|
|
251
|
+
const description = ctx.input?.description
|
|
252
|
+
this._tagger.changeKind(span, 'agent')
|
|
253
|
+
if (description) this._tagger._setTag(span, NAME, `${ctx.name} (${description})`)
|
|
254
|
+
const output = getToolOutputText(ctx.output)
|
|
255
|
+
this._tagger.tagTextIO(span, ctx.input?.prompt, output)
|
|
256
|
+
return
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const input = ctx.input ? JSON.stringify(ctx.input) : undefined
|
|
260
|
+
const output = getToolOutputText(ctx.output)
|
|
261
|
+
this._tagger.tagTextIO(span, input, output)
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
module.exports = [
|
|
266
|
+
QueryLLMObsPlugin,
|
|
267
|
+
StepLlmObsPlugin,
|
|
268
|
+
ToolLlmObsPlugin,
|
|
269
|
+
LlmLlmObsPlugin,
|
|
270
|
+
]
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
function splitModel (model) {
|
|
4
|
+
if (!model) return { modelName: undefined, modelProvider: 'anthropic' }
|
|
5
|
+
const idx = model.indexOf('/')
|
|
6
|
+
if (idx === -1) return { modelName: model, modelProvider: 'anthropic' }
|
|
7
|
+
return { modelName: model.slice(idx + 1), modelProvider: model.slice(0, idx) }
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
module.exports = { splitModel }
|
|
@@ -14,7 +14,7 @@ class PregelStreamLLMObsPlugin extends LLMObsPlugin {
|
|
|
14
14
|
getLLMObsSpanRegisterOptions (ctx) {
|
|
15
15
|
const name = ctx.self.name || 'LangGraph'
|
|
16
16
|
|
|
17
|
-
const enabled = this._tracerConfig.llmobs.
|
|
17
|
+
const enabled = this._tracerConfig.llmobs.DD_LLMOBS_ENABLED
|
|
18
18
|
if (!enabled) return
|
|
19
19
|
|
|
20
20
|
const span = ctx.currentStore?.span
|