@arizeai/openinference-instrumentation-openai 0.6.0 → 1.1.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.
@@ -40,7 +40,11 @@ import {
40
40
  import { assertUnreachable, isString } from "./typeUtils";
41
41
  import { isTracingSuppressed } from "@opentelemetry/core";
42
42
 
43
- import { safelyJSONStringify } from "@arizeai/openinference-core";
43
+ import {
44
+ OITracer,
45
+ safelyJSONStringify,
46
+ TraceConfigOptions,
47
+ } from "@arizeai/openinference-core";
44
48
 
45
49
  const MODULE_NAME = "openai";
46
50
 
@@ -74,13 +78,34 @@ function getExecContext(span: Span) {
74
78
  }
75
79
  return execContext;
76
80
  }
81
+ /**
82
+ * An auto instrumentation class for OpenAI that creates {@link https://github.com/Arize-ai/openinference/blob/main/spec/semantic_conventions.md|OpenInference} Compliant spans for the OpenAI API
83
+ * @param instrumentationConfig The config for the instrumentation @see {@link InstrumentationConfig}
84
+ * @param traceConfig The OpenInference trace configuration. Can be used to mask or redact sensitive information on spans. @see {@link TraceConfigOptions}
85
+ */
77
86
  export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
78
- constructor(config?: InstrumentationConfig) {
87
+ private oiTracer: OITracer;
88
+ constructor({
89
+ instrumentationConfig,
90
+ traceConfig,
91
+ }: {
92
+ /**
93
+ * The config for the instrumentation
94
+ * @see {@link InstrumentationConfig}
95
+ */
96
+ instrumentationConfig?: InstrumentationConfig;
97
+ /**
98
+ * The OpenInference trace configuration. Can be used to mask or redact sensitive information on spans.
99
+ * @see {@link TraceConfigOptions}
100
+ */
101
+ traceConfig?: TraceConfigOptions;
102
+ } = {}) {
79
103
  super(
80
104
  "@arizeai/openinference-instrumentation-openai",
81
105
  VERSION,
82
- Object.assign({}, config),
106
+ Object.assign({}, instrumentationConfig),
83
107
  );
108
+ this.oiTracer = new OITracer({ tracer: this.tracer, traceConfig });
84
109
  }
85
110
 
86
111
  protected init(): InstrumentationModuleDefinition<typeof openai> {
@@ -131,7 +156,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
131
156
  ) {
132
157
  const body = args[0];
133
158
  const { messages: _messages, ...invocationParameters } = body;
134
- const span = instrumentation.tracer.startSpan(
159
+ const span = instrumentation.oiTracer.startSpan(
135
160
  `OpenAI Chat Completions`,
136
161
  {
137
162
  kind: SpanKind.INTERNAL,
@@ -150,13 +175,12 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
150
175
  },
151
176
  },
152
177
  );
153
-
154
178
  const execContext = getExecContext(span);
155
179
  const execPromise = safeExecuteInTheMiddle<
156
180
  ReturnType<ChatCompletionCreateType>
157
181
  >(
158
182
  () => {
159
- return context.with(execContext, () => {
183
+ return context.with(trace.setSpan(execContext, span), () => {
160
184
  return original.apply(this, args);
161
185
  });
162
186
  },
@@ -216,26 +240,29 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
216
240
  ) {
217
241
  const body = args[0];
218
242
  const { prompt: _prompt, ...invocationParameters } = body;
219
- const span = instrumentation.tracer.startSpan(`OpenAI Completions`, {
220
- kind: SpanKind.INTERNAL,
221
- attributes: {
222
- [SemanticConventions.OPENINFERENCE_SPAN_KIND]:
223
- OpenInferenceSpanKind.LLM,
224
- [SemanticConventions.LLM_MODEL_NAME]: body.model,
225
- [SemanticConventions.LLM_INVOCATION_PARAMETERS]:
226
- JSON.stringify(invocationParameters),
227
- [SemanticConventions.LLM_SYSTEM]: LLMSystem.OPENAI,
228
- [SemanticConventions.LLM_PROVIDER]: LLMProvider.OPENAI,
229
- ...getCompletionInputValueAndMimeType(body),
243
+ const span = instrumentation.oiTracer.startSpan(
244
+ `OpenAI Completions`,
245
+ {
246
+ kind: SpanKind.INTERNAL,
247
+ attributes: {
248
+ [SemanticConventions.OPENINFERENCE_SPAN_KIND]:
249
+ OpenInferenceSpanKind.LLM,
250
+ [SemanticConventions.LLM_MODEL_NAME]: body.model,
251
+ [SemanticConventions.LLM_INVOCATION_PARAMETERS]:
252
+ JSON.stringify(invocationParameters),
253
+ [SemanticConventions.LLM_SYSTEM]: LLMSystem.OPENAI,
254
+ [SemanticConventions.LLM_PROVIDER]: LLMProvider.OPENAI,
255
+ ...getCompletionInputValueAndMimeType(body),
256
+ },
230
257
  },
231
- });
258
+ );
232
259
  const execContext = getExecContext(span);
233
260
 
234
261
  const execPromise = safeExecuteInTheMiddle<
235
262
  ReturnType<CompletionsCreateType>
236
263
  >(
237
264
  () => {
238
- return context.with(execContext, () => {
265
+ return context.with(trace.setSpan(execContext, span), () => {
239
266
  return original.apply(this, args);
240
267
  });
241
268
  },
@@ -287,7 +314,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
287
314
  const body = args[0];
288
315
  const { input } = body;
289
316
  const isStringInput = typeof input === "string";
290
- const span = instrumentation.tracer.startSpan(`OpenAI Embeddings`, {
317
+ const span = instrumentation.oiTracer.startSpan(`OpenAI Embeddings`, {
291
318
  kind: SpanKind.INTERNAL,
292
319
  attributes: {
293
320
  [SemanticConventions.OPENINFERENCE_SPAN_KIND]:
@@ -307,7 +334,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
307
334
  ReturnType<EmbeddingsCreateType>
308
335
  >(
309
336
  () => {
310
- return context.with(execContext, () => {
337
+ return context.with(trace.setSpan(execContext, span), () => {
311
338
  return original.apply(this, args);
312
339
  });
313
340
  },
@@ -345,7 +372,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
345
372
  // This can fail if the module is made immutable via the runtime or bundler
346
373
  module.openInferencePatched = true;
347
374
  } catch (e) {
348
- diag.warn(`Failed to set ${MODULE_NAME} patched flag on the module`, e);
375
+ diag.debug(`Failed to set ${MODULE_NAME} patched flag on the module`, e);
349
376
  }
350
377
 
351
378
  return module;
@@ -475,15 +502,21 @@ function getChatCompletionInputMessageAttributes(
475
502
  case "assistant":
476
503
  if (message.tool_calls) {
477
504
  message.tool_calls.forEach((toolCall, index) => {
505
+ const toolCallIndexPrefix = `${SemanticConventions.MESSAGE_TOOL_CALLS}.${index}.`;
506
+
507
+ // Add the tool call id if it exists
508
+ if (toolCall.id) {
509
+ attributes[
510
+ `${toolCallIndexPrefix}${SemanticConventions.TOOL_CALL_ID}`
511
+ ] = toolCall.id;
512
+ }
478
513
  // Make sure the tool call has a function
479
514
  if (toolCall.function) {
480
- const toolCallIndexPrefix = `${SemanticConventions.MESSAGE_TOOL_CALLS}.${index}.`;
481
515
  attributes[
482
- toolCallIndexPrefix + SemanticConventions.TOOL_CALL_FUNCTION_NAME
516
+ `${toolCallIndexPrefix}${SemanticConventions.TOOL_CALL_FUNCTION_NAME}`
483
517
  ] = toolCall.function.name;
484
518
  attributes[
485
- toolCallIndexPrefix +
486
- SemanticConventions.TOOL_CALL_FUNCTION_ARGUMENTS_JSON
519
+ `${toolCallIndexPrefix}${SemanticConventions.TOOL_CALL_FUNCTION_ARGUMENTS_JSON}`
487
520
  ] = toolCall.function.arguments;
488
521
  }
489
522
  });
@@ -493,8 +526,10 @@ function getChatCompletionInputMessageAttributes(
493
526
  attributes[SemanticConventions.MESSAGE_FUNCTION_CALL_NAME] = message.name;
494
527
  break;
495
528
  case "tool":
496
- // There's nothing to add for the tool. There is a tool_id, but there are no
497
- // semantic conventions for it
529
+ if (message.tool_call_id) {
530
+ attributes[`${SemanticConventions.MESSAGE_TOOL_CALL_ID}`] =
531
+ message.tool_call_id;
532
+ }
498
533
  break;
499
534
  case "system":
500
535
  // There's nothing to add for the system. Content is captured above
@@ -585,6 +620,12 @@ function getChatCompletionOutputMessageAttributes(
585
620
  if (message.tool_calls) {
586
621
  message.tool_calls.forEach((toolCall, index) => {
587
622
  const toolCallIndexPrefix = `${SemanticConventions.MESSAGE_TOOL_CALLS}.${index}.`;
623
+ // Add the tool call id if it exists
624
+ if (toolCall.id) {
625
+ attributes[
626
+ `${toolCallIndexPrefix}${SemanticConventions.TOOL_CALL_ID}`
627
+ ] = toolCall.id;
628
+ }
588
629
  // Double check that the tool call has a function
589
630
  // NB: OpenAI only supports tool calls with functions right now but this may change
590
631
  if (toolCall.function) {
@@ -731,6 +772,12 @@ function getToolAndFunctionCallAttributesFromStreamChunk(
731
772
  if (choice.delta.tool_calls) {
732
773
  choice.delta.tool_calls.forEach((toolCall, index) => {
733
774
  const toolCallIndexPrefix = `${SemanticConventions.MESSAGE_TOOL_CALLS}.${index}.`;
775
+ // Add the tool call id if it exists
776
+ if (toolCall.id) {
777
+ attributes[
778
+ `${toolCallIndexPrefix}${SemanticConventions.TOOL_CALL_ID}`
779
+ ] = toolCall.id;
780
+ }
734
781
  // Double check that the tool call has a function
735
782
  // NB: OpenAI only supports tool calls with functions right now but this may change
736
783
  if (toolCall.function) {
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  // this is an auto-generated file. see js/scripts/version-update.js
2
- export const VERSION = "0.6.0";
2
+ export const VERSION = "1.1.0";