dd-trace 5.79.0 → 5.81.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 (242) hide show
  1. package/LICENSE-3rdparty.csv +79 -87
  2. package/ext/tags.d.ts +1 -0
  3. package/ext/tags.js +1 -0
  4. package/index.d.ts +46 -39
  5. package/initialize.mjs +10 -10
  6. package/loader-hook.mjs +10 -3
  7. package/package.json +23 -40
  8. package/packages/datadog-core/src/storage.js +4 -4
  9. package/packages/datadog-esbuild/index.js +36 -19
  10. package/packages/datadog-esbuild/src/utils.js +5 -1
  11. package/packages/datadog-instrumentations/index.js +1 -0
  12. package/packages/datadog-instrumentations/src/anthropic.js +12 -0
  13. package/packages/datadog-instrumentations/src/aws-sdk.js +13 -2
  14. package/packages/datadog-instrumentations/src/azure-service-bus.js +43 -36
  15. package/packages/datadog-instrumentations/src/cucumber.js +2 -2
  16. package/packages/datadog-instrumentations/src/find-my-way.js +6 -5
  17. package/packages/datadog-instrumentations/src/google-genai.js +120 -0
  18. package/packages/datadog-instrumentations/src/graphql.js +20 -0
  19. package/packages/datadog-instrumentations/src/helpers/hook.js +1 -0
  20. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  21. package/packages/datadog-instrumentations/src/helpers/instrument.js +12 -1
  22. package/packages/datadog-instrumentations/src/helpers/register.js +6 -1
  23. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +27 -0
  24. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +152 -0
  25. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +5 -0
  26. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/langchain.js +237 -0
  27. package/packages/datadog-instrumentations/src/helpers/rewriter/loader.js +9 -0
  28. package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +11 -0
  29. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +139 -0
  30. package/packages/datadog-instrumentations/src/jest.js +1 -1
  31. package/packages/datadog-instrumentations/src/langchain.js +3 -109
  32. package/packages/datadog-instrumentations/src/mocha/main.js +1 -1
  33. package/packages/datadog-instrumentations/src/mysql2.js +1 -1
  34. package/packages/datadog-instrumentations/src/playwright.js +65 -16
  35. package/packages/datadog-instrumentations/src/router.js +1 -1
  36. package/packages/datadog-instrumentations/src/selenium.js +3 -1
  37. package/packages/datadog-instrumentations/src/ws.js +35 -17
  38. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +3 -2
  39. package/packages/datadog-plugin-azure-service-bus/src/producer.js +14 -5
  40. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +1 -1
  41. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +23 -2
  42. package/packages/datadog-plugin-cypress/src/plugin.js +1 -1
  43. package/packages/datadog-plugin-cypress/src/support.js +73 -31
  44. package/packages/datadog-plugin-google-genai/src/index.js +17 -0
  45. package/packages/datadog-plugin-google-genai/src/tracing.js +41 -0
  46. package/packages/datadog-plugin-graphql/src/tools/transforms.js +5 -4
  47. package/packages/datadog-plugin-jest/src/util.js +4 -3
  48. package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -1
  49. package/packages/datadog-plugin-kafkajs/src/producer.js +3 -1
  50. package/packages/datadog-plugin-langchain/src/tracing.js +7 -3
  51. package/packages/datadog-plugin-next/src/index.js +11 -3
  52. package/packages/datadog-plugin-openai/src/stream-helpers.js +1 -1
  53. package/packages/datadog-shimmer/src/shimmer.js +2 -2
  54. package/packages/dd-trace/src/aiguard/sdk.js +29 -14
  55. package/packages/dd-trace/src/appsec/api_security_sampler.js +1 -1
  56. package/packages/dd-trace/src/appsec/iast/overhead-controller.js +1 -1
  57. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-esm.mjs +1 -1
  58. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +1 -2
  59. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +1 -1
  60. package/packages/dd-trace/src/appsec/reporter.js +0 -4
  61. package/packages/dd-trace/src/baggage.js +11 -0
  62. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +4 -8
  63. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +1 -1
  64. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +4 -2
  65. package/packages/dd-trace/src/config.js +81 -7
  66. package/packages/dd-trace/src/config_defaults.js +15 -2
  67. package/packages/dd-trace/src/datastreams/encoding.js +23 -6
  68. package/packages/dd-trace/src/datastreams/pathway.js +40 -1
  69. package/packages/dd-trace/src/datastreams/processor.js +1 -1
  70. package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +1 -1
  71. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +15 -5
  72. package/packages/dd-trace/src/debugger/devtools_client/condition.js +1 -1
  73. package/packages/dd-trace/src/debugger/devtools_client/config.js +2 -0
  74. package/packages/dd-trace/src/debugger/devtools_client/index.js +30 -15
  75. package/packages/dd-trace/src/debugger/devtools_client/inspector_promises_polyfill.js +2 -0
  76. package/packages/dd-trace/src/debugger/devtools_client/json-buffer.js +24 -18
  77. package/packages/dd-trace/src/debugger/devtools_client/send.js +18 -8
  78. package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +103 -15
  79. package/packages/dd-trace/src/debugger/devtools_client/snapshot/constants.js +25 -0
  80. package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +56 -25
  81. package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +64 -23
  82. package/packages/dd-trace/src/debugger/devtools_client/snapshot/symbols.js +3 -1
  83. package/packages/dd-trace/src/debugger/devtools_client/snapshot-pruner.js +404 -0
  84. package/packages/dd-trace/src/debugger/devtools_client/source-maps.js +1 -1
  85. package/packages/dd-trace/src/debugger/devtools_client/state.js +7 -2
  86. package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -1
  87. package/packages/dd-trace/src/debugger/index.js +1 -1
  88. package/packages/dd-trace/src/encode/0.4.js +3 -3
  89. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +2 -2
  90. package/packages/dd-trace/src/encode/span-stats.js +7 -1
  91. package/packages/dd-trace/src/exporters/agent/writer.js +6 -13
  92. package/packages/dd-trace/src/histogram.js +1 -1
  93. package/packages/dd-trace/src/id.js +60 -0
  94. package/packages/dd-trace/src/lambda/runtime/ritm.js +2 -3
  95. package/packages/dd-trace/src/llmobs/constants/tags.js +1 -0
  96. package/packages/dd-trace/src/llmobs/index.js +5 -5
  97. package/packages/dd-trace/src/llmobs/noop.js +6 -0
  98. package/packages/dd-trace/src/llmobs/plugins/ai/index.js +1 -0
  99. package/packages/dd-trace/src/llmobs/plugins/genai/index.js +104 -0
  100. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +486 -0
  101. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +2 -2
  102. package/packages/dd-trace/src/llmobs/plugins/{openai.js → openai/index.js} +87 -39
  103. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +114 -0
  104. package/packages/dd-trace/src/llmobs/sdk.js +10 -1
  105. package/packages/dd-trace/src/llmobs/span_processor.js +11 -6
  106. package/packages/dd-trace/src/llmobs/tagger.js +35 -17
  107. package/packages/dd-trace/src/msgpack/chunk.js +2 -2
  108. package/packages/dd-trace/src/msgpack/encoder.js +2 -3
  109. package/packages/dd-trace/src/msgpack/index.js +2 -2
  110. package/packages/dd-trace/src/openfeature/flagging_provider.js +5 -3
  111. package/packages/dd-trace/src/opentelemetry/logs/index.js +3 -3
  112. package/packages/dd-trace/src/opentelemetry/logs/logger.js +14 -8
  113. package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +6 -4
  114. package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +9 -17
  115. package/packages/dd-trace/src/opentelemetry/metrics/constants.js +34 -0
  116. package/packages/dd-trace/src/opentelemetry/metrics/index.js +81 -0
  117. package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +225 -0
  118. package/packages/dd-trace/src/opentelemetry/metrics/meter.js +171 -0
  119. package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +54 -0
  120. package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +62 -0
  121. package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +251 -0
  122. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +532 -0
  123. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +10 -18
  124. package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +36 -22
  125. package/packages/dd-trace/src/opentelemetry/otlp/protobuf_loader.js +2 -2
  126. package/packages/dd-trace/src/opentelemetry/span.js +1 -1
  127. package/packages/dd-trace/src/opentelemetry/tracer.js +1 -1
  128. package/packages/dd-trace/src/opentelemetry/tracer_provider.js +1 -1
  129. package/packages/dd-trace/src/payload-tagging/index.js +2 -2
  130. package/packages/dd-trace/src/plugin_manager.js +4 -2
  131. package/packages/dd-trace/src/plugins/database.js +1 -0
  132. package/packages/dd-trace/src/plugins/index.js +1 -0
  133. package/packages/dd-trace/src/plugins/plugin.js +7 -9
  134. package/packages/dd-trace/src/plugins/util/test.js +3 -3
  135. package/packages/dd-trace/src/plugins/util/url.js +119 -1
  136. package/packages/dd-trace/src/plugins/util/web.js +10 -41
  137. package/packages/dd-trace/src/process-tags/index.js +81 -0
  138. package/packages/dd-trace/src/profiling/config.js +1 -1
  139. package/packages/dd-trace/src/profiling/exporter_cli.js +7 -6
  140. package/packages/dd-trace/src/profiling/exporters/agent.js +1 -1
  141. package/packages/dd-trace/src/profiling/profilers/events.js +10 -1
  142. package/packages/dd-trace/src/proxy.js +5 -0
  143. package/packages/dd-trace/src/rate_limiter.js +1 -1
  144. package/packages/dd-trace/src/remote_config/manager.js +1 -1
  145. package/packages/dd-trace/src/require-package-json.js +1 -1
  146. package/packages/dd-trace/src/ritm.js +1 -1
  147. package/packages/dd-trace/src/service-naming/index.js +31 -4
  148. package/packages/dd-trace/src/service-naming/schemas/v0/web.js +4 -0
  149. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +4 -0
  150. package/packages/dd-trace/src/{format.js → span_format.js} +9 -4
  151. package/packages/dd-trace/src/span_processor.js +16 -11
  152. package/packages/dd-trace/src/span_stats.js +15 -4
  153. package/packages/dd-trace/src/spanleak.js +1 -1
  154. package/packages/dd-trace/src/supported-configurations.json +13 -0
  155. package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
  156. package/packages/dd-trace/src/telemetry/telemetry.js +11 -2
  157. package/vendor/dist/@datadog/sketches-js/LICENSE +39 -0
  158. package/vendor/dist/@datadog/sketches-js/index.js +1 -0
  159. package/vendor/dist/@datadog/source-map/LICENSE +28 -0
  160. package/vendor/dist/@datadog/source-map/index.js +1 -0
  161. package/vendor/dist/@isaacs/ttlcache/LICENSE +55 -0
  162. package/vendor/dist/@isaacs/ttlcache/index.js +1 -0
  163. package/vendor/dist/@opentelemetry/core/LICENSE +201 -0
  164. package/vendor/dist/@opentelemetry/core/index.js +1 -0
  165. package/vendor/dist/@opentelemetry/resources/LICENSE +201 -0
  166. package/vendor/dist/@opentelemetry/resources/index.js +1 -0
  167. package/vendor/dist/astring/LICENSE +19 -0
  168. package/vendor/dist/astring/index.js +1 -0
  169. package/vendor/dist/crypto-randomuuid/index.js +1 -0
  170. package/vendor/dist/escape-string-regexp/LICENSE +9 -0
  171. package/vendor/dist/escape-string-regexp/index.js +1 -0
  172. package/vendor/dist/esquery/LICENSE +24 -0
  173. package/vendor/dist/esquery/index.js +1 -0
  174. package/vendor/dist/ignore/LICENSE +21 -0
  175. package/vendor/dist/ignore/index.js +1 -0
  176. package/vendor/dist/istanbul-lib-coverage/LICENSE +24 -0
  177. package/vendor/dist/istanbul-lib-coverage/index.js +1 -0
  178. package/vendor/dist/jest-docblock/LICENSE +21 -0
  179. package/vendor/dist/jest-docblock/index.js +1 -0
  180. package/vendor/dist/jsonpath-plus/LICENSE +22 -0
  181. package/vendor/dist/jsonpath-plus/index.js +1 -0
  182. package/vendor/dist/limiter/LICENSE +19 -0
  183. package/vendor/dist/limiter/index.js +1 -0
  184. package/vendor/dist/lodash.sortby/LICENSE +47 -0
  185. package/vendor/dist/lodash.sortby/index.js +1 -0
  186. package/vendor/dist/lru-cache/LICENSE +15 -0
  187. package/vendor/dist/lru-cache/index.js +1 -0
  188. package/vendor/dist/meriyah/LICENSE +7 -0
  189. package/vendor/dist/meriyah/index.js +1 -0
  190. package/vendor/dist/module-details-from-path/LICENSE +21 -0
  191. package/vendor/dist/module-details-from-path/index.js +1 -0
  192. package/vendor/dist/mutexify/promise/LICENSE +21 -0
  193. package/vendor/dist/mutexify/promise/index.js +1 -0
  194. package/vendor/dist/opentracing/LICENSE +201 -0
  195. package/vendor/dist/opentracing/binary_carrier.d.ts +11 -0
  196. package/vendor/dist/opentracing/constants.d.ts +61 -0
  197. package/vendor/dist/opentracing/examples/demo/demo.d.ts +2 -0
  198. package/vendor/dist/opentracing/ext/tags.d.ts +90 -0
  199. package/vendor/dist/opentracing/functions.d.ts +20 -0
  200. package/vendor/dist/opentracing/global_tracer.d.ts +14 -0
  201. package/vendor/dist/opentracing/index.d.ts +12 -0
  202. package/vendor/dist/opentracing/index.js +1 -0
  203. package/vendor/dist/opentracing/mock_tracer/index.d.ts +5 -0
  204. package/vendor/dist/opentracing/mock_tracer/mock_context.d.ts +13 -0
  205. package/vendor/dist/opentracing/mock_tracer/mock_report.d.ts +16 -0
  206. package/vendor/dist/opentracing/mock_tracer/mock_span.d.ts +50 -0
  207. package/vendor/dist/opentracing/mock_tracer/mock_tracer.d.ts +26 -0
  208. package/vendor/dist/opentracing/noop.d.ts +8 -0
  209. package/vendor/dist/opentracing/reference.d.ts +33 -0
  210. package/vendor/dist/opentracing/span.d.ts +147 -0
  211. package/vendor/dist/opentracing/span_context.d.ts +26 -0
  212. package/vendor/dist/opentracing/test/api_compatibility.d.ts +16 -0
  213. package/vendor/dist/opentracing/test/mocktracer_implemenation.d.ts +3 -0
  214. package/vendor/dist/opentracing/test/noop_implementation.d.ts +4 -0
  215. package/vendor/dist/opentracing/test/opentracing_api.d.ts +3 -0
  216. package/vendor/dist/opentracing/test/unittest.d.ts +2 -0
  217. package/vendor/dist/opentracing/tracer.d.ts +127 -0
  218. package/vendor/dist/path-to-regexp/LICENSE +21 -0
  219. package/vendor/dist/path-to-regexp/index.js +1 -0
  220. package/vendor/dist/pprof-format/LICENSE +8 -0
  221. package/vendor/dist/pprof-format/index.js +1 -0
  222. package/vendor/dist/protobufjs/LICENSE +39 -0
  223. package/vendor/dist/protobufjs/index.js +1 -0
  224. package/vendor/dist/protobufjs/minimal/LICENSE +39 -0
  225. package/vendor/dist/protobufjs/minimal/index.js +1 -0
  226. package/vendor/dist/retry/LICENSE +21 -0
  227. package/vendor/dist/retry/index.js +1 -0
  228. package/vendor/dist/rfdc/LICENSE +15 -0
  229. package/vendor/dist/rfdc/index.js +1 -0
  230. package/vendor/dist/semifies/LICENSE +201 -0
  231. package/vendor/dist/semifies/index.js +1 -0
  232. package/vendor/dist/shell-quote/LICENSE +24 -0
  233. package/vendor/dist/shell-quote/index.js +1 -0
  234. package/vendor/dist/source-map/LICENSE +28 -0
  235. package/vendor/dist/source-map/index.js +1 -0
  236. package/vendor/dist/source-map/lib/util/LICENSE +28 -0
  237. package/vendor/dist/source-map/lib/util/index.js +1 -0
  238. package/vendor/dist/source-map/mappings.wasm +0 -0
  239. package/vendor/dist/tlhunter-sorted-set/LICENSE +21 -0
  240. package/vendor/dist/tlhunter-sorted-set/index.js +1 -0
  241. package/vendor/dist/ttl-set/LICENSE +21 -0
  242. package/vendor/dist/ttl-set/index.js +1 -0
@@ -0,0 +1,486 @@
1
+ 'use strict'
2
+
3
+ // Constants for role mapping
4
+ const ROLES = {
5
+ MODEL: 'model',
6
+ ASSISTANT: 'assistant',
7
+ USER: 'user',
8
+ REASONING: 'reasoning'
9
+ }
10
+
11
+ /**
12
+ * Get the operation type from the method name
13
+ * @param {string} methodName
14
+ * @returns {'embedding' | 'llm'}
15
+ */
16
+ function getOperation (methodName) {
17
+ return methodName.includes('embed') ? 'embedding' : 'llm'
18
+ }
19
+
20
+ /**
21
+ * Extract text parts from an array of parts
22
+ * @param {Array<{text?: string}>} parts
23
+ * @returns {string[]}
24
+ */
25
+ function extractTextParts (parts) {
26
+ return parts
27
+ .filter(part => part.text)
28
+ .map(part => part.text)
29
+ }
30
+
31
+ /**
32
+ * Group parts by role (reasoning vs assistant)
33
+ * @param {Array<{text?: string, thought?: boolean}>} parts
34
+ * @returns {{reasoning: string, assistant: string}}
35
+ */
36
+ function groupPartsByRole (parts) {
37
+ const grouped = {
38
+ reasoning: '',
39
+ assistant: ''
40
+ }
41
+
42
+ for (const part of parts) {
43
+ if (!part.text) continue
44
+
45
+ if (part.thought === true) {
46
+ grouped.reasoning += part.text
47
+ } else {
48
+ grouped.assistant += part.text
49
+ }
50
+ }
51
+
52
+ return grouped
53
+ }
54
+
55
+ /**
56
+ * Check if parts contain thought/reasoning content
57
+ * @param {Array<{thought?: boolean}>} parts
58
+ * @returns {boolean}
59
+ */
60
+ function hasThoughtParts (parts) {
61
+ return parts.some(part => part.thought === true)
62
+ }
63
+
64
+ /**
65
+ * Determine the role from a candidate and its parts
66
+ * @param {object} candidate
67
+ * @param {Array<{thought?: boolean}>} parts
68
+ * @returns {string}
69
+ */
70
+ function determineRole (candidate, parts = []) {
71
+ // Check parts for thought indicators
72
+ if (hasThoughtParts(parts)) {
73
+ return ROLES.REASONING
74
+ }
75
+
76
+ // Extract role from various possible locations
77
+ const rawRole = candidate.role ||
78
+ candidate.content?.role ||
79
+ candidate[0]?.content?.role
80
+
81
+ return normalizeRole(rawRole)
82
+ }
83
+
84
+ /**
85
+ * Normalize role to standard values
86
+ * @param {string} role
87
+ * @returns {string}
88
+ */
89
+ function normalizeRole (role) {
90
+ if (role === ROLES.MODEL) return ROLES.ASSISTANT
91
+ if (role === ROLES.ASSISTANT) return ROLES.ASSISTANT
92
+ if (role === ROLES.USER) return ROLES.USER
93
+ if (role === ROLES.REASONING) return ROLES.REASONING
94
+ return ROLES.USER // default
95
+ }
96
+
97
+ /**
98
+ * Extract metrics from response
99
+ * @param {object} response
100
+ * @returns {object}
101
+ */
102
+ function extractMetrics (response) {
103
+ const metrics = {}
104
+ const tokenUsage = response.usageMetadata
105
+
106
+ if (!tokenUsage) return metrics
107
+
108
+ if (tokenUsage.promptTokenCount) {
109
+ metrics.inputTokens = tokenUsage.promptTokenCount
110
+ }
111
+
112
+ if (tokenUsage.candidatesTokenCount) {
113
+ metrics.outputTokens = tokenUsage.candidatesTokenCount
114
+ }
115
+
116
+ const totalTokens = tokenUsage.totalTokenCount ||
117
+ (tokenUsage.promptTokenCount || 0) + (tokenUsage.candidatesTokenCount || 0)
118
+ if (totalTokens) {
119
+ metrics.totalTokens = totalTokens
120
+ }
121
+
122
+ return metrics
123
+ }
124
+
125
+ /**
126
+ * Extract metadata from config
127
+ * @param {object} config
128
+ * @returns {object}
129
+ */
130
+ function extractMetadata (config) {
131
+ if (!config) return {}
132
+
133
+ const fieldMap = {
134
+ temperature: 'temperature',
135
+ top_p: 'topP',
136
+ top_k: 'topK',
137
+ candidate_count: 'candidateCount',
138
+ max_output_tokens: 'maxOutputTokens',
139
+ stop_sequences: 'stopSequences',
140
+ response_logprobs: 'responseLogprobs',
141
+ logprobs: 'logprobs',
142
+ presence_penalty: 'presencePenalty',
143
+ frequency_penalty: 'frequencyPenalty',
144
+ seed: 'seed',
145
+ response_mime_type: 'responseMimeType',
146
+ safety_settings: 'safetySettings',
147
+ automatic_function_calling: 'automaticFunctionCalling'
148
+ }
149
+
150
+ const metadata = {}
151
+ for (const [metadataKey, configKey] of Object.entries(fieldMap)) {
152
+ metadata[metadataKey] = config[configKey] ?? null
153
+ }
154
+
155
+ return metadata
156
+ }
157
+
158
+ /**
159
+ * Format function call message
160
+ * @param {Array} parts
161
+ * @param {Array} functionCalls
162
+ * @param {string} role
163
+ * @returns {object}
164
+ */
165
+ function formatFunctionCallMessage (parts, functionCalls, role) {
166
+ const toolCalls = functionCalls.map(part => ({
167
+ name: part.functionCall.name,
168
+ arguments: part.functionCall.args,
169
+ toolId: part.functionCall.id || '',
170
+ type: 'function_call'
171
+ }))
172
+
173
+ const textParts = extractTextParts(parts)
174
+ const content = textParts.length > 0 ? textParts.join('\n') : undefined
175
+ const message = { role, toolCalls }
176
+
177
+ if (content) message.content = content
178
+
179
+ return message
180
+ }
181
+
182
+ /**
183
+ * Format function response message
184
+ * @param {Array} functionResponses
185
+ * @param {string} role
186
+ * @returns {object}
187
+ */
188
+ function formatFunctionResponseMessage (functionResponses, role) {
189
+ const toolResults = functionResponses.map(part => ({
190
+ name: part.functionResponse.name,
191
+ result: JSON.stringify(part.functionResponse.response),
192
+ toolId: part.functionResponse.id,
193
+ type: 'function_response'
194
+ }))
195
+
196
+ return {
197
+ role,
198
+ toolResults
199
+ }
200
+ }
201
+
202
+ /**
203
+ * Aggregate streaming chunks into a single response
204
+ * @param {Array} chunks
205
+ * @returns {object}
206
+ */
207
+ function aggregateStreamingChunks (chunks) {
208
+ const response = { candidates: [] }
209
+
210
+ for (const chunk of chunks) {
211
+ if (chunk.candidates) {
212
+ // Flatten candidates array
213
+ response.candidates.push(...chunk.candidates)
214
+ }
215
+ if (chunk.usageMetadata) {
216
+ response.usageMetadata = chunk.usageMetadata
217
+ }
218
+ }
219
+
220
+ return response
221
+ }
222
+
223
+ /**
224
+ * Format a content object into a message
225
+ * @param {object} content
226
+ * @returns {object}
227
+ */
228
+ function formatContentObject (content) {
229
+ const parts = content.parts || []
230
+ const role = determineRole(content, parts)
231
+
232
+ // Check if this is a thought/reasoning part
233
+ if (hasThoughtParts(parts)) {
234
+ return {
235
+ role: ROLES.REASONING,
236
+ content: extractTextParts(parts).join('\n')
237
+ }
238
+ }
239
+
240
+ // Check for function calls
241
+ const functionCalls = parts.filter(part => part.functionCall)
242
+ if (functionCalls.length > 0) {
243
+ return formatFunctionCallMessage(parts, functionCalls, role)
244
+ }
245
+
246
+ // Check for function responses
247
+ const functionResponses = parts.filter(part => part.functionResponse)
248
+ if (functionResponses.length > 0) {
249
+ return formatFunctionResponseMessage(functionResponses, role)
250
+ }
251
+
252
+ // Regular text content
253
+ return {
254
+ role,
255
+ content: extractTextParts(parts).join('\n')
256
+ }
257
+ }
258
+
259
+ /**
260
+ * Format input messages from contents
261
+ * @param {*} contents
262
+ * @returns {Array}
263
+ */
264
+ function formatInputMessages (contents) {
265
+ if (!contents) return []
266
+
267
+ const contentArray = Array.isArray(contents) ? contents : [contents]
268
+ const messages = []
269
+
270
+ for (const content of contentArray) {
271
+ if (typeof content === 'string') {
272
+ messages.push({ role: ROLES.USER, content })
273
+ } else if (content.text) {
274
+ messages.push({ role: ROLES.USER, content: content.text })
275
+ } else if (content.parts) {
276
+ const message = formatContentObject(content)
277
+ if (message) messages.push(message)
278
+ } else {
279
+ messages.push({ role: ROLES.USER, content: JSON.stringify(content) })
280
+ }
281
+ }
282
+
283
+ return messages
284
+ }
285
+
286
+ /**
287
+ * Format embedding input from contents
288
+ * @param {*} contents
289
+ * @returns {Array}
290
+ */
291
+ function formatEmbeddingInput (contents) {
292
+ if (!contents) return []
293
+
294
+ const contentArray = Array.isArray(contents) ? contents : [contents]
295
+ const documents = []
296
+
297
+ for (const content of contentArray) {
298
+ if (typeof content === 'string') {
299
+ documents.push({ text: content })
300
+ } else if (content.text) {
301
+ documents.push({ text: content.text })
302
+ } else if (content.parts) {
303
+ for (const part of content.parts) {
304
+ if (typeof part === 'string') {
305
+ documents.push({ text: part })
306
+ } else if (part.text) {
307
+ documents.push({ text: part.text })
308
+ }
309
+ }
310
+ }
311
+ }
312
+
313
+ return documents
314
+ }
315
+
316
+ /**
317
+ * Format a non-streaming candidate into messages
318
+ * @param {object} candidate
319
+ * @returns {Array}
320
+ */
321
+ function formatNonStreamingCandidate (candidate) {
322
+ const messages = []
323
+ const content = Array.isArray(candidate) ? candidate[0].content : candidate.content
324
+
325
+ if (!content?.parts) return messages
326
+
327
+ const { parts } = content
328
+
329
+ // Check for function calls
330
+ const functionCalls = parts.filter(part => part.functionCall)
331
+ if (functionCalls.length > 0) {
332
+ messages.push(formatFunctionCallMessage(parts, functionCalls, ROLES.ASSISTANT))
333
+ return messages
334
+ }
335
+
336
+ // Check for executable code
337
+ const executableCode = parts.find(part => part.executableCode)
338
+ if (executableCode) {
339
+ messages.push({
340
+ role: ROLES.ASSISTANT,
341
+ content: JSON.stringify({
342
+ language: executableCode.executableCode.language,
343
+ code: executableCode.executableCode.code
344
+ })
345
+ })
346
+ return messages
347
+ }
348
+
349
+ // Check for code execution result
350
+ const codeExecutionResult = parts.find(part => part.codeExecutionResult)
351
+ if (codeExecutionResult) {
352
+ messages.push({
353
+ role: ROLES.ASSISTANT,
354
+ content: JSON.stringify({
355
+ outcome: codeExecutionResult.codeExecutionResult.outcome,
356
+ output: codeExecutionResult.codeExecutionResult.output
357
+ })
358
+ })
359
+ return messages
360
+ }
361
+
362
+ // Regular text content - may contain both reasoning and assistant parts
363
+ const partsByRole = groupPartsByRole(parts)
364
+
365
+ if (partsByRole.reasoning) {
366
+ messages.push({
367
+ role: ROLES.REASONING,
368
+ content: partsByRole.reasoning
369
+ })
370
+ }
371
+
372
+ if (partsByRole.assistant) {
373
+ messages.push({
374
+ role: ROLES.ASSISTANT,
375
+ content: partsByRole.assistant
376
+ })
377
+ }
378
+
379
+ return messages
380
+ }
381
+
382
+ /**
383
+ * Format streaming output from response
384
+ * @param {object} response
385
+ * @returns {Array}
386
+ */
387
+ function formatStreamingOutput (response) {
388
+ const messages = []
389
+ const messagesByRole = new Map()
390
+
391
+ for (const candidate of response.candidates) {
392
+ const content = Array.isArray(candidate) ? candidate[0].content : candidate.content
393
+ if (!content?.parts) continue
394
+
395
+ // Skip special cases in streaming (handle them as non-streaming)
396
+ if (content.parts.some(part => part.functionCall ||
397
+ part.executableCode ||
398
+ part.codeExecutionResult)) {
399
+ messages.push(...formatNonStreamingCandidate(candidate))
400
+ continue
401
+ }
402
+
403
+ // Accumulate text parts by role
404
+ const partsByRole = groupPartsByRole(content.parts)
405
+
406
+ for (const [partRole, textContent] of Object.entries(partsByRole)) {
407
+ if (!textContent) continue
408
+
409
+ if (messagesByRole.has(partRole)) {
410
+ const index = messagesByRole.get(partRole)
411
+ messages[index].content += textContent
412
+ } else {
413
+ const messageIndex = messages.length
414
+ messages.push({ role: partRole, content: textContent })
415
+ messagesByRole.set(partRole, messageIndex)
416
+ }
417
+ }
418
+ }
419
+
420
+ return messages.length > 0 ? messages : [{ content: '' }]
421
+ }
422
+
423
+ /**
424
+ * Format non-streaming output from response
425
+ * @param {object} response
426
+ * @returns {Array}
427
+ */
428
+ function formatNonStreamingOutput (response) {
429
+ const messages = []
430
+
431
+ for (const candidate of response.candidates) {
432
+ messages.push(...formatNonStreamingCandidate(candidate))
433
+ }
434
+
435
+ return messages.length > 0 ? messages : [{ content: '' }]
436
+ }
437
+
438
+ /**
439
+ * Format output messages from response
440
+ * @param {object} response
441
+ * @param {boolean} isStreaming
442
+ * @returns {Array}
443
+ */
444
+ function formatOutputMessages (response, isStreaming = false) {
445
+ if (!response?.candidates?.length) {
446
+ return [{ content: '' }]
447
+ }
448
+
449
+ if (isStreaming) {
450
+ return formatStreamingOutput(response)
451
+ }
452
+
453
+ return formatNonStreamingOutput(response)
454
+ }
455
+
456
+ /**
457
+ * Format embedding output from response
458
+ * @param {object} response
459
+ * @returns {string}
460
+ */
461
+ function formatEmbeddingOutput (response) {
462
+ if (!response?.embeddings?.length) {
463
+ return ''
464
+ }
465
+
466
+ const embeddingCount = response.embeddings.length
467
+ const firstEmbedding = response.embeddings[0]
468
+
469
+ if (firstEmbedding.values && Array.isArray(firstEmbedding.values)) {
470
+ const embeddingDim = firstEmbedding.values.length
471
+ return `[${embeddingCount} embedding(s) returned with size ${embeddingDim}]`
472
+ }
473
+
474
+ return `[${embeddingCount} embedding(s) returned]`
475
+ }
476
+
477
+ module.exports = {
478
+ getOperation,
479
+ extractMetrics,
480
+ extractMetadata,
481
+ aggregateStreamingChunks,
482
+ formatInputMessages,
483
+ formatEmbeddingInput,
484
+ formatOutputMessages,
485
+ formatEmbeddingOutput
486
+ }
@@ -174,13 +174,13 @@ class BaseLLMGeneratePlugin extends BaseLangChainLLMObsPlugin {
174
174
  class EmbeddingsEmbedQueryPlugin extends BaseLangChainLLMObsPlugin {
175
175
  static id = 'llmobs_langchain_embeddings_embed_query'
176
176
  static lcType = 'embedding'
177
- static prefix = 'tracing:apm:@langchain/core:Embeddings_embedQuery'
177
+ static prefix = 'tracing:orchestrion:@langchain/core:Embeddings_embedQuery'
178
178
  }
179
179
 
180
180
  class EmbeddingsEmbedDocumentsPlugin extends BaseLangChainLLMObsPlugin {
181
181
  static id = 'llmobs_langchain_embeddings_embed_documents'
182
182
  static lcType = 'embedding'
183
- static prefix = 'tracing:apm:@langchain/core:Embeddings_embedDocuments'
183
+ static prefix = 'tracing:orchestrion:@langchain/core:Embeddings_embedDocuments'
184
184
  }
185
185
 
186
186
  class ToolInvokePlugin extends BaseLangChainLLMObsPlugin {
@@ -1,6 +1,7 @@
1
1
  'use strict'
2
2
 
3
- const LLMObsPlugin = require('./base')
3
+ const LLMObsPlugin = require('../base')
4
+ const { extractChatTemplateFromInstructions, normalizePromptVariables, extractTextFromContentItem } = require('./utils')
4
5
 
5
6
  const allowedParamKeys = new Set([
6
7
  'max_output_tokens',
@@ -67,6 +68,12 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
67
68
  if (!error) {
68
69
  const metrics = this._extractMetrics(response)
69
70
  this._tagger.tagMetrics(span, metrics)
71
+
72
+ const responseModel = response.model
73
+ if (responseModel) {
74
+ // override the model name with the response model (more accurate)
75
+ this._tagger.tagModelName(span, responseModel)
76
+ }
70
77
  }
71
78
  }
72
79
 
@@ -85,11 +92,11 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
85
92
 
86
93
  if (tokenUsage) {
87
94
  // Responses API uses input_tokens, Chat/Completions use prompt_tokens
88
- const inputTokens = tokenUsage.input_tokens ?? tokenUsage.prompt_tokens
95
+ const inputTokens = tokenUsage.input_tokens ?? tokenUsage.prompt_tokens ?? 0
89
96
  if (inputTokens !== undefined) metrics.inputTokens = inputTokens
90
97
 
91
98
  // Responses API uses output_tokens, Chat/Completions use completion_tokens
92
- const outputTokens = tokenUsage.output_tokens ?? tokenUsage.completion_tokens
99
+ const outputTokens = tokenUsage.output_tokens ?? tokenUsage.completion_tokens ?? 0
93
100
  if (outputTokens !== undefined) metrics.outputTokens = outputTokens
94
101
 
95
102
  const totalTokens = tokenUsage.total_tokens || (inputTokens + outputTokens)
@@ -105,10 +112,14 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
105
112
  } else if (tokenUsage.prompt_tokens_details) {
106
113
  // Chat/Completions API - only include if > 0
107
114
  const cacheReadTokens = tokenUsage.prompt_tokens_details.cached_tokens
108
- if (cacheReadTokens) {
115
+ if (cacheReadTokens != null) {
109
116
  metrics.cacheReadTokens = cacheReadTokens
110
117
  }
111
118
  }
119
+ // Reasoning tokens - Responses API returns `output_tokens_details`, `completion_tokens_details`
120
+ const reasoningOutputObject = tokenUsage.output_tokens_details ?? tokenUsage.completion_tokens_details
121
+ const reasoningOutputTokens = reasoningOutputObject?.reasoning_tokens ?? 0
122
+ if (reasoningOutputTokens !== undefined) metrics.reasoningOutputTokens = reasoningOutputTokens
112
123
  }
113
124
 
114
125
  return metrics
@@ -159,6 +170,16 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
159
170
  _tagChatCompletion (span, inputs, response, error) {
160
171
  const { messages, model, ...parameters } = inputs
161
172
 
173
+ const metadata = Object.entries(parameters).reduce((obj, [key, value]) => {
174
+ if (!['tools', 'functions'].includes(key)) {
175
+ obj[key] = value
176
+ }
177
+
178
+ return obj
179
+ }, {})
180
+
181
+ this._tagger.tagMetadata(span, metadata)
182
+
162
183
  if (error) {
163
184
  this._tagger.tagLLMIO(span, messages, [{ content: '' }])
164
185
  return
@@ -200,22 +221,13 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
200
221
  }
201
222
 
202
223
  this._tagger.tagLLMIO(span, messages, outputMessages)
203
-
204
- const metadata = Object.entries(parameters).reduce((obj, [key, value]) => {
205
- if (!['tools', 'functions'].includes(key)) {
206
- obj[key] = value
207
- }
208
-
209
- return obj
210
- }, {})
211
-
212
- this._tagger.tagMetadata(span, metadata)
213
224
  }
214
225
 
215
226
  #tagResponse (span, inputs, response, error) {
216
227
  // Tag metadata - use allowlist approach for request parameters
217
228
 
218
- const { input, model, ...parameters } = inputs
229
+ const { model, ...parameters } = inputs
230
+ let input = inputs.input
219
231
 
220
232
  // Create input messages
221
233
  const inputMessages = []
@@ -225,10 +237,33 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
225
237
  inputMessages.push({ role: 'system', content: inputs.instructions })
226
238
  }
227
239
 
240
+ // For reusable prompts, use response.instructions if no explicit input is provided
241
+ if (!input && inputs.prompt && response?.instructions) {
242
+ input = response.instructions
243
+ }
244
+
228
245
  // Handle input - can be string or array of mixed messages
229
246
  if (Array.isArray(input)) {
230
247
  for (const item of input) {
231
- if (item.type === 'function_call') {
248
+ if (item.type === 'message') {
249
+ // Handle instruction messages (from response.instructions for reusable prompts)
250
+ const role = item.role
251
+ if (!role) continue
252
+
253
+ let content = ''
254
+ if (Array.isArray(item.content)) {
255
+ const textParts = item.content
256
+ .map(extractTextFromContentItem)
257
+ .filter(Boolean)
258
+ content = textParts.join('')
259
+ } else if (typeof item.content === 'string') {
260
+ content = item.content
261
+ }
262
+
263
+ if (content) {
264
+ inputMessages.push({ role, content })
265
+ }
266
+ } else if (item.type === 'function_call') {
232
267
  // Function call: convert to message with tool_calls
233
268
  // Parse arguments if it's a JSON string
234
269
  let parsedArgs = item.arguments
@@ -269,6 +304,15 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
269
304
  inputMessages.push({ role: 'user', content: input })
270
305
  }
271
306
 
307
+ const inputMetadata = Object.entries(parameters).reduce((obj, [key, value]) => {
308
+ if (allowedParamKeys.has(key)) {
309
+ obj[key] = value
310
+ }
311
+ return obj
312
+ }, {})
313
+
314
+ this._tagger.tagMetadata(span, inputMetadata)
315
+
272
316
  if (error) {
273
317
  this._tagger.tagLLMIO(span, inputMessages, [{ content: '' }])
274
318
  return
@@ -287,17 +331,13 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
287
331
  for (const item of response.output) {
288
332
  // Handle reasoning type (reasoning responses)
289
333
  if (item.type === 'reasoning') {
290
- // Extract reasoning text from summary
291
- let reasoningText = ''
292
- if (Array.isArray(item.summary) && item.summary.length > 0) {
293
- const summaryItem = item.summary[0]
294
- if (summaryItem.type === 'summary_text' && summaryItem.text) {
295
- reasoningText = summaryItem.text
296
- }
297
- }
298
334
  outputMessages.push({
299
335
  role: 'reasoning',
300
- content: reasoningText
336
+ content: JSON.stringify({
337
+ summary: item.summary ?? [],
338
+ encrypted_content: item.encrypted_content ?? null,
339
+ id: item.id ?? ''
340
+ })
301
341
  })
302
342
  } else if (item.type === 'function_call') {
303
343
  // Handle function_call type (responses API tool calls)
@@ -369,24 +409,32 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
369
409
 
370
410
  this._tagger.tagLLMIO(span, inputMessages, outputMessages)
371
411
 
372
- const metadata = Object.entries(parameters).reduce((obj, [key, value]) => {
373
- if (allowedParamKeys.has(key)) {
374
- obj[key] = value
412
+ // Handle prompt tracking for reusable prompts
413
+ if (inputs.prompt && response?.prompt) {
414
+ const { id, version } = response.prompt // ResponsePrompt
415
+ // TODO: Add proper tagger API for prompt metadata
416
+ if (id && version) {
417
+ const normalizedVariables = normalizePromptVariables(inputs.prompt.variables)
418
+ const chatTemplate = extractChatTemplateFromInstructions(response.instructions, normalizedVariables)
419
+ this._tagger._setTag(span, '_ml_obs.meta.input.prompt', {
420
+ id,
421
+ version,
422
+ variables: normalizedVariables,
423
+ chat_template: chatTemplate
424
+ })
375
425
  }
376
- return obj
377
- }, {})
426
+ }
427
+
428
+ const outputMetadata = {}
378
429
 
379
430
  // Add fields from response object (convert numbers to floats)
380
- if (response.temperature !== undefined) metadata.temperature = Number(response.temperature)
381
- if (response.top_p !== undefined) metadata.top_p = Number(response.top_p)
382
- if (response.tool_choice !== undefined) metadata.tool_choice = response.tool_choice
383
- if (response.truncation !== undefined) metadata.truncation = response.truncation
384
- if (response.text !== undefined) metadata.text = response.text
385
- if (response.usage?.output_tokens_details?.reasoning_tokens !== undefined) {
386
- metadata.reasoning_tokens = response.usage.output_tokens_details.reasoning_tokens
387
- }
431
+ if (response.temperature !== undefined) outputMetadata.temperature = Number(response.temperature)
432
+ if (response.top_p !== undefined) outputMetadata.top_p = Number(response.top_p)
433
+ if (response.tool_choice !== undefined) outputMetadata.tool_choice = response.tool_choice
434
+ if (response.truncation !== undefined) outputMetadata.truncation = response.truncation
435
+ if (response.text !== undefined) outputMetadata.text = response.text
388
436
 
389
- this._tagger.tagMetadata(span, metadata)
437
+ this._tagger.tagMetadata(span, outputMetadata) // update the metadata with the output metadata
390
438
  }
391
439
  }
392
440