@arizeai/openinference-instrumentation-openai 2.3.1 → 3.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.
@@ -1,4 +1,4 @@
1
- import openai from "openai";
1
+ import openai, { APIPromise } from "openai";
2
2
  import {
3
3
  InstrumentationBase,
4
4
  InstrumentationConfig,
@@ -123,7 +123,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
123
123
  protected init(): InstrumentationModuleDefinition<typeof openai> {
124
124
  const module = new InstrumentationNodeModuleDefinition<typeof openai>(
125
125
  "openai",
126
- ["^4.0.0"],
126
+ ["^5.0.0"],
127
127
  this.patch.bind(this),
128
128
  this.unpatch.bind(this),
129
129
  );
@@ -188,9 +188,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
188
188
  },
189
189
  );
190
190
  const execContext = getExecContext(span);
191
- const execPromise = safeExecuteInTheMiddle<
192
- ReturnType<ChatCompletionCreateType>
193
- >(
191
+ const execPromise = safeExecuteInTheMiddle(
194
192
  () => {
195
193
  return context.with(trace.setSpan(execContext, span), () => {
196
194
  return original.apply(this, args);
@@ -208,7 +206,12 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
208
206
  }
209
207
  },
210
208
  );
211
- const wrappedPromise = execPromise.then((result) => {
209
+
210
+ const wrappedPromiseThen = (
211
+ result:
212
+ | Stream<openai.Chat.Completions.ChatCompletionChunk>
213
+ | openai.Chat.Completions.ChatCompletion,
214
+ ) => {
212
215
  if (isChatCompletionResponse(result)) {
213
216
  // Record the results
214
217
  span.setAttributes({
@@ -231,7 +234,11 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
231
234
  }
232
235
 
233
236
  return result;
234
- });
237
+ };
238
+ const wrappedPromise = invokeMaybeAPIPromise(
239
+ execPromise,
240
+ wrappedPromiseThen,
241
+ );
235
242
  return context.bind(execContext, wrappedPromise);
236
243
  };
237
244
  },
@@ -270,9 +277,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
270
277
  );
271
278
  const execContext = getExecContext(span);
272
279
 
273
- const execPromise = safeExecuteInTheMiddle<
274
- ReturnType<CompletionsCreateType>
275
- >(
280
+ const execPromise = safeExecuteInTheMiddle(
276
281
  () => {
277
282
  return context.with(trace.setSpan(execContext, span), () => {
278
283
  return original.apply(this, args);
@@ -290,7 +295,9 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
290
295
  }
291
296
  },
292
297
  );
293
- const wrappedPromise = execPromise.then((result) => {
298
+ const wrappedPromiseThen = (
299
+ result: Completion | Stream<Completion>,
300
+ ) => {
294
301
  if (isCompletionResponse(result)) {
295
302
  // Record the results
296
303
  span.setAttributes({
@@ -305,7 +312,11 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
305
312
  span.end();
306
313
  }
307
314
  return result;
308
- });
315
+ };
316
+ const wrappedPromise = invokeMaybeAPIPromise(
317
+ execPromise,
318
+ wrappedPromiseThen,
319
+ );
309
320
  return context.bind(execContext, wrappedPromise);
310
321
  };
311
322
  },
@@ -342,9 +353,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
342
353
  },
343
354
  });
344
355
  const execContext = getExecContext(span);
345
- const execPromise = safeExecuteInTheMiddle<
346
- ReturnType<EmbeddingsCreateType>
347
- >(
356
+ const execPromise = safeExecuteInTheMiddle(
348
357
  () => {
349
358
  return context.with(trace.setSpan(execContext, span), () => {
350
359
  return original.apply(this, args);
@@ -362,7 +371,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
362
371
  }
363
372
  },
364
373
  );
365
- const wrappedPromise = execPromise.then((result) => {
374
+ const wrappedPromiseThen = (result: CreateEmbeddingResponse) => {
366
375
  if (result) {
367
376
  // Record the results
368
377
  span.setAttributes({
@@ -373,7 +382,11 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
373
382
  span.setStatus({ code: SpanStatusCode.OK });
374
383
  span.end();
375
384
  return result;
376
- });
385
+ };
386
+ const wrappedPromise = invokeMaybeAPIPromise(
387
+ execPromise,
388
+ wrappedPromiseThen,
389
+ );
377
390
  return context.bind(execContext, wrappedPromise);
378
391
  };
379
392
  },
@@ -415,9 +428,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
415
428
  },
416
429
  );
417
430
  const execContext = getExecContext(span);
418
- const execPromise = safeExecuteInTheMiddle<
419
- ReturnType<ResponsesCreateType>
420
- >(
431
+ const execPromise = safeExecuteInTheMiddle(
421
432
  () => {
422
433
  return context.with(trace.setSpan(execContext, span), () => {
423
434
  return original.apply(this, args);
@@ -435,7 +446,9 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
435
446
  }
436
447
  },
437
448
  );
438
- const wrappedPromise = execPromise.then((result) => {
449
+ const wrappedPromiseThen = (
450
+ result: Stream<ResponseStreamEvent> | ResponseType,
451
+ ) => {
439
452
  const recordSpan = (result?: ResponseType) => {
440
453
  if (!result) {
441
454
  span.setStatus({ code: SpanStatusCode.ERROR });
@@ -468,7 +481,11 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
468
481
  }
469
482
 
470
483
  return result;
471
- });
484
+ };
485
+ const wrappedPromise = invokeMaybeAPIPromise(
486
+ execPromise,
487
+ wrappedPromiseThen,
488
+ );
472
489
  return context.bind(execContext, wrappedPromise);
473
490
  };
474
491
  },
@@ -925,3 +942,46 @@ function getToolAndFunctionCallAttributesFromStreamChunk(
925
942
  }
926
943
  return attributes;
927
944
  }
945
+
946
+ /**
947
+ * Type-guard that checks if the promise is an APIPromise.
948
+ *
949
+ * APIPromise is a class from openai that wraps promises with special behavior.
950
+ *
951
+ * @param promise - The promise to check
952
+ * @returns True if the promise is an APIPromise, false otherwise
953
+ */
954
+ function isAPIPromise<T>(promise: unknown): promise is APIPromise<T> {
955
+ return promise instanceof APIPromise;
956
+ }
957
+
958
+ /**
959
+ * Invokes the thennable of a promise or an APIPromise.
960
+ *
961
+ * This is necessary to safely invoke promises returned by openai sdk methods.
962
+ *
963
+ * Without this wrapper, instrumentation will "consume" returned APIPromise instances,
964
+ * Making them unusable by other openai sdk methods, such as when completions.parse internally
965
+ * calls completions.create, expecting an APIPromise which we would otherwise consume by calling `then` on it.
966
+ *
967
+ * @param promise - The promise to invoke the thennable of
968
+ * @param then - The thennable to invoke
969
+ * @returns The promise with the thennable invoked
970
+ */
971
+ function invokeMaybeAPIPromise<T>(
972
+ promise: T,
973
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
974
+ then: (value: any) => unknown,
975
+ ): T {
976
+ if (isAPIPromise<T>(promise)) {
977
+ return promise._thenUnwrap(then) as T;
978
+ } else if (promise instanceof Promise) {
979
+ return promise.then(then) as T;
980
+ } else {
981
+ // eslint-disable-next-line no-console
982
+ console.warn(
983
+ "Promise is not an APIPromise or a regular promise, cannot instrument.",
984
+ );
985
+ return promise;
986
+ }
987
+ }
@@ -220,7 +220,7 @@ export function getResponsesInputMessagesAttributes(
220
220
  if (typeof body.input === "string") {
221
221
  items.push({ content: body.input, role: "user" });
222
222
  } else {
223
- items.push(...body.input);
223
+ items.push(...(body.input ?? []));
224
224
  }
225
225
  items.forEach((item, index) => {
226
226
  const indexPrefix = `${SemanticConventions.LLM_INPUT_MESSAGES}.${index}.`;
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 = "2.3.1";
2
+ export const VERSION = "3.1.0";