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.
Files changed (164) hide show
  1. package/LICENSE-3rdparty.csv +1 -2
  2. package/README.md +13 -8
  3. package/ci/init.js +21 -2
  4. package/ci/vitest-no-worker-init-setup.mjs +430 -0
  5. package/ext/tags.js +2 -0
  6. package/index.d.ts +34 -1
  7. package/initialize.mjs +5 -6
  8. package/loader-hook.mjs +76 -55
  9. package/package.json +37 -34
  10. package/packages/datadog-instrumentations/src/ai.js +45 -0
  11. package/packages/datadog-instrumentations/src/apollo-server.js +5 -13
  12. package/packages/datadog-instrumentations/src/child_process.js +3 -3
  13. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +587 -0
  14. package/packages/datadog-instrumentations/src/cucumber.js +102 -43
  15. package/packages/datadog-instrumentations/src/cypress-config.js +11 -3
  16. package/packages/datadog-instrumentations/src/fastify.js +27 -8
  17. package/packages/datadog-instrumentations/src/fs.js +8 -6
  18. package/packages/datadog-instrumentations/src/graphql.js +26 -495
  19. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  20. package/packages/datadog-instrumentations/src/helpers/instrument.js +7 -0
  21. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +6 -6
  22. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/claude-agent-sdk.js +17 -0
  23. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +201 -0
  24. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -0
  25. package/packages/datadog-instrumentations/src/jest.js +713 -63
  26. package/packages/datadog-instrumentations/src/mocha/main.js +24 -3
  27. package/packages/datadog-instrumentations/src/mocha/utils.js +128 -22
  28. package/packages/datadog-instrumentations/src/mocha/worker.js +13 -0
  29. package/packages/datadog-instrumentations/src/mongodb.js +74 -0
  30. package/packages/datadog-instrumentations/src/mongoose.js +13 -2
  31. package/packages/datadog-instrumentations/src/playwright.js +13 -9
  32. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +1297 -0
  33. package/packages/datadog-instrumentations/src/vitest-main.js +1594 -0
  34. package/packages/datadog-instrumentations/src/vitest-util.js +254 -0
  35. package/packages/datadog-instrumentations/src/vitest-worker.js +823 -0
  36. package/packages/datadog-instrumentations/src/vitest.js +11 -1855
  37. package/packages/datadog-plugin-ai/src/index.js +1 -1
  38. package/packages/datadog-plugin-ai/src/tracing.js +66 -3
  39. package/packages/datadog-plugin-ai/src/utils.js +17 -4
  40. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/client.js +2 -2
  41. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +19 -10
  42. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +1 -0
  43. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +47 -11
  44. package/packages/datadog-plugin-child_process/src/index.js +13 -2
  45. package/packages/datadog-plugin-claude-agent-sdk/src/index.js +31 -0
  46. package/packages/datadog-plugin-claude-agent-sdk/src/tracing.js +107 -0
  47. package/packages/datadog-plugin-claude-agent-sdk/src/util.js +15 -0
  48. package/packages/datadog-plugin-cucumber/src/index.js +15 -24
  49. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +360 -14
  50. package/packages/datadog-plugin-cypress/src/index.js +47 -2
  51. package/packages/datadog-plugin-cypress/src/plugin.js +1 -0
  52. package/packages/datadog-plugin-cypress/src/support.js +45 -1
  53. package/packages/datadog-plugin-express/src/code_origin.js +1 -1
  54. package/packages/datadog-plugin-fastify/src/code_origin.js +1 -1
  55. package/packages/datadog-plugin-graphql/src/execute.js +639 -12
  56. package/packages/datadog-plugin-graphql/src/index.js +37 -9
  57. package/packages/datadog-plugin-graphql/src/parse.js +24 -4
  58. package/packages/datadog-plugin-graphql/src/utils.js +9 -2
  59. package/packages/datadog-plugin-graphql/src/validate.js +17 -6
  60. package/packages/datadog-plugin-http/src/index.js +6 -8
  61. package/packages/datadog-plugin-jest/src/index.js +31 -15
  62. package/packages/datadog-plugin-mocha/src/index.js +40 -15
  63. package/packages/datadog-plugin-mongodb-core/src/bulk-write.js +43 -0
  64. package/packages/datadog-plugin-mongodb-core/src/index.js +8 -443
  65. package/packages/datadog-plugin-mongodb-core/src/query.js +452 -0
  66. package/packages/datadog-plugin-openai/src/services.js +2 -2
  67. package/packages/datadog-plugin-playwright/src/index.js +4 -3
  68. package/packages/datadog-plugin-vitest/src/index.js +120 -72
  69. package/packages/datadog-shimmer/src/shimmer.js +37 -16
  70. package/packages/dd-trace/src/aiguard/index.js +9 -14
  71. package/packages/dd-trace/src/aiguard/integrations/index.js +34 -0
  72. package/packages/dd-trace/src/aiguard/integrations/openai.js +47 -33
  73. package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +42 -28
  74. package/packages/dd-trace/src/aiguard/sdk.js +2 -2
  75. package/packages/dd-trace/src/appsec/api_security/sampler.js +3 -3
  76. package/packages/dd-trace/src/appsec/channels.js +3 -1
  77. package/packages/dd-trace/src/appsec/downstream_requests.js +4 -4
  78. package/packages/dd-trace/src/appsec/graphql.js +14 -11
  79. package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +23 -23
  80. package/packages/dd-trace/src/appsec/iast/security-controls/index.js +2 -2
  81. package/packages/dd-trace/src/appsec/iast/telemetry/index.js +2 -1
  82. package/packages/dd-trace/src/appsec/index.js +10 -1
  83. package/packages/dd-trace/src/appsec/lambda.js +135 -0
  84. package/packages/dd-trace/src/appsec/reporter.js +49 -10
  85. package/packages/dd-trace/src/appsec/telemetry/index.js +1 -1
  86. package/packages/dd-trace/src/appsec/waf/index.js +16 -4
  87. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +4 -4
  88. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +26 -1
  89. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +115 -24
  90. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +21 -6
  91. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +5 -2
  92. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +1 -1
  93. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +1 -1
  94. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +6 -2
  95. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +1 -1
  96. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +130 -20
  97. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +2 -2
  98. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +17 -0
  99. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +47 -26
  100. package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +2 -2
  101. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +64 -53
  102. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +3 -3
  103. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +156 -0
  104. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +23 -5
  105. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +128 -0
  106. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +287 -0
  107. package/packages/dd-trace/src/config/defaults.js +3 -2
  108. package/packages/dd-trace/src/config/generated-config-types.d.ts +71 -57
  109. package/packages/dd-trace/src/config/helper.js +1 -0
  110. package/packages/dd-trace/src/config/index.js +35 -22
  111. package/packages/dd-trace/src/config/major-overrides.js +4 -2
  112. package/packages/dd-trace/src/config/remote_config.js +1 -1
  113. package/packages/dd-trace/src/config/supported-configurations.json +127 -56
  114. package/packages/dd-trace/src/constants.js +7 -0
  115. package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -2
  116. package/packages/dd-trace/src/datastreams/processor.js +11 -3
  117. package/packages/dd-trace/src/exporters/agent/index.js +1 -1
  118. package/packages/dd-trace/src/exporters/agentless/writer.js +4 -4
  119. package/packages/dd-trace/src/llmobs/index.js +1 -1
  120. package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +403 -0
  121. package/packages/dd-trace/src/llmobs/plugins/ai/index.js +8 -395
  122. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +27 -0
  123. package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +363 -0
  124. package/packages/dd-trace/src/llmobs/plugins/base.js +4 -4
  125. package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +270 -0
  126. package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/utils.js +10 -0
  127. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
  128. package/packages/dd-trace/src/llmobs/sdk.js +3 -3
  129. package/packages/dd-trace/src/llmobs/span_processor.js +1 -1
  130. package/packages/dd-trace/src/llmobs/tagger.js +3 -3
  131. package/packages/dd-trace/src/llmobs/writers/base.js +1 -1
  132. package/packages/dd-trace/src/opentelemetry/metrics/index.js +54 -5
  133. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +40 -0
  134. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +169 -0
  135. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +38 -39
  136. package/packages/dd-trace/src/opentelemetry/span.js +5 -0
  137. package/packages/dd-trace/src/opentelemetry/trace/index.js +4 -0
  138. package/packages/dd-trace/src/opentelemetry/tracer_provider.js +1 -1
  139. package/packages/dd-trace/src/opentracing/tracer.js +6 -1
  140. package/packages/dd-trace/src/plugin_manager.js +23 -7
  141. package/packages/dd-trace/src/plugins/ci_plugin.js +159 -10
  142. package/packages/dd-trace/src/plugins/index.js +2 -0
  143. package/packages/dd-trace/src/plugins/util/llm.js +6 -1
  144. package/packages/dd-trace/src/plugins/util/test.js +15 -9
  145. package/packages/dd-trace/src/profiling/exporter_cli.js +2 -2
  146. package/packages/dd-trace/src/profiling/exporters/agent.js +28 -3
  147. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +3 -3
  148. package/packages/dd-trace/src/profiling/profilers/wall.js +1 -1
  149. package/packages/dd-trace/src/proxy.js +8 -8
  150. package/packages/dd-trace/src/span_processor.js +5 -6
  151. package/packages/dd-trace/src/span_stats.js +96 -78
  152. package/packages/dd-trace/src/startup-log.js +2 -1
  153. package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
  154. package/packages/dd-trace/src/telemetry/endpoints.js +6 -4
  155. package/packages/dd-trace/src/telemetry/index.js +2 -2
  156. package/packages/dd-trace/src/telemetry/logs/index.js +1 -1
  157. package/packages/dd-trace/src/telemetry/send-data.js +8 -8
  158. package/packages/dd-trace/src/telemetry/session-propagation.js +1 -1
  159. package/packages/dd-trace/src/telemetry/telemetry.js +9 -9
  160. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  161. package/vendor/dist/protobufjs/index.js +1 -1
  162. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  163. package/vendor/dist/shell-quote/index.js +1 -1
  164. package/packages/datadog-plugin-graphql/src/resolve.js +0 -170
@@ -40,7 +40,7 @@ class AgentlessWriter extends BaseWriter {
40
40
  }
41
41
  }
42
42
 
43
- if (!getConfig().apiKey) {
43
+ if (!getConfig().DD_API_KEY) {
44
44
  this.#apiKeyMissing = true
45
45
  log.error('DD_API_KEY is required for agentless trace intake. Set DD_API_KEY. Traces will not be sent.')
46
46
  }
@@ -109,8 +109,8 @@ class AgentlessWriter extends BaseWriter {
109
109
  return
110
110
  }
111
111
 
112
- const apiKey = getConfig().apiKey
113
- if (!apiKey) {
112
+ const { DD_API_KEY } = getConfig()
113
+ if (!DD_API_KEY) {
114
114
  if (!this.#apiKeyMissing) {
115
115
  this.#apiKeyMissing = true
116
116
  log.error('DD_API_KEY is required for agentless trace intake. Set DD_API_KEY. Traces will not be sent.')
@@ -126,7 +126,7 @@ class AgentlessWriter extends BaseWriter {
126
126
  method: 'POST',
127
127
  headers: {
128
128
  'Content-Type': 'application/json',
129
- 'dd-api-key': apiKey,
129
+ 'dd-api-key': DD_API_KEY,
130
130
  'X-Datadog-Trace-Count': String(count),
131
131
  'Datadog-Meta-Lang': 'nodejs',
132
132
  'Datadog-Meta-Lang-Version': process.version,
@@ -75,7 +75,7 @@ function enable (config) {
75
75
  injectCh.subscribe(handleLLMObsInjection)
76
76
 
77
77
  setAgentStrategy(config, useAgentless => {
78
- if (useAgentless && !(config.apiKey && config.site)) {
78
+ if (useAgentless && !(config.DD_API_KEY && config.site)) {
79
79
  if (DD_MAJOR < 6 || !config?.startupLogs) {
80
80
  // eslint-disable-next-line no-console
81
81
  console.error(INCOMPATIBLE_INITIALIZATION)
@@ -0,0 +1,403 @@
1
+ 'use strict'
2
+
3
+ const { channel } = require('dc-polyfill')
4
+ const BaseLLMObsPlugin = require('../base')
5
+ const { getModelProvider } = require('../../../../../datadog-plugin-ai/src/utils')
6
+
7
+ const toolCreationCh = channel('tracing:orchestrion:ai:tool:start')
8
+ const setAttributesCh = channel('dd-trace:vercel-ai:span:setAttributes')
9
+
10
+ const { MODEL_NAME, MODEL_PROVIDER, NAME } = require('../../constants/tags')
11
+ const {
12
+ getSpanTags,
13
+ getOperation,
14
+ getUsage,
15
+ getJsonStringValue,
16
+ getModelMetadata,
17
+ getGenerationMetadata,
18
+ getToolNameFromTags,
19
+ getToolCallResultContent,
20
+ getLlmObsSpanName,
21
+ getTelemetryMetadata,
22
+ } = require('./util')
23
+
24
+ /**
25
+ * @typedef {Record<string, unknown> & { description?: string, id?: string }} AvailableToolArgs
26
+ */
27
+
28
+ /**
29
+ * @typedef {string | number | boolean | null | undefined | string[] | number[] | boolean[]} TagValue
30
+ * @typedef {Record<string, TagValue>} SpanTags
31
+ *
32
+ * @typedef {{ name?: string, description?: string }} ToolForModel
33
+ *
34
+ * @typedef {{ type: 'text' | 'reasoning' | 'redacted-reasoning', text?: string, data?: string }} TextPart
35
+ * @typedef {{ type: 'tool-call', toolName: string, toolCallId: string, args?: unknown, input?: unknown }} ToolCallPart
36
+ * @typedef {(
37
+ * { type: 'tool-result', toolCallId: string, output?: { type: string, value?: unknown }, result?: unknown } &
38
+ * Record<string, unknown>
39
+ * )} ToolResultPart
40
+ *
41
+ * @typedef {{
42
+ * role: 'system',
43
+ * content: string
44
+ * } | {
45
+ * role: 'user',
46
+ * content: TextPart[]
47
+ * } | {
48
+ * role: 'assistant',
49
+ * content: Array<TextPart | ToolCallPart>
50
+ * } | {
51
+ * role: 'tool',
52
+ * content: ToolResultPart[]
53
+ * }} AiSdkMessage
54
+ */
55
+
56
+ const SPAN_NAME_TO_KIND_MAPPING = {
57
+ // embeddings
58
+ embed: 'workflow',
59
+ embedMany: 'workflow',
60
+ doEmbed: 'embedding',
61
+ // object generation
62
+ generateObject: 'workflow',
63
+ streamObject: 'workflow',
64
+ // text generation
65
+ generateText: 'workflow',
66
+ streamText: 'workflow',
67
+ // llm operations
68
+ doGenerate: 'llm',
69
+ doStream: 'llm',
70
+ // tools
71
+ toolCall: 'tool',
72
+ }
73
+
74
+ class DdTelemetryPlugin extends BaseLLMObsPlugin {
75
+ static id = 'ai_llmobs_dd_telemetry'
76
+ static integration = 'ai'
77
+ static prefix = 'tracing:dd-trace:vercel-ai'
78
+
79
+ /**
80
+ * The available tools within the runtime scope of this integration.
81
+ * This essentially acts as a global registry for all tools made through the Vercel AI SDK.
82
+ * @type {Set<AvailableToolArgs>}
83
+ */
84
+ #availableTools
85
+
86
+ /**
87
+ * A mapping of tool call IDs to tool names.
88
+ * This is used to map the tool call ID to the tool name for the output message.
89
+ * @type {Record<string, string>}
90
+ */
91
+ #toolCallIdsToName
92
+
93
+ constructor (...args) {
94
+ super(...args)
95
+
96
+ this.#toolCallIdsToName = {}
97
+ this.#availableTools = new Set()
98
+ toolCreationCh.subscribe(ctx => {
99
+ const toolArgs = ctx.arguments
100
+ const tool = toolArgs[0] ?? {}
101
+ this.#availableTools.add(tool)
102
+ })
103
+
104
+ setAttributesCh.subscribe(({ ctx, attributes }) => {
105
+ Object.assign(ctx.attributes, attributes)
106
+ })
107
+ }
108
+
109
+ /**
110
+ * Does a best-effort attempt to find the right tool name for the given tool description.
111
+ * This is because the Vercel AI SDK does not tag tools by name properly, but
112
+ * rather by the index they were passed in. Tool names appear nowhere in the span tags.
113
+ *
114
+ * We use the tool description as the next best identifier for a tool.
115
+ *
116
+ * @param {string} toolName
117
+ * @param {string | undefined} toolDescription
118
+ * @returns {string | undefined}
119
+ */
120
+ findToolName (toolName, toolDescription) {
121
+ if (Number.isNaN(Number.parseInt(toolName))) return toolName
122
+
123
+ for (const availableTool of this.#availableTools) {
124
+ const description = availableTool.description
125
+ if (description === toolDescription && availableTool.id) {
126
+ return availableTool.id
127
+ }
128
+ }
129
+ }
130
+
131
+ /**
132
+ * @override
133
+ */
134
+ getLLMObsSpanRegisterOptions (ctx) {
135
+ const span = ctx.currentStore?.span
136
+ const operation = getOperation(span)
137
+ const kind = SPAN_NAME_TO_KIND_MAPPING[operation]
138
+ if (!kind) return
139
+
140
+ return { kind, name: getLlmObsSpanName(operation, ctx.attributes['ai.telemetry.functionId']) }
141
+ }
142
+
143
+ /**
144
+ * @override
145
+ */
146
+ setLLMObsTags (ctx) {
147
+ const span = ctx.currentStore?.span
148
+ if (!span) return
149
+
150
+ const operation = getOperation(span)
151
+ const kind = SPAN_NAME_TO_KIND_MAPPING[operation]
152
+ if (!kind) return
153
+
154
+ const tags = getSpanTags(ctx)
155
+
156
+ if (['embedding', 'llm'].includes(kind)) {
157
+ this._tagger._setTag(span, MODEL_NAME, tags['ai.model.id'])
158
+ this._tagger._setTag(span, MODEL_PROVIDER, getModelProvider(tags))
159
+ }
160
+
161
+ switch (operation) {
162
+ case 'embed':
163
+ case 'embedMany':
164
+ this.setEmbeddingWorkflowTags(span, tags)
165
+ break
166
+ case 'doEmbed':
167
+ this.setEmbeddingTags(span, tags)
168
+ break
169
+ case 'generateObject':
170
+ case 'streamObject':
171
+ this.setObjectGenerationTags(span, tags)
172
+ break
173
+ case 'generateText':
174
+ case 'streamText':
175
+ this.setTextGenerationTags(span, tags)
176
+ break
177
+ case 'doGenerate':
178
+ case 'doStream':
179
+ this.setLLMOperationTags(span, tags)
180
+ break
181
+ case 'toolCall':
182
+ this.setToolTags(span, tags)
183
+ break
184
+ default:
185
+ break
186
+ }
187
+ }
188
+
189
+ setEmbeddingWorkflowTags (span, tags) {
190
+ const inputs = tags['ai.value'] ?? tags['ai.values']
191
+ const parsedInputs = Array.isArray(inputs)
192
+ ? inputs.map(input => getJsonStringValue(input, ''))
193
+ : getJsonStringValue(inputs, '')
194
+
195
+ const embeddingsOutput = tags['ai.embedding'] ?? tags['ai.embeddings']
196
+ const isSingleEmbedding = !Array.isArray(embeddingsOutput)
197
+ const numberOfEmbeddings = isSingleEmbedding ? 1 : embeddingsOutput.length
198
+ const embeddingsLength = getJsonStringValue(isSingleEmbedding ? embeddingsOutput : embeddingsOutput?.[0], []).length
199
+ const output = `[${numberOfEmbeddings} embedding(s) returned with size ${embeddingsLength}]`
200
+
201
+ this._tagger.tagTextIO(span, parsedInputs, output)
202
+
203
+ const metadata = getGenerationMetadata(tags)
204
+ this._tagger.tagMetadata(span, metadata)
205
+ }
206
+
207
+ setEmbeddingTags (span, tags) {
208
+ const inputs = tags['ai.values']
209
+ if (!Array.isArray(inputs)) return
210
+
211
+ const parsedInputs = inputs.map(input => getJsonStringValue(input, ''))
212
+
213
+ const embeddingsOutput = tags['ai.embeddings']
214
+ const numberOfEmbeddings = embeddingsOutput?.length
215
+ const embeddingsLength = getJsonStringValue(embeddingsOutput?.[0], []).length
216
+ const output = `[${numberOfEmbeddings} embedding(s) returned with size ${embeddingsLength}]`
217
+
218
+ this._tagger.tagEmbeddingIO(span, parsedInputs, output)
219
+
220
+ const metadata = getTelemetryMetadata(tags)
221
+ this._tagger.tagMetadata(span, metadata)
222
+
223
+ const usage = tags['ai.usage.tokens']
224
+ this._tagger.tagMetrics(span, {
225
+ inputTokens: usage,
226
+ totalTokens: usage,
227
+ })
228
+ }
229
+
230
+ setObjectGenerationTags (span, tags) {
231
+ const promptInfo = getJsonStringValue(tags['ai.prompt'], {})
232
+ const lastUserPrompt =
233
+ promptInfo.prompt ??
234
+ promptInfo.messages.reverse().find(message => message.role === 'user')?.content
235
+ const prompt = Array.isArray(lastUserPrompt) ? lastUserPrompt.map(part => part.text ?? '').join('') : lastUserPrompt
236
+
237
+ const output = tags['ai.response.object']
238
+
239
+ this._tagger.tagTextIO(span, prompt, output)
240
+
241
+ const metadata = getGenerationMetadata(tags)
242
+ metadata.schema = getJsonStringValue(tags['ai.schema'], {})
243
+ this._tagger.tagMetadata(span, metadata)
244
+ }
245
+
246
+ setTextGenerationTags (span, tags) {
247
+ const promptInfo = getJsonStringValue(tags['ai.prompt'], {})
248
+ const lastUserPrompt =
249
+ promptInfo.prompt ??
250
+ promptInfo.messages.reverse().find(message => message.role === 'user')?.content
251
+ const prompt = Array.isArray(lastUserPrompt) ? lastUserPrompt.map(part => part.text ?? '').join('') : lastUserPrompt
252
+
253
+ const output = tags['ai.response.text']
254
+
255
+ this._tagger.tagTextIO(span, prompt, output)
256
+
257
+ const metadata = getGenerationMetadata(tags)
258
+ this._tagger.tagMetadata(span, metadata)
259
+ }
260
+
261
+ /**
262
+ * @param {import('../../../opentracing/span')} span
263
+ * @param {SpanTags} tags
264
+ */
265
+ setLLMOperationTags (span, tags) {
266
+ const toolsForModel = tags['ai.prompt.tools']?.map(getJsonStringValue)
267
+
268
+ const inputMessages = getJsonStringValue(tags['ai.prompt.messages'], [])
269
+ const parsedInputMessages = []
270
+ for (const message of inputMessages) {
271
+ const formattedMessages = this.formatMessage(message, toolsForModel)
272
+ parsedInputMessages.push(...formattedMessages)
273
+ }
274
+
275
+ const outputMessage = this.formatOutputMessage(tags, toolsForModel)
276
+
277
+ this._tagger.tagLLMIO(span, parsedInputMessages, outputMessage)
278
+
279
+ const metadata = getModelMetadata(tags)
280
+ this._tagger.tagMetadata(span, metadata)
281
+
282
+ const usage = getUsage(tags)
283
+ this._tagger.tagMetrics(span, usage)
284
+ }
285
+
286
+ setToolTags (span, tags) {
287
+ const toolCallId = tags['ai.toolCall.id']
288
+ const name = getToolNameFromTags(tags) ?? this.#toolCallIdsToName[toolCallId]
289
+ if (name) this._tagger._setTag(span, NAME, name)
290
+
291
+ const input = tags['ai.toolCall.args']
292
+ const output = tags['ai.toolCall.result']
293
+
294
+ this._tagger.tagTextIO(span, input, output)
295
+ }
296
+
297
+ formatOutputMessage (tags, toolsForModel) {
298
+ const outputMessageText = tags['ai.response.text'] ?? tags['ai.response.object']
299
+ const outputMessageToolCalls = getJsonStringValue(tags['ai.response.toolCalls'], [])
300
+
301
+ const formattedToolCalls = []
302
+ for (const toolCall of outputMessageToolCalls) {
303
+ const toolArgs = toolCall.args ?? toolCall.input
304
+ const toolCallArgs = typeof toolArgs === 'string' ? getJsonStringValue(toolArgs, {}) : toolArgs
305
+ const toolDescription = toolsForModel?.find(tool => toolCall.toolName === tool.name)?.description
306
+ const name = this.findToolName(toolCall.toolName, toolDescription)
307
+ this.#toolCallIdsToName[toolCall.toolCallId] = name
308
+
309
+ formattedToolCalls.push({
310
+ arguments: toolCallArgs,
311
+ name,
312
+ toolId: toolCall.toolCallId,
313
+ type: toolCall.toolCallType ?? 'function',
314
+ })
315
+ }
316
+
317
+ return {
318
+ role: 'assistant',
319
+ content: outputMessageText,
320
+ toolCalls: formattedToolCalls,
321
+ }
322
+ }
323
+
324
+ /**
325
+ * Returns a list of formatted messages from a message object.
326
+ * Most of these will just be one entry, but in the case of a "tool" role,
327
+ * it is possible to have multiple tool call results in a single message that we
328
+ * need to split into multiple messages.
329
+ *
330
+ * @param {AiSdkMessage} message
331
+ * @param {ToolForModel[] | null | undefined} toolsForModel
332
+ * @returns {Array<{role: string, content: string, toolId?: string,
333
+ * toolCalls?: Array<{arguments: string, name: string, toolId: string, type: string}>}>}
334
+ */
335
+ formatMessage (message, toolsForModel) {
336
+ const { role, content } = message
337
+
338
+ if (role === 'system') {
339
+ return [{ role, content }]
340
+ } else if (role === 'user') {
341
+ let finalContent = ''
342
+ for (const part of content) {
343
+ const { type } = part
344
+ if (type === 'text') {
345
+ finalContent += part.text
346
+ }
347
+ }
348
+
349
+ return [{ role, content: finalContent }]
350
+ } else if (role === 'assistant') {
351
+ const toolCalls = []
352
+ let finalContent = ''
353
+
354
+ for (const part of content) {
355
+ const { type } = part
356
+ // TODO(sabrenner): do we want to include reasoning?
357
+ if (['text', 'reasoning', 'redacted-reasoning'].includes(type)) {
358
+ finalContent += part.text ?? part.data
359
+ } else if (type === 'tool-call') {
360
+ const toolDescription = toolsForModel?.find(tool => part.toolName === tool.name)?.description
361
+ const name = this.findToolName(part.toolName, toolDescription)
362
+
363
+ toolCalls.push({
364
+ arguments: part.args ?? part.input,
365
+ name,
366
+ toolId: part.toolCallId,
367
+ type: 'function',
368
+ })
369
+ }
370
+ }
371
+
372
+ const finalMessage = {
373
+ role,
374
+ content: finalContent,
375
+ }
376
+
377
+ if (toolCalls.length) {
378
+ finalMessage.toolCalls = toolCalls.length ? toolCalls : undefined
379
+ }
380
+
381
+ return [finalMessage]
382
+ } else if (role === 'tool') {
383
+ const finalMessages = []
384
+ for (const part of content) {
385
+ if (part.type === 'tool-result') {
386
+ const safeResult = getToolCallResultContent(part)
387
+
388
+ finalMessages.push({
389
+ role,
390
+ content: safeResult,
391
+ toolId: part.toolCallId,
392
+ })
393
+ }
394
+ }
395
+
396
+ return finalMessages
397
+ }
398
+
399
+ return []
400
+ }
401
+ }
402
+
403
+ module.exports = DdTelemetryPlugin