openlit 1.11.0 → 1.12.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 (186) hide show
  1. package/README.md +1 -1
  2. package/dist/config.d.ts +12 -4
  3. package/dist/config.js +7 -17
  4. package/dist/config.js.map +1 -1
  5. package/dist/evals/llm/anthropic.js +10 -6
  6. package/dist/evals/llm/anthropic.js.map +1 -1
  7. package/dist/evals/llm/openai.js +9 -5
  8. package/dist/evals/llm/openai.js.map +1 -1
  9. package/dist/features/vault.js +1 -1
  10. package/dist/features/vault.js.map +1 -1
  11. package/dist/helpers.d.ts +93 -1
  12. package/dist/helpers.js +270 -8
  13. package/dist/helpers.js.map +1 -1
  14. package/dist/index.d.ts +6 -5
  15. package/dist/index.js +95 -50
  16. package/dist/index.js.map +1 -1
  17. package/dist/instrumentation/__tests__/anthropic-wrapper.test.js +215 -27
  18. package/dist/instrumentation/__tests__/anthropic-wrapper.test.js.map +1 -1
  19. package/dist/instrumentation/__tests__/base-wrapper.test.js +19 -23
  20. package/dist/instrumentation/__tests__/base-wrapper.test.js.map +1 -1
  21. package/dist/instrumentation/__tests__/bedrock-trace-comparison.test.d.ts +1 -0
  22. package/dist/instrumentation/__tests__/bedrock-trace-comparison.test.js +422 -0
  23. package/dist/instrumentation/__tests__/bedrock-trace-comparison.test.js.map +1 -0
  24. package/dist/instrumentation/__tests__/chroma-trace-comparison.test.js +1 -1
  25. package/dist/instrumentation/__tests__/chroma-trace-comparison.test.js.map +1 -1
  26. package/dist/instrumentation/__tests__/cohere-wrapper.test.js +150 -25
  27. package/dist/instrumentation/__tests__/cohere-wrapper.test.js.map +1 -1
  28. package/dist/instrumentation/__tests__/google-ai-trace-comparison.test.js +152 -33
  29. package/dist/instrumentation/__tests__/google-ai-trace-comparison.test.js.map +1 -1
  30. package/dist/instrumentation/__tests__/groq-trace-comparison.test.js +391 -45
  31. package/dist/instrumentation/__tests__/groq-trace-comparison.test.js.map +1 -1
  32. package/dist/instrumentation/__tests__/huggingface-trace-comparison.test.d.ts +2 -2
  33. package/dist/instrumentation/__tests__/huggingface-trace-comparison.test.js +323 -31
  34. package/dist/instrumentation/__tests__/huggingface-trace-comparison.test.js.map +1 -1
  35. package/dist/instrumentation/__tests__/langchain-wrapper.test.d.ts +1 -0
  36. package/dist/instrumentation/__tests__/langchain-wrapper.test.js +282 -0
  37. package/dist/instrumentation/__tests__/langchain-wrapper.test.js.map +1 -0
  38. package/dist/instrumentation/__tests__/milvus-trace-comparison.test.js +1 -1
  39. package/dist/instrumentation/__tests__/milvus-trace-comparison.test.js.map +1 -1
  40. package/dist/instrumentation/__tests__/mistral-trace-comparison.test.d.ts +0 -3
  41. package/dist/instrumentation/__tests__/mistral-trace-comparison.test.js +275 -68
  42. package/dist/instrumentation/__tests__/mistral-trace-comparison.test.js.map +1 -1
  43. package/dist/instrumentation/__tests__/openai-wrapper.test.js +7 -9
  44. package/dist/instrumentation/__tests__/openai-wrapper.test.js.map +1 -1
  45. package/dist/instrumentation/__tests__/qdrant-trace-comparison.test.js +1 -1
  46. package/dist/instrumentation/__tests__/qdrant-trace-comparison.test.js.map +1 -1
  47. package/dist/instrumentation/__tests__/replicate-trace-comparison.test.d.ts +2 -1
  48. package/dist/instrumentation/__tests__/replicate-trace-comparison.test.js +209 -21
  49. package/dist/instrumentation/__tests__/replicate-trace-comparison.test.js.map +1 -1
  50. package/dist/instrumentation/__tests__/together-trace-comparison.test.js +231 -51
  51. package/dist/instrumentation/__tests__/together-trace-comparison.test.js.map +1 -1
  52. package/dist/instrumentation/__tests__/vercel-ai-trace-comparison.test.d.ts +8 -0
  53. package/dist/instrumentation/__tests__/vercel-ai-trace-comparison.test.js +446 -0
  54. package/dist/instrumentation/__tests__/vercel-ai-trace-comparison.test.js.map +1 -0
  55. package/dist/instrumentation/anthropic/index.d.ts +2 -3
  56. package/dist/instrumentation/anthropic/index.js.map +1 -1
  57. package/dist/instrumentation/anthropic/wrapper.d.ts +1 -3
  58. package/dist/instrumentation/anthropic/wrapper.js +211 -91
  59. package/dist/instrumentation/anthropic/wrapper.js.map +1 -1
  60. package/dist/instrumentation/azure-ai-inference/index.d.ts +11 -0
  61. package/dist/instrumentation/azure-ai-inference/index.js +76 -0
  62. package/dist/instrumentation/azure-ai-inference/index.js.map +1 -0
  63. package/dist/instrumentation/azure-ai-inference/wrapper.d.ts +42 -0
  64. package/dist/instrumentation/azure-ai-inference/wrapper.js +515 -0
  65. package/dist/instrumentation/azure-ai-inference/wrapper.js.map +1 -0
  66. package/dist/instrumentation/base-wrapper.d.ts +2 -1
  67. package/dist/instrumentation/base-wrapper.js +35 -23
  68. package/dist/instrumentation/base-wrapper.js.map +1 -1
  69. package/dist/instrumentation/bedrock/wrapper.d.ts +21 -3
  70. package/dist/instrumentation/bedrock/wrapper.js +318 -265
  71. package/dist/instrumentation/bedrock/wrapper.js.map +1 -1
  72. package/dist/instrumentation/chroma/wrapper.js +1 -1
  73. package/dist/instrumentation/chroma/wrapper.js.map +1 -1
  74. package/dist/instrumentation/claude-agent-sdk/index.d.ts +23 -0
  75. package/dist/instrumentation/claude-agent-sdk/index.js +83 -0
  76. package/dist/instrumentation/claude-agent-sdk/index.js.map +1 -0
  77. package/dist/instrumentation/claude-agent-sdk/wrapper.d.ts +13 -0
  78. package/dist/instrumentation/claude-agent-sdk/wrapper.js +1031 -0
  79. package/dist/instrumentation/claude-agent-sdk/wrapper.js.map +1 -0
  80. package/dist/instrumentation/cohere/index.d.ts +2 -3
  81. package/dist/instrumentation/cohere/index.js.map +1 -1
  82. package/dist/instrumentation/cohere/wrapper.d.ts +1 -1
  83. package/dist/instrumentation/cohere/wrapper.js +215 -56
  84. package/dist/instrumentation/cohere/wrapper.js.map +1 -1
  85. package/dist/instrumentation/google-adk/index.d.ts +57 -0
  86. package/dist/instrumentation/google-adk/index.js +371 -0
  87. package/dist/instrumentation/google-adk/index.js.map +1 -0
  88. package/dist/instrumentation/google-adk/utils.d.ts +45 -0
  89. package/dist/instrumentation/google-adk/utils.js +663 -0
  90. package/dist/instrumentation/google-adk/utils.js.map +1 -0
  91. package/dist/instrumentation/google-adk/wrapper.d.ts +11 -0
  92. package/dist/instrumentation/google-adk/wrapper.js +391 -0
  93. package/dist/instrumentation/google-adk/wrapper.js.map +1 -0
  94. package/dist/instrumentation/google-ai/wrapper.d.ts +7 -4
  95. package/dist/instrumentation/google-ai/wrapper.js +197 -61
  96. package/dist/instrumentation/google-ai/wrapper.js.map +1 -1
  97. package/dist/instrumentation/groq/wrapper.js +137 -65
  98. package/dist/instrumentation/groq/wrapper.js.map +1 -1
  99. package/dist/instrumentation/huggingface/wrapper.js +241 -39
  100. package/dist/instrumentation/huggingface/wrapper.js.map +1 -1
  101. package/dist/instrumentation/index.d.ts +2 -2
  102. package/dist/instrumentation/index.js +64 -6
  103. package/dist/instrumentation/index.js.map +1 -1
  104. package/dist/instrumentation/langchain/index.d.ts +0 -7
  105. package/dist/instrumentation/langchain/index.js +2 -20
  106. package/dist/instrumentation/langchain/index.js.map +1 -1
  107. package/dist/instrumentation/langchain/wrapper.d.ts +35 -0
  108. package/dist/instrumentation/langchain/wrapper.js +1098 -184
  109. package/dist/instrumentation/langchain/wrapper.js.map +1 -1
  110. package/dist/instrumentation/langgraph/index.d.ts +12 -0
  111. package/dist/instrumentation/langgraph/index.js +99 -0
  112. package/dist/instrumentation/langgraph/index.js.map +1 -0
  113. package/dist/instrumentation/langgraph/wrapper.d.ts +20 -0
  114. package/dist/instrumentation/langgraph/wrapper.js +619 -0
  115. package/dist/instrumentation/langgraph/wrapper.js.map +1 -0
  116. package/dist/instrumentation/llamaindex/index.d.ts +31 -6
  117. package/dist/instrumentation/llamaindex/index.js +180 -61
  118. package/dist/instrumentation/llamaindex/index.js.map +1 -1
  119. package/dist/instrumentation/llamaindex/wrapper.d.ts +15 -3
  120. package/dist/instrumentation/llamaindex/wrapper.js +670 -179
  121. package/dist/instrumentation/llamaindex/wrapper.js.map +1 -1
  122. package/dist/instrumentation/milvus/wrapper.js +1 -1
  123. package/dist/instrumentation/milvus/wrapper.js.map +1 -1
  124. package/dist/instrumentation/mistral/wrapper.js +154 -79
  125. package/dist/instrumentation/mistral/wrapper.js.map +1 -1
  126. package/dist/instrumentation/ollama/index.js +33 -4
  127. package/dist/instrumentation/ollama/index.js.map +1 -1
  128. package/dist/instrumentation/ollama/wrapper.d.ts +28 -2
  129. package/dist/instrumentation/ollama/wrapper.js +432 -48
  130. package/dist/instrumentation/ollama/wrapper.js.map +1 -1
  131. package/dist/instrumentation/openai/index.d.ts +2 -3
  132. package/dist/instrumentation/openai/index.js.map +1 -1
  133. package/dist/instrumentation/openai/wrapper.js +293 -194
  134. package/dist/instrumentation/openai/wrapper.js.map +1 -1
  135. package/dist/instrumentation/openai-agents/index.d.ts +20 -0
  136. package/dist/instrumentation/openai-agents/index.js +174 -0
  137. package/dist/instrumentation/openai-agents/index.js.map +1 -0
  138. package/dist/instrumentation/openai-agents/processor.d.ts +35 -0
  139. package/dist/instrumentation/openai-agents/processor.js +249 -0
  140. package/dist/instrumentation/openai-agents/processor.js.map +1 -0
  141. package/dist/instrumentation/openai-agents/utils.d.ts +20 -0
  142. package/dist/instrumentation/openai-agents/utils.js +624 -0
  143. package/dist/instrumentation/openai-agents/utils.js.map +1 -0
  144. package/dist/instrumentation/pinecone/wrapper.js +2 -2
  145. package/dist/instrumentation/pinecone/wrapper.js.map +1 -1
  146. package/dist/instrumentation/qdrant/wrapper.js +1 -1
  147. package/dist/instrumentation/qdrant/wrapper.js.map +1 -1
  148. package/dist/instrumentation/replicate/wrapper.js +103 -21
  149. package/dist/instrumentation/replicate/wrapper.js.map +1 -1
  150. package/dist/instrumentation/strands/index.d.ts +21 -0
  151. package/dist/instrumentation/strands/index.js +83 -0
  152. package/dist/instrumentation/strands/index.js.map +1 -0
  153. package/dist/instrumentation/strands/processor.d.ts +45 -0
  154. package/dist/instrumentation/strands/processor.js +545 -0
  155. package/dist/instrumentation/strands/processor.js.map +1 -0
  156. package/dist/instrumentation/strands/utils.d.ts +24 -0
  157. package/dist/instrumentation/strands/utils.js +360 -0
  158. package/dist/instrumentation/strands/utils.js.map +1 -0
  159. package/dist/instrumentation/together/wrapper.js +125 -51
  160. package/dist/instrumentation/together/wrapper.js.map +1 -1
  161. package/dist/instrumentation/vercel-ai/wrapper.d.ts +28 -2
  162. package/dist/instrumentation/vercel-ai/wrapper.js +314 -164
  163. package/dist/instrumentation/vercel-ai/wrapper.js.map +1 -1
  164. package/dist/llm/anthropic.js +10 -6
  165. package/dist/llm/anthropic.js.map +1 -1
  166. package/dist/llm/openai.js +9 -5
  167. package/dist/llm/openai.js.map +1 -1
  168. package/dist/otel/__tests__/metrics.test.js +16 -27
  169. package/dist/otel/__tests__/metrics.test.js.map +1 -1
  170. package/dist/otel/events.d.ts +11 -0
  171. package/dist/otel/events.js +74 -0
  172. package/dist/otel/events.js.map +1 -0
  173. package/dist/otel/metrics.d.ts +5 -6
  174. package/dist/otel/metrics.js +66 -48
  175. package/dist/otel/metrics.js.map +1 -1
  176. package/dist/otel/tracing.d.ts +6 -2
  177. package/dist/otel/tracing.js +71 -24
  178. package/dist/otel/tracing.js.map +1 -1
  179. package/dist/otel/utils.d.ts +11 -0
  180. package/dist/otel/utils.js +34 -0
  181. package/dist/otel/utils.js.map +1 -0
  182. package/dist/semantic-convention.d.ts +44 -5
  183. package/dist/semantic-convention.js +51 -8
  184. package/dist/semantic-convention.js.map +1 -1
  185. package/dist/types.d.ts +58 -22
  186. package/package.json +41 -9
@@ -1,21 +1,71 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
37
  };
5
38
  Object.defineProperty(exports, "__esModule", { value: true });
6
39
  const api_1 = require("@opentelemetry/api");
7
40
  const config_1 = __importDefault(require("../../config"));
8
- const helpers_1 = __importDefault(require("../../helpers"));
41
+ const helpers_1 = __importStar(require("../../helpers"));
9
42
  const semantic_convention_1 = __importDefault(require("../../semantic-convention"));
10
43
  const base_wrapper_1 = __importDefault(require("../base-wrapper"));
44
+ function spanCreationAttrs(operationName, requestModel) {
45
+ return {
46
+ [semantic_convention_1.default.GEN_AI_OPERATION]: operationName,
47
+ [semantic_convention_1.default.GEN_AI_PROVIDER_NAME_OTEL]: semantic_convention_1.default.GEN_AI_SYSTEM_OPENAI,
48
+ [semantic_convention_1.default.GEN_AI_REQUEST_MODEL]: requestModel,
49
+ [semantic_convention_1.default.SERVER_ADDRESS]: OpenAIWrapper.serverAddress,
50
+ [semantic_convention_1.default.SERVER_PORT]: OpenAIWrapper.serverPort,
51
+ };
52
+ }
11
53
  class OpenAIWrapper extends base_wrapper_1.default {
12
54
  static _patchChatCompletionCreate(tracer) {
13
55
  const genAIEndpoint = 'openai.resources.chat.completions';
14
56
  return (originalMethod) => {
15
57
  return async function (...args) {
16
- const span = tracer.startSpan(genAIEndpoint, { kind: api_1.SpanKind.CLIENT });
58
+ if ((0, helpers_1.isFrameworkLlmActive)())
59
+ return originalMethod.apply(this, args);
60
+ const requestModel = args[0]?.model || 'gpt-4o';
61
+ const spanName = `${semantic_convention_1.default.GEN_AI_OPERATION_TYPE_CHAT} ${requestModel}`;
62
+ const effectiveCtx = (0, helpers_1.getFrameworkParentContext)() ?? api_1.context.active();
63
+ const span = tracer.startSpan(spanName, {
64
+ kind: api_1.SpanKind.CLIENT,
65
+ attributes: spanCreationAttrs(semantic_convention_1.default.GEN_AI_OPERATION_TYPE_CHAT, requestModel),
66
+ }, effectiveCtx);
17
67
  return api_1.context
18
- .with(api_1.trace.setSpan(api_1.context.active(), span), async () => {
68
+ .with(api_1.trace.setSpan(effectiveCtx, span), async () => {
19
69
  return originalMethod.apply(this, args);
20
70
  })
21
71
  .then((response) => {
@@ -32,7 +82,16 @@ class OpenAIWrapper extends base_wrapper_1.default {
32
82
  })
33
83
  .catch((e) => {
34
84
  helpers_1.default.handleException(span, e);
85
+ base_wrapper_1.default.recordMetrics(span, {
86
+ genAIEndpoint,
87
+ model: requestModel,
88
+ aiSystem: OpenAIWrapper.aiSystem,
89
+ serverAddress: OpenAIWrapper.serverAddress,
90
+ serverPort: OpenAIWrapper.serverPort,
91
+ errorType: e?.constructor?.name || '_OTHER',
92
+ });
35
93
  span.end();
94
+ throw e;
36
95
  });
37
96
  };
38
97
  };
@@ -50,10 +109,10 @@ class OpenAIWrapper extends base_wrapper_1.default {
50
109
  }
51
110
  catch (e) {
52
111
  helpers_1.default.handleException(span, e);
112
+ throw e;
53
113
  }
54
114
  finally {
55
115
  span.end();
56
- // Record metrics after span has ended if parameters are available
57
116
  if (metricParams) {
58
117
  base_wrapper_1.default.recordMetrics(span, metricParams);
59
118
  }
@@ -94,7 +153,7 @@ class OpenAIWrapper extends base_wrapper_1.default {
94
153
  },
95
154
  },
96
155
  };
97
- let toolCalls = [];
156
+ const toolCalls = [];
98
157
  for await (const chunk of response) {
99
158
  timestamps.push(Date.now());
100
159
  result.id = chunk.id;
@@ -115,12 +174,10 @@ class OpenAIWrapper extends base_wrapper_1.default {
115
174
  if (chunk.choices[0]?.delta.content) {
116
175
  result.choices[0].message.content += chunk.choices[0].delta.content;
117
176
  }
118
- // Improved tool calls handling for streaming
119
177
  if (chunk.choices[0]?.delta.tool_calls) {
120
178
  const deltaTools = chunk.choices[0].delta.tool_calls;
121
179
  for (const tool of deltaTools) {
122
180
  const idx = tool.index || 0;
123
- // Extend array if needed
124
181
  while (toolCalls.length <= idx) {
125
182
  toolCalls.push({
126
183
  id: '',
@@ -129,7 +186,6 @@ class OpenAIWrapper extends base_wrapper_1.default {
129
186
  });
130
187
  }
131
188
  if (tool.id) {
132
- // New tool call
133
189
  toolCalls[idx].id = tool.id;
134
190
  toolCalls[idx].type = tool.type || 'function';
135
191
  if (tool.function?.name) {
@@ -140,7 +196,6 @@ class OpenAIWrapper extends base_wrapper_1.default {
140
196
  }
141
197
  }
142
198
  else if (tool.function?.arguments) {
143
- // Append arguments to existing tool call
144
199
  toolCalls[idx].function.arguments += tool.function.arguments;
145
200
  }
146
201
  }
@@ -169,7 +224,6 @@ class OpenAIWrapper extends base_wrapper_1.default {
169
224
  };
170
225
  }
171
226
  args[0].tools = tools;
172
- // Calculate TTFT and TBT
173
227
  const ttft = timestamps.length > 0 ? (timestamps[0] - startTime) / 1000 : 0;
174
228
  let tbt = 0;
175
229
  if (timestamps.length > 1) {
@@ -188,107 +242,95 @@ class OpenAIWrapper extends base_wrapper_1.default {
188
242
  }
189
243
  catch (e) {
190
244
  helpers_1.default.handleException(span, e);
245
+ throw e;
191
246
  }
192
247
  finally {
193
248
  span.end();
194
- // Record metrics after span has ended if parameters are available
195
249
  if (metricParams) {
196
250
  base_wrapper_1.default.recordMetrics(span, metricParams);
197
251
  }
198
252
  }
199
253
  }
200
254
  static async _chatCompletionCommonSetter({ args, genAIEndpoint, result, span, ttft = 0, tbt = 0, }) {
201
- const traceContent = config_1.default.traceContent;
202
- const { messages, frequency_penalty = 0, max_tokens = null, n = 1, presence_penalty = 0, seed = null, stop = null, temperature = 1, top_p, user, stream = false, tools, } = args[0];
203
- // Request Params attributes : Start
255
+ const captureContent = config_1.default.captureMessageContent;
256
+ const requestModel = args[0]?.model || 'gpt-4o';
257
+ const { messages, frequency_penalty = 0, max_tokens = null, n = 1, presence_penalty = 0, seed = null, stop = null, temperature = 1, top_p, user, stream = false, tools: _tools, service_tier, } = args[0];
204
258
  span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_TOP_P, top_p || 1);
205
- span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_MAX_TOKENS, max_tokens || -1);
259
+ if (max_tokens != null) {
260
+ span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_MAX_TOKENS, max_tokens);
261
+ }
206
262
  span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_TEMPERATURE, temperature);
207
- span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_PRESENCE_PENALTY, presence_penalty);
208
- span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_FREQUENCY_PENALTY, frequency_penalty);
209
- span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_SEED, seed ? String(seed) : '');
263
+ if (presence_penalty) {
264
+ span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_PRESENCE_PENALTY, presence_penalty);
265
+ }
266
+ if (frequency_penalty) {
267
+ span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_FREQUENCY_PENALTY, frequency_penalty);
268
+ }
269
+ if (seed != null) {
270
+ span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_SEED, Number(seed));
271
+ }
210
272
  span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_IS_STREAM, stream);
211
273
  if (stop) {
212
274
  span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_STOP_SEQUENCES, Array.isArray(stop) ? stop : [stop]);
213
275
  }
214
- if (user) {
215
- span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_USER, user);
276
+ if (n && n !== 1) {
277
+ span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_CHOICE_COUNT, n);
278
+ }
279
+ if (service_tier && service_tier !== 'auto') {
280
+ span.setAttribute(semantic_convention_1.default.OPENAI_REQUEST_SERVICE_TIER, service_tier);
216
281
  }
217
- if (traceContent) {
282
+ if (captureContent) {
218
283
  span.setAttribute(semantic_convention_1.default.GEN_AI_INPUT_MESSAGES, helpers_1.default.buildInputMessages(messages || []));
219
284
  }
220
- // Request Params attributes : End
221
- span.setAttribute(semantic_convention_1.default.GEN_AI_OPERATION, semantic_convention_1.default.GEN_AI_OPERATION_TYPE_CHAT);
222
285
  span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_ID, result.id);
223
- const model = result.model || 'gpt-3.5-turbo';
224
- const responseModel = result.model || model;
225
- const pricingInfo = await config_1.default.updatePricingJson(config_1.default.pricing_json);
226
- // Calculate cost of the operation
227
- const cost = helpers_1.default.getChatModelCost(model, pricingInfo, result.usage.prompt_tokens, result.usage.completion_tokens);
286
+ const responseModel = result.model || requestModel;
287
+ const pricingInfo = config_1.default.pricingInfo || {};
288
+ const cost = helpers_1.default.getChatModelCost(requestModel, pricingInfo, result.usage.prompt_tokens, result.usage.completion_tokens);
228
289
  OpenAIWrapper.setBaseSpanAttributes(span, {
229
290
  genAIEndpoint,
230
- model,
291
+ model: requestModel,
231
292
  user,
232
293
  cost,
233
294
  aiSystem: OpenAIWrapper.aiSystem,
234
295
  serverAddress: OpenAIWrapper.serverAddress,
235
296
  serverPort: OpenAIWrapper.serverPort,
236
297
  });
237
- // Response model
238
298
  span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_MODEL, responseModel);
239
- // OpenAI-specific attributes
299
+ span.setAttribute(semantic_convention_1.default.OPENAI_API_TYPE, 'chat_completions');
240
300
  if (result.system_fingerprint) {
241
- span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_SYSTEM_FINGERPRINT, result.system_fingerprint);
301
+ span.setAttribute(semantic_convention_1.default.OPENAI_RESPONSE_SYSTEM_FINGERPRINT, result.system_fingerprint);
242
302
  }
243
303
  if (result.service_tier) {
244
- span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_SERVICE_TIER, result.service_tier);
245
- }
246
- // Token usage
247
- span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_INPUT_TOKENS, result.usage.prompt_tokens);
248
- span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_OUTPUT_TOKENS, result.usage.completion_tokens);
249
- span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_TOTAL_TOKENS, result.usage.total_tokens);
250
- span.setAttribute(semantic_convention_1.default.GEN_AI_CLIENT_TOKEN_USAGE, result.usage.total_tokens);
251
- // Enhanced token details
252
- if (result.usage.completion_tokens_details) {
253
- if (result.usage.completion_tokens_details.reasoning_tokens) {
254
- span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_REASONING_TOKENS, result.usage.completion_tokens_details.reasoning_tokens);
255
- span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_COMPLETION_TOKENS_DETAILS_REASONING, result.usage.completion_tokens_details.reasoning_tokens);
256
- }
257
- if (result.usage.completion_tokens_details.audio_tokens) {
258
- span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_COMPLETION_TOKENS_DETAILS_AUDIO, result.usage.completion_tokens_details.audio_tokens);
259
- }
304
+ span.setAttribute(semantic_convention_1.default.OPENAI_RESPONSE_SERVICE_TIER, result.service_tier);
260
305
  }
261
- if (result.usage.prompt_tokens_details) {
262
- if (result.usage.prompt_tokens_details.cached_tokens) {
263
- span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_PROMPT_TOKENS_DETAILS_CACHE_READ, result.usage.prompt_tokens_details.cached_tokens);
264
- }
265
- if (result.usage.prompt_tokens_details.audio_tokens) {
266
- span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_PROMPT_TOKENS_DETAILS_CACHE_WRITE, result.usage.prompt_tokens_details.audio_tokens);
267
- }
306
+ const inputTokens = result.usage.prompt_tokens;
307
+ const outputTokens = result.usage.completion_tokens;
308
+ span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_INPUT_TOKENS, inputTokens);
309
+ span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_OUTPUT_TOKENS, outputTokens);
310
+ if (result.usage.prompt_tokens_details?.cached_tokens) {
311
+ span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS, result.usage.prompt_tokens_details.cached_tokens);
312
+ }
313
+ if (result.usage.prompt_tokens_details?.cache_creation_tokens) {
314
+ span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS, result.usage.prompt_tokens_details.cache_creation_tokens);
268
315
  }
269
- // TTFT and TBT metrics
270
316
  if (ttft > 0) {
271
317
  span.setAttribute(semantic_convention_1.default.GEN_AI_SERVER_TTFT, ttft);
272
318
  }
273
319
  if (tbt > 0) {
274
320
  span.setAttribute(semantic_convention_1.default.GEN_AI_SERVER_TBT, tbt);
275
321
  }
276
- // Finish reason
277
322
  if (result.choices[0].finish_reason) {
278
323
  span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_FINISH_REASON, [result.choices[0].finish_reason]);
279
324
  }
280
- // Output type
281
325
  const outputType = typeof result.choices[0].message.content === 'string'
282
326
  ? semantic_convention_1.default.GEN_AI_OUTPUT_TYPE_TEXT
283
327
  : semantic_convention_1.default.GEN_AI_OUTPUT_TYPE_JSON;
284
328
  span.setAttribute(semantic_convention_1.default.GEN_AI_OUTPUT_TYPE, outputType);
285
- // Tool calls handling
286
329
  if (result.choices[0].message.tool_calls) {
287
330
  const toolCalls = result.choices[0].message.tool_calls;
288
331
  const toolNames = toolCalls.map((t) => t.function?.name || '').filter(Boolean);
289
332
  const toolIds = toolCalls.map((t) => t.id || '').filter(Boolean);
290
333
  const toolArgs = toolCalls.map((t) => t.function?.arguments || '').filter(Boolean);
291
- const toolTypes = toolCalls.map((t) => t.type || '').filter(Boolean);
292
334
  if (toolNames.length > 0) {
293
335
  span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_NAME, toolNames.join(', '));
294
336
  }
@@ -296,23 +338,41 @@ class OpenAIWrapper extends base_wrapper_1.default {
296
338
  span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_CALL_ID, toolIds.join(', '));
297
339
  }
298
340
  if (toolArgs.length > 0) {
299
- span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_CALL_ARGUMENTS, toolArgs);
300
- }
301
- if (toolTypes.length > 0) {
302
- const toolTypesStr = toolTypes.join(', ');
303
- span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_TYPE, toolTypesStr);
304
- span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_TYPE_OTEL, toolTypesStr);
341
+ span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_ARGS, toolArgs.join(', '));
305
342
  }
306
343
  }
307
- // Content
308
- if (traceContent) {
344
+ let inputMessagesJson;
345
+ let outputMessagesJson;
346
+ if (captureContent) {
309
347
  const toolCalls = result.choices[0].message.tool_calls;
310
- const outputJson = helpers_1.default.buildOutputMessages(result.choices[0].message.content || '', result.choices[0].finish_reason || 'stop', toolCalls);
311
- span.setAttribute(semantic_convention_1.default.GEN_AI_OUTPUT_MESSAGES, outputJson);
348
+ outputMessagesJson = helpers_1.default.buildOutputMessages(result.choices[0].message.content || '', result.choices[0].finish_reason || 'stop', toolCalls);
349
+ span.setAttribute(semantic_convention_1.default.GEN_AI_OUTPUT_MESSAGES, outputMessagesJson);
350
+ inputMessagesJson = helpers_1.default.buildInputMessages(messages || []);
351
+ }
352
+ if (!config_1.default.disableEvents) {
353
+ const eventAttrs = {
354
+ [semantic_convention_1.default.GEN_AI_OPERATION]: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_CHAT,
355
+ [semantic_convention_1.default.GEN_AI_REQUEST_MODEL]: requestModel,
356
+ [semantic_convention_1.default.GEN_AI_RESPONSE_MODEL]: responseModel,
357
+ [semantic_convention_1.default.SERVER_ADDRESS]: OpenAIWrapper.serverAddress,
358
+ [semantic_convention_1.default.SERVER_PORT]: OpenAIWrapper.serverPort,
359
+ [semantic_convention_1.default.GEN_AI_RESPONSE_ID]: result.id,
360
+ [semantic_convention_1.default.GEN_AI_RESPONSE_FINISH_REASON]: [result.choices[0].finish_reason],
361
+ [semantic_convention_1.default.GEN_AI_OUTPUT_TYPE]: outputType,
362
+ [semantic_convention_1.default.GEN_AI_USAGE_INPUT_TOKENS]: inputTokens,
363
+ [semantic_convention_1.default.GEN_AI_USAGE_OUTPUT_TOKENS]: outputTokens,
364
+ };
365
+ if (captureContent) {
366
+ if (inputMessagesJson)
367
+ eventAttrs[semantic_convention_1.default.GEN_AI_INPUT_MESSAGES] = inputMessagesJson;
368
+ if (outputMessagesJson)
369
+ eventAttrs[semantic_convention_1.default.GEN_AI_OUTPUT_MESSAGES] = outputMessagesJson;
370
+ }
371
+ helpers_1.default.emitInferenceEvent(span, eventAttrs);
312
372
  }
313
373
  return {
314
374
  genAIEndpoint,
315
- model,
375
+ model: requestModel,
316
376
  user,
317
377
  cost,
318
378
  aiSystem: OpenAIWrapper.aiSystem,
@@ -320,23 +380,29 @@ class OpenAIWrapper extends base_wrapper_1.default {
320
380
  }
321
381
  static _patchEmbedding(tracer) {
322
382
  const genAIEndpoint = 'openai.resources.embeddings';
323
- const traceContent = config_1.default.traceContent;
324
383
  return (originalMethod) => {
325
384
  return async function (...args) {
326
- const span = tracer.startSpan(genAIEndpoint, { kind: api_1.SpanKind.CLIENT });
327
- return api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), async () => {
385
+ if ((0, helpers_1.isFrameworkLlmActive)())
386
+ return originalMethod.apply(this, args);
387
+ const requestModel = args[0]?.model || 'text-embedding-ada-002';
388
+ const spanName = `${semantic_convention_1.default.GEN_AI_OPERATION_TYPE_EMBEDDING} ${requestModel}`;
389
+ const effectiveCtx = (0, helpers_1.getFrameworkParentContext)() ?? api_1.context.active();
390
+ const span = tracer.startSpan(spanName, {
391
+ kind: api_1.SpanKind.CLIENT,
392
+ attributes: spanCreationAttrs(semantic_convention_1.default.GEN_AI_OPERATION_TYPE_EMBEDDING, requestModel),
393
+ }, effectiveCtx);
394
+ return api_1.context.with(api_1.trace.setSpan(effectiveCtx, span), async () => {
395
+ const captureContent = config_1.default.captureMessageContent;
328
396
  let metricParams;
329
397
  try {
330
398
  const response = await originalMethod.apply(this, args);
331
- const model = response.model || 'text-embedding-ada-002';
332
- const pricingInfo = await config_1.default.updatePricingJson(config_1.default.pricing_json);
333
- const cost = helpers_1.default.getEmbedModelCost(model, pricingInfo, response.usage.prompt_tokens);
334
- span.setAttribute(semantic_convention_1.default.GEN_AI_OPERATION, semantic_convention_1.default.GEN_AI_OPERATION_TYPE_EMBEDDING);
399
+ const _responseModel = response.model || requestModel;
400
+ const pricingInfo = config_1.default.pricingInfo || {};
401
+ const cost = helpers_1.default.getEmbedModelCost(requestModel, pricingInfo, response.usage.prompt_tokens);
335
402
  const { dimensions, encoding_format = 'float', input, user } = args[0];
336
- // Set base span attributes
337
403
  OpenAIWrapper.setBaseSpanAttributes(span, {
338
404
  genAIEndpoint,
339
- model,
405
+ model: requestModel,
340
406
  user,
341
407
  cost,
342
408
  aiSystem: OpenAIWrapper.aiSystem,
@@ -344,25 +410,18 @@ class OpenAIWrapper extends base_wrapper_1.default {
344
410
  serverPort: OpenAIWrapper.serverPort,
345
411
  });
346
412
  span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_IS_STREAM, false);
347
- // Request Params attributes : Start
348
413
  span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_ENCODING_FORMATS, [encoding_format]);
349
414
  if (dimensions) {
350
- span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_EMBEDDING_DIMENSION, dimensions);
415
+ span.setAttribute(semantic_convention_1.default.GEN_AI_EMBEDDINGS_DIMENSION_COUNT, dimensions);
351
416
  }
352
- if (user) {
353
- span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_USER, user);
354
- }
355
- if (traceContent) {
417
+ if (captureContent) {
356
418
  const formattedInput = typeof input === 'string' ? input : JSON.stringify(input);
357
419
  span.setAttribute(semantic_convention_1.default.GEN_AI_INPUT_MESSAGES, formattedInput);
358
420
  }
359
- // Request Params attributes : End
360
421
  span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_INPUT_TOKENS, response.usage.prompt_tokens);
361
- span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_TOTAL_TOKENS, response.usage.total_tokens);
362
- span.setAttribute(semantic_convention_1.default.GEN_AI_CLIENT_TOKEN_USAGE, response.usage.prompt_tokens);
363
422
  metricParams = {
364
423
  genAIEndpoint,
365
- model,
424
+ model: requestModel,
366
425
  user,
367
426
  cost,
368
427
  aiSystem: OpenAIWrapper.aiSystem,
@@ -387,38 +446,40 @@ class OpenAIWrapper extends base_wrapper_1.default {
387
446
  const genAIEndpoint = 'openai.resources.fine_tuning.jobs';
388
447
  return (originalMethod) => {
389
448
  return async function (...args) {
390
- const span = tracer.startSpan(genAIEndpoint, { kind: api_1.SpanKind.CLIENT });
391
- return api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), async () => {
449
+ if ((0, helpers_1.isFrameworkLlmActive)())
450
+ return originalMethod.apply(this, args);
451
+ const requestModel = args[0]?.model || 'gpt-3.5-turbo';
452
+ const spanName = `${semantic_convention_1.default.GEN_AI_OPERATION_TYPE_FINETUNING} ${requestModel}`;
453
+ const effectiveCtx = (0, helpers_1.getFrameworkParentContext)() ?? api_1.context.active();
454
+ const span = tracer.startSpan(spanName, {
455
+ kind: api_1.SpanKind.CLIENT,
456
+ attributes: spanCreationAttrs(semantic_convention_1.default.GEN_AI_OPERATION_TYPE_FINETUNING, requestModel),
457
+ }, effectiveCtx);
458
+ return api_1.context.with(api_1.trace.setSpan(effectiveCtx, span), async () => {
392
459
  let metricParams;
393
460
  try {
394
461
  const response = await originalMethod.apply(this, args);
395
- const model = response.model || 'gpt-3.5-turbo';
396
462
  const { hyperparameters = {}, suffix = '', training_file, user, validation_file, } = args[0];
397
- // Set base span attributes
398
463
  OpenAIWrapper.setBaseSpanAttributes(span, {
399
464
  genAIEndpoint,
400
- model,
465
+ model: requestModel,
401
466
  user,
402
467
  aiSystem: OpenAIWrapper.aiSystem,
403
468
  serverAddress: OpenAIWrapper.serverAddress,
404
469
  serverPort: OpenAIWrapper.serverPort,
405
470
  });
406
- span.setAttribute(semantic_convention_1.default.GEN_AI_OPERATION, semantic_convention_1.default.GEN_AI_OPERATION_TYPE_FINETUNING);
407
- // Request Params attributes : Start
408
471
  span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_TRAINING_FILE, training_file);
409
472
  span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_VALIDATION_FILE, validation_file);
410
473
  span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_FINETUNE_BATCH_SIZE, hyperparameters?.batch_size || 'auto');
411
474
  span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_FINETUNE_MODEL_LRM, hyperparameters?.learning_rate_multiplier || 'auto');
412
475
  span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_FINETUNE_MODEL_EPOCHS, hyperparameters?.n_epochs || 'auto');
413
476
  span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_FINETUNE_MODEL_SUFFIX, suffix);
414
- // Request Params attributes : End
415
477
  span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_ID, response.id);
416
478
  span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_INPUT_TOKENS, response.usage.prompt_tokens);
417
479
  span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_FINETUNE_STATUS, response.status);
418
- // Store metric parameters for use after span ends
419
480
  metricParams = {
420
481
  genAIEndpoint,
421
- model,
482
+ model: requestModel,
422
483
  user,
423
484
  aiSystem: OpenAIWrapper.aiSystem,
424
485
  };
@@ -426,10 +487,10 @@ class OpenAIWrapper extends base_wrapper_1.default {
426
487
  }
427
488
  catch (e) {
428
489
  helpers_1.default.handleException(span, e);
490
+ throw e;
429
491
  }
430
492
  finally {
431
493
  span.end();
432
- // Record metrics after span has ended if parameters are available
433
494
  if (metricParams) {
434
495
  base_wrapper_1.default.recordMetrics(span, metricParams);
435
496
  }
@@ -440,51 +501,56 @@ class OpenAIWrapper extends base_wrapper_1.default {
440
501
  }
441
502
  static _patchImageGenerate(tracer) {
442
503
  const genAIEndpoint = 'openai.resources.images';
443
- const traceContent = config_1.default.traceContent;
444
504
  return (originalMethod) => {
445
505
  return async function (...args) {
446
- const span = tracer.startSpan(genAIEndpoint, { kind: api_1.SpanKind.CLIENT });
447
- return api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), async () => {
506
+ if ((0, helpers_1.isFrameworkLlmActive)())
507
+ return originalMethod.apply(this, args);
508
+ const requestModel = args[0]?.model || 'dall-e-2';
509
+ const spanName = `${semantic_convention_1.default.GEN_AI_OPERATION_TYPE_IMAGE} ${requestModel}`;
510
+ const effectiveCtx = (0, helpers_1.getFrameworkParentContext)() ?? api_1.context.active();
511
+ const span = tracer.startSpan(spanName, {
512
+ kind: api_1.SpanKind.CLIENT,
513
+ attributes: spanCreationAttrs(semantic_convention_1.default.GEN_AI_OPERATION_TYPE_IMAGE, requestModel),
514
+ }, effectiveCtx);
515
+ return api_1.context.with(api_1.trace.setSpan(effectiveCtx, span), async () => {
516
+ const captureContent = config_1.default.captureMessageContent;
448
517
  let metricParams;
449
518
  try {
450
519
  const response = await originalMethod.apply(this, args);
451
520
  const { prompt, quality = 'standard', response_format = 'url', size = '1024x1024', style = 'vivid', user, } = args[0];
452
- span.setAttribute(semantic_convention_1.default.GEN_AI_OPERATION, semantic_convention_1.default.GEN_AI_OPERATION_TYPE_IMAGE);
453
- const model = response.model || 'dall-e-2';
454
- const pricingInfo = await config_1.default.updatePricingJson(config_1.default.pricing_json);
455
- // Calculate cost of the operation
521
+ const responseModel = response.model || requestModel;
522
+ const pricingInfo = config_1.default.pricingInfo || {};
456
523
  const cost = (response.data?.length || 1) *
457
- helpers_1.default.getImageModelCost(model, pricingInfo, size, quality);
524
+ helpers_1.default.getImageModelCost(responseModel, pricingInfo, size, quality);
458
525
  OpenAIWrapper.setBaseSpanAttributes(span, {
459
526
  genAIEndpoint,
460
- model,
527
+ model: requestModel,
461
528
  user,
462
529
  cost,
463
530
  aiSystem: OpenAIWrapper.aiSystem,
464
531
  serverAddress: OpenAIWrapper.serverAddress,
465
532
  serverPort: OpenAIWrapper.serverPort,
466
533
  });
467
- // Request Params attributes : Start
468
534
  span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_IMAGE_SIZE, size);
469
535
  span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_IMAGE_QUALITY, quality);
470
536
  span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_IMAGE_STYLE, style);
471
- if (traceContent) {
537
+ if (captureContent) {
472
538
  span.setAttribute(semantic_convention_1.default.GEN_AI_INPUT_MESSAGES, prompt);
473
539
  }
474
- // Request Params attributes : End
475
- let imagesCount = 0;
476
540
  if (response.data) {
541
+ const imageUrls = [];
542
+ const revisedPrompts = [];
477
543
  for (const items of response.data) {
478
- span.setAttribute(`${semantic_convention_1.default.GEN_AI_CONTENT_REVISED_PROMPT}.${imagesCount}`, items.revised_prompt || '');
479
- const attributeName = `${semantic_convention_1.default.GEN_AI_RESPONSE_IMAGE}.${imagesCount}`;
480
- span.setAttribute(attributeName, items[response_format]);
481
- imagesCount++;
544
+ revisedPrompts.push(items.revised_prompt || '');
545
+ const value = items[response_format];
546
+ imageUrls.push(value && !String(value).startsWith('data:') ? value : '[base64_image_data]');
482
547
  }
548
+ span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_IMAGE, imageUrls);
549
+ span.setAttribute(semantic_convention_1.default.GEN_AI_CONTENT_REVISED_PROMPT, revisedPrompts);
483
550
  }
484
- // Store metric parameters for use after span ends
485
551
  metricParams = {
486
552
  genAIEndpoint,
487
- model,
553
+ model: requestModel,
488
554
  user,
489
555
  cost,
490
556
  aiSystem: OpenAIWrapper.aiSystem,
@@ -493,10 +559,10 @@ class OpenAIWrapper extends base_wrapper_1.default {
493
559
  }
494
560
  catch (e) {
495
561
  helpers_1.default.handleException(span, e);
562
+ throw e;
496
563
  }
497
564
  finally {
498
565
  span.end();
499
- // Record metrics after span has ended if parameters are available
500
566
  if (metricParams) {
501
567
  base_wrapper_1.default.recordMetrics(span, metricParams);
502
568
  }
@@ -507,52 +573,57 @@ class OpenAIWrapper extends base_wrapper_1.default {
507
573
  }
508
574
  static _patchImageVariation(tracer) {
509
575
  const genAIEndpoint = 'openai.resources.images';
510
- const traceContent = config_1.default.traceContent;
511
576
  return (originalMethod) => {
512
577
  return async function (...args) {
513
- const span = tracer.startSpan(genAIEndpoint, { kind: api_1.SpanKind.CLIENT });
514
- return api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), async () => {
578
+ if ((0, helpers_1.isFrameworkLlmActive)())
579
+ return originalMethod.apply(this, args);
580
+ const requestModel = args[0]?.model || 'dall-e-2';
581
+ const spanName = `${semantic_convention_1.default.GEN_AI_OPERATION_TYPE_IMAGE} ${requestModel}`;
582
+ const effectiveCtx = (0, helpers_1.getFrameworkParentContext)() ?? api_1.context.active();
583
+ const span = tracer.startSpan(spanName, {
584
+ kind: api_1.SpanKind.CLIENT,
585
+ attributes: spanCreationAttrs(semantic_convention_1.default.GEN_AI_OPERATION_TYPE_IMAGE, requestModel),
586
+ }, effectiveCtx);
587
+ return api_1.context.with(api_1.trace.setSpan(effectiveCtx, span), async () => {
588
+ const captureContent = config_1.default.captureMessageContent;
515
589
  let metricParams;
516
590
  try {
517
591
  const response = await originalMethod.apply(this, args);
518
592
  const { prompt, quality = 'standard', response_format = 'url', size = '1024x1024', style = 'vivid', user, } = args[0];
519
- span.setAttribute(semantic_convention_1.default.GEN_AI_OPERATION, semantic_convention_1.default.GEN_AI_OPERATION_TYPE_IMAGE);
520
593
  span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_ID, response.created);
521
- const model = response.model || 'dall-e-2';
522
- const pricingInfo = await config_1.default.updatePricingJson(config_1.default.pricing_json);
523
- // Calculate cost of the operation
594
+ const responseModel = response.model || requestModel;
595
+ const pricingInfo = config_1.default.pricingInfo || {};
524
596
  const cost = (response.data?.length || 1) *
525
- helpers_1.default.getImageModelCost(model, pricingInfo, size, quality);
597
+ helpers_1.default.getImageModelCost(responseModel, pricingInfo, size, quality);
526
598
  OpenAIWrapper.setBaseSpanAttributes(span, {
527
599
  genAIEndpoint,
528
- model,
600
+ model: requestModel,
529
601
  user,
530
602
  cost,
531
603
  aiSystem: OpenAIWrapper.aiSystem,
532
604
  serverAddress: OpenAIWrapper.serverAddress,
533
605
  serverPort: OpenAIWrapper.serverPort,
534
606
  });
535
- // Request Params attributes : Start
536
607
  span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_IMAGE_SIZE, size);
537
608
  span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_IMAGE_QUALITY, quality);
538
609
  span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_IMAGE_STYLE, style);
539
- if (traceContent) {
610
+ if (captureContent) {
540
611
  span.setAttribute(semantic_convention_1.default.GEN_AI_INPUT_MESSAGES, prompt);
541
612
  }
542
- // Request Params attributes : End
543
- let imagesCount = 0;
544
613
  if (response.data) {
614
+ const imageUrls = [];
615
+ const revisedPrompts = [];
545
616
  for (const items of response.data) {
546
- span.setAttribute(`${semantic_convention_1.default.GEN_AI_CONTENT_REVISED_PROMPT}.${imagesCount}`, items.revised_prompt || '');
547
- const attributeName = `${semantic_convention_1.default.GEN_AI_RESPONSE_IMAGE}.${imagesCount}`;
548
- span.setAttribute(attributeName, items[response_format]);
549
- imagesCount++;
617
+ revisedPrompts.push(items.revised_prompt || '');
618
+ const value = items[response_format];
619
+ imageUrls.push(value && !String(value).startsWith('data:') ? value : '[base64_image_data]');
550
620
  }
621
+ span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_IMAGE, imageUrls);
622
+ span.setAttribute(semantic_convention_1.default.GEN_AI_CONTENT_REVISED_PROMPT, revisedPrompts);
551
623
  }
552
- // Store metric parameters for use after span ends
553
624
  metricParams = {
554
625
  genAIEndpoint,
555
- model,
626
+ model: requestModel,
556
627
  user,
557
628
  cost,
558
629
  aiSystem: OpenAIWrapper.aiSystem,
@@ -561,10 +632,10 @@ class OpenAIWrapper extends base_wrapper_1.default {
561
632
  }
562
633
  catch (e) {
563
634
  helpers_1.default.handleException(span, e);
635
+ throw e;
564
636
  }
565
637
  finally {
566
638
  span.end();
567
- // Record metrics after span has ended if parameters are available
568
639
  if (metricParams) {
569
640
  base_wrapper_1.default.recordMetrics(span, metricParams);
570
641
  }
@@ -575,41 +646,44 @@ class OpenAIWrapper extends base_wrapper_1.default {
575
646
  }
576
647
  static _patchAudioCreate(tracer) {
577
648
  const genAIEndpoint = 'openai.resources.audio.speech';
578
- const traceContent = config_1.default.traceContent;
579
649
  return (originalMethod) => {
580
650
  return async function (...args) {
581
- const span = tracer.startSpan(genAIEndpoint, { kind: api_1.SpanKind.CLIENT });
582
- return api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), async () => {
651
+ if ((0, helpers_1.isFrameworkLlmActive)())
652
+ return originalMethod.apply(this, args);
653
+ const requestModel = args[0]?.model || 'tts-1';
654
+ const spanName = `${semantic_convention_1.default.GEN_AI_OPERATION_TYPE_AUDIO} ${requestModel}`;
655
+ const effectiveCtx = (0, helpers_1.getFrameworkParentContext)() ?? api_1.context.active();
656
+ const span = tracer.startSpan(spanName, {
657
+ kind: api_1.SpanKind.CLIENT,
658
+ attributes: spanCreationAttrs(semantic_convention_1.default.GEN_AI_OPERATION_TYPE_AUDIO, requestModel),
659
+ }, effectiveCtx);
660
+ return api_1.context.with(api_1.trace.setSpan(effectiveCtx, span), async () => {
661
+ const captureContent = config_1.default.captureMessageContent;
583
662
  let metricParams;
584
663
  try {
585
664
  const response = await originalMethod.apply(this, args);
586
665
  const { input, user, voice, response_format = 'mp3', speed = 1 } = args[0];
587
- span.setAttribute(semantic_convention_1.default.GEN_AI_OPERATION, semantic_convention_1.default.GEN_AI_OPERATION_TYPE_AUDIO);
588
- const model = response.model || 'tts-1';
589
- const pricingInfo = await config_1.default.updatePricingJson(config_1.default.pricing_json);
590
- // Calculate cost of the operation
591
- const cost = helpers_1.default.getAudioModelCost(model, pricingInfo, input);
666
+ const responseModel = response.model || requestModel;
667
+ const pricingInfo = config_1.default.pricingInfo || {};
668
+ const cost = helpers_1.default.getAudioModelCost(responseModel, pricingInfo, input);
592
669
  OpenAIWrapper.setBaseSpanAttributes(span, {
593
670
  genAIEndpoint,
594
- model,
671
+ model: requestModel,
595
672
  user,
596
673
  cost,
597
674
  aiSystem: OpenAIWrapper.aiSystem,
598
675
  serverAddress: OpenAIWrapper.serverAddress,
599
676
  serverPort: OpenAIWrapper.serverPort,
600
677
  });
601
- // Request Params attributes : Start
602
678
  span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_AUDIO_VOICE, voice);
603
679
  span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_AUDIO_RESPONSE_FORMAT, response_format);
604
680
  span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_AUDIO_SPEED, speed);
605
- if (traceContent) {
681
+ if (captureContent) {
606
682
  span.setAttribute(semantic_convention_1.default.GEN_AI_INPUT_MESSAGES, input);
607
683
  }
608
- // Request Params attributes : End
609
- // Store metric parameters for use after span ends
610
684
  metricParams = {
611
685
  genAIEndpoint,
612
- model,
686
+ model: requestModel,
613
687
  user,
614
688
  cost,
615
689
  aiSystem: OpenAIWrapper.aiSystem,
@@ -618,10 +692,10 @@ class OpenAIWrapper extends base_wrapper_1.default {
618
692
  }
619
693
  catch (e) {
620
694
  helpers_1.default.handleException(span, e);
695
+ throw e;
621
696
  }
622
697
  finally {
623
698
  span.end();
624
- // Record metrics after span has ended if parameters are available
625
699
  if (metricParams) {
626
700
  base_wrapper_1.default.recordMetrics(span, metricParams);
627
701
  }
@@ -634,9 +708,17 @@ class OpenAIWrapper extends base_wrapper_1.default {
634
708
  const genAIEndpoint = 'openai.resources.responses';
635
709
  return (originalMethod) => {
636
710
  return async function (...args) {
637
- const span = tracer.startSpan(genAIEndpoint, { kind: api_1.SpanKind.CLIENT });
711
+ if ((0, helpers_1.isFrameworkLlmActive)())
712
+ return originalMethod.apply(this, args);
713
+ const requestModel = args[0]?.model || 'gpt-4o';
714
+ const spanName = `${semantic_convention_1.default.GEN_AI_OPERATION_TYPE_CHAT} ${requestModel}`;
715
+ const effectiveCtx = (0, helpers_1.getFrameworkParentContext)() ?? api_1.context.active();
716
+ const span = tracer.startSpan(spanName, {
717
+ kind: api_1.SpanKind.CLIENT,
718
+ attributes: spanCreationAttrs(semantic_convention_1.default.GEN_AI_OPERATION_TYPE_CHAT, requestModel),
719
+ }, effectiveCtx);
638
720
  return api_1.context
639
- .with(api_1.trace.setSpan(api_1.context.active(), span), async () => {
721
+ .with(api_1.trace.setSpan(effectiveCtx, span), async () => {
640
722
  return originalMethod.apply(this, args);
641
723
  })
642
724
  .then((response) => {
@@ -653,7 +735,16 @@ class OpenAIWrapper extends base_wrapper_1.default {
653
735
  })
654
736
  .catch((e) => {
655
737
  helpers_1.default.handleException(span, e);
738
+ base_wrapper_1.default.recordMetrics(span, {
739
+ genAIEndpoint,
740
+ model: requestModel,
741
+ aiSystem: OpenAIWrapper.aiSystem,
742
+ serverAddress: OpenAIWrapper.serverAddress,
743
+ serverPort: OpenAIWrapper.serverPort,
744
+ errorType: e?.constructor?.name || '_OTHER',
745
+ });
656
746
  span.end();
747
+ throw e;
657
748
  });
658
749
  };
659
750
  };
@@ -671,6 +762,7 @@ class OpenAIWrapper extends base_wrapper_1.default {
671
762
  }
672
763
  catch (e) {
673
764
  helpers_1.default.handleException(span, e);
765
+ throw e;
674
766
  }
675
767
  finally {
676
768
  span.end();
@@ -684,7 +776,6 @@ class OpenAIWrapper extends base_wrapper_1.default {
684
776
  const timestamps = [];
685
777
  const startTime = Date.now();
686
778
  try {
687
- const { input } = args[0];
688
779
  const result = {
689
780
  id: '',
690
781
  model: '',
@@ -700,7 +791,7 @@ class OpenAIWrapper extends base_wrapper_1.default {
700
791
  },
701
792
  };
702
793
  let llmResponse = '';
703
- let responseTools = [];
794
+ const responseTools = [];
704
795
  for await (const chunk of response) {
705
796
  timestamps.push(Date.now());
706
797
  if (chunk.type === 'response.output_text.delta') {
@@ -740,7 +831,6 @@ class OpenAIWrapper extends base_wrapper_1.default {
740
831
  }
741
832
  yield chunk;
742
833
  }
743
- // Construct output array
744
834
  if (llmResponse) {
745
835
  result.output.push({
746
836
  type: 'message',
@@ -750,7 +840,6 @@ class OpenAIWrapper extends base_wrapper_1.default {
750
840
  if (responseTools.length > 0) {
751
841
  result.output.push(...responseTools);
752
842
  }
753
- // Calculate TTFT and TBT
754
843
  const ttft = timestamps.length > 0 ? (timestamps[0] - startTime) / 1000 : 0;
755
844
  let tbt = 0;
756
845
  if (timestamps.length > 1) {
@@ -769,6 +858,7 @@ class OpenAIWrapper extends base_wrapper_1.default {
769
858
  }
770
859
  catch (e) {
771
860
  helpers_1.default.handleException(span, e);
861
+ throw e;
772
862
  }
773
863
  finally {
774
864
  span.end();
@@ -778,65 +868,57 @@ class OpenAIWrapper extends base_wrapper_1.default {
778
868
  }
779
869
  }
780
870
  static async _responsesCommonSetter({ args, genAIEndpoint, result, span, ttft = 0, tbt = 0, }) {
781
- const traceContent = config_1.default.traceContent;
871
+ const captureContent = config_1.default.captureMessageContent;
872
+ const requestModel = args[0]?.model || 'gpt-4o';
782
873
  const { input, temperature = 1.0, top_p = 1.0, max_output_tokens, reasoning, stream = false, } = args[0];
783
- // Normalize Responses API input to messages array for buildInputMessages
784
874
  const responsesMessages = typeof input === 'string'
785
875
  ? [{ role: 'user', content: input }]
786
876
  : (Array.isArray(input) ? input : []);
787
- // Request Params attributes
788
877
  span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_TEMPERATURE, temperature);
789
878
  span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_TOP_P, top_p);
790
- span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_MAX_TOKENS, max_output_tokens || -1);
879
+ if (max_output_tokens != null) {
880
+ span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_MAX_TOKENS, max_output_tokens);
881
+ }
791
882
  span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_IS_STREAM, stream);
792
883
  if (reasoning?.effort) {
793
884
  span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_REASONING_EFFORT, reasoning.effort);
794
885
  }
795
- if (traceContent) {
886
+ if (captureContent) {
796
887
  span.setAttribute(semantic_convention_1.default.GEN_AI_INPUT_MESSAGES, helpers_1.default.buildInputMessages(responsesMessages));
797
888
  }
798
- span.setAttribute(semantic_convention_1.default.GEN_AI_OPERATION, semantic_convention_1.default.GEN_AI_OPERATION_TYPE_CHAT);
799
- const model = result.model || 'gpt-4o';
800
- const responseModel = result.model || model;
801
- const pricingInfo = await config_1.default.updatePricingJson(config_1.default.pricing_json);
802
- // Calculate cost
889
+ const responseModel = result.model || requestModel;
890
+ const pricingInfo = config_1.default.pricingInfo || {};
803
891
  const inputTokens = result.usage?.input_tokens || 0;
804
892
  const outputTokens = result.usage?.output_tokens || 0;
805
- const cost = helpers_1.default.getChatModelCost(model, pricingInfo, inputTokens, outputTokens);
893
+ const cost = helpers_1.default.getChatModelCost(requestModel, pricingInfo, inputTokens, outputTokens);
806
894
  OpenAIWrapper.setBaseSpanAttributes(span, {
807
895
  genAIEndpoint,
808
- model,
896
+ model: requestModel,
809
897
  user: '',
810
898
  cost,
811
899
  aiSystem: OpenAIWrapper.aiSystem,
812
900
  serverAddress: OpenAIWrapper.serverAddress,
813
901
  serverPort: OpenAIWrapper.serverPort,
814
902
  });
815
- // Response attributes
816
903
  span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_ID, result.id);
817
904
  span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_MODEL, responseModel);
818
905
  span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_FINISH_REASON, [result.status || 'completed']);
819
906
  span.setAttribute(semantic_convention_1.default.GEN_AI_OUTPUT_TYPE, semantic_convention_1.default.GEN_AI_OUTPUT_TYPE_TEXT);
907
+ span.setAttribute(semantic_convention_1.default.OPENAI_API_TYPE, 'responses');
820
908
  if (result.service_tier) {
821
- span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_SERVICE_TIER, result.service_tier);
909
+ span.setAttribute(semantic_convention_1.default.OPENAI_RESPONSE_SERVICE_TIER, result.service_tier);
822
910
  }
823
- // Token usage
824
911
  span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_INPUT_TOKENS, inputTokens);
825
912
  span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_OUTPUT_TOKENS, outputTokens);
826
- span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_TOTAL_TOKENS, inputTokens + outputTokens);
827
- span.setAttribute(semantic_convention_1.default.GEN_AI_CLIENT_TOKEN_USAGE, inputTokens + outputTokens);
828
- // Reasoning tokens
829
913
  if (result.usage?.output_tokens_details?.reasoning_tokens) {
830
914
  span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_REASONING_TOKENS, result.usage.output_tokens_details.reasoning_tokens);
831
915
  }
832
- // TTFT and TBT metrics
833
916
  if (ttft > 0) {
834
917
  span.setAttribute(semantic_convention_1.default.GEN_AI_SERVER_TTFT, ttft);
835
918
  }
836
919
  if (tbt > 0) {
837
920
  span.setAttribute(semantic_convention_1.default.GEN_AI_SERVER_TBT, tbt);
838
921
  }
839
- // Extract completion text from output
840
922
  let completionText = '';
841
923
  if (result.output && Array.isArray(result.output)) {
842
924
  for (const item of result.output) {
@@ -849,13 +931,11 @@ class OpenAIWrapper extends base_wrapper_1.default {
849
931
  }
850
932
  }
851
933
  }
852
- // Tool calls handling for Responses API
853
934
  const toolCalls = result.tools || [];
854
935
  if (toolCalls.length > 0) {
855
936
  const toolNames = toolCalls.map((t) => t.name || '').filter(Boolean);
856
937
  const toolIds = toolCalls.map((t) => t.call_id || '').filter(Boolean);
857
938
  const toolArgs = toolCalls.map((t) => t.arguments || '').filter(Boolean);
858
- const toolTypes = toolCalls.map((t) => t.type || '').filter(Boolean);
859
939
  if (toolNames.length > 0) {
860
940
  span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_NAME, toolNames.join(', '));
861
941
  }
@@ -863,21 +943,40 @@ class OpenAIWrapper extends base_wrapper_1.default {
863
943
  span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_CALL_ID, toolIds.join(', '));
864
944
  }
865
945
  if (toolArgs.length > 0) {
866
- span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_CALL_ARGUMENTS, toolArgs.join(', '));
867
- }
868
- if (toolTypes.length > 0) {
869
- const toolTypesStr = toolTypes.join(', ');
870
- span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_TYPE, toolTypesStr);
871
- span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_TYPE_OTEL, toolTypesStr);
946
+ span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_ARGS, toolArgs.join(', '));
872
947
  }
873
948
  }
874
- // Content
875
- if (traceContent) {
876
- span.setAttribute(semantic_convention_1.default.GEN_AI_OUTPUT_MESSAGES, helpers_1.default.buildOutputMessages(completionText, result.status || 'stop'));
949
+ let inputMessagesJson;
950
+ let outputMessagesJson;
951
+ if (captureContent) {
952
+ outputMessagesJson = helpers_1.default.buildOutputMessages(completionText, result.status || 'stop');
953
+ span.setAttribute(semantic_convention_1.default.GEN_AI_OUTPUT_MESSAGES, outputMessagesJson);
954
+ inputMessagesJson = helpers_1.default.buildInputMessages(responsesMessages);
955
+ }
956
+ if (!config_1.default.disableEvents) {
957
+ const eventAttrs = {
958
+ [semantic_convention_1.default.GEN_AI_OPERATION]: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_CHAT,
959
+ [semantic_convention_1.default.GEN_AI_REQUEST_MODEL]: requestModel,
960
+ [semantic_convention_1.default.GEN_AI_RESPONSE_MODEL]: responseModel,
961
+ [semantic_convention_1.default.SERVER_ADDRESS]: OpenAIWrapper.serverAddress,
962
+ [semantic_convention_1.default.SERVER_PORT]: OpenAIWrapper.serverPort,
963
+ [semantic_convention_1.default.GEN_AI_RESPONSE_ID]: result.id,
964
+ [semantic_convention_1.default.GEN_AI_RESPONSE_FINISH_REASON]: [result.status || 'completed'],
965
+ [semantic_convention_1.default.GEN_AI_OUTPUT_TYPE]: semantic_convention_1.default.GEN_AI_OUTPUT_TYPE_TEXT,
966
+ [semantic_convention_1.default.GEN_AI_USAGE_INPUT_TOKENS]: inputTokens,
967
+ [semantic_convention_1.default.GEN_AI_USAGE_OUTPUT_TOKENS]: outputTokens,
968
+ };
969
+ if (captureContent) {
970
+ if (inputMessagesJson)
971
+ eventAttrs[semantic_convention_1.default.GEN_AI_INPUT_MESSAGES] = inputMessagesJson;
972
+ if (outputMessagesJson)
973
+ eventAttrs[semantic_convention_1.default.GEN_AI_OUTPUT_MESSAGES] = outputMessagesJson;
974
+ }
975
+ helpers_1.default.emitInferenceEvent(span, eventAttrs);
877
976
  }
878
977
  return {
879
978
  genAIEndpoint,
880
- model,
979
+ model: requestModel,
881
980
  user: '',
882
981
  cost,
883
982
  aiSystem: OpenAIWrapper.aiSystem,