@arizeai/openinference-instrumentation-openai 3.0.0 → 3.1.1

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,
@@ -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);
@@ -209,7 +207,11 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
209
207
  },
210
208
  );
211
209
 
212
- const wrappedPromise = execPromise.then((result) => {
210
+ const wrappedPromiseThen = (
211
+ result:
212
+ | Stream<openai.Chat.Completions.ChatCompletionChunk>
213
+ | openai.Chat.Completions.ChatCompletion,
214
+ ) => {
213
215
  if (isChatCompletionResponse(result)) {
214
216
  // Record the results
215
217
  span.setAttributes({
@@ -232,7 +234,11 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
232
234
  }
233
235
 
234
236
  return result;
235
- });
237
+ };
238
+ const wrappedPromise = invokeMaybeAPIPromise(
239
+ execPromise,
240
+ wrappedPromiseThen,
241
+ );
236
242
  return context.bind(execContext, wrappedPromise);
237
243
  };
238
244
  },
@@ -271,9 +277,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
271
277
  );
272
278
  const execContext = getExecContext(span);
273
279
 
274
- const execPromise = safeExecuteInTheMiddle<
275
- ReturnType<CompletionsCreateType>
276
- >(
280
+ const execPromise = safeExecuteInTheMiddle(
277
281
  () => {
278
282
  return context.with(trace.setSpan(execContext, span), () => {
279
283
  return original.apply(this, args);
@@ -291,7 +295,9 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
291
295
  }
292
296
  },
293
297
  );
294
- const wrappedPromise = execPromise.then((result) => {
298
+ const wrappedPromiseThen = (
299
+ result: Completion | Stream<Completion>,
300
+ ) => {
295
301
  if (isCompletionResponse(result)) {
296
302
  // Record the results
297
303
  span.setAttributes({
@@ -306,7 +312,11 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
306
312
  span.end();
307
313
  }
308
314
  return result;
309
- });
315
+ };
316
+ const wrappedPromise = invokeMaybeAPIPromise(
317
+ execPromise,
318
+ wrappedPromiseThen,
319
+ );
310
320
  return context.bind(execContext, wrappedPromise);
311
321
  };
312
322
  },
@@ -343,9 +353,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
343
353
  },
344
354
  });
345
355
  const execContext = getExecContext(span);
346
- const execPromise = safeExecuteInTheMiddle<
347
- ReturnType<EmbeddingsCreateType>
348
- >(
356
+ const execPromise = safeExecuteInTheMiddle(
349
357
  () => {
350
358
  return context.with(trace.setSpan(execContext, span), () => {
351
359
  return original.apply(this, args);
@@ -363,7 +371,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
363
371
  }
364
372
  },
365
373
  );
366
- const wrappedPromise = execPromise.then((result) => {
374
+ const wrappedPromiseThen = (result: CreateEmbeddingResponse) => {
367
375
  if (result) {
368
376
  // Record the results
369
377
  span.setAttributes({
@@ -374,7 +382,11 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
374
382
  span.setStatus({ code: SpanStatusCode.OK });
375
383
  span.end();
376
384
  return result;
377
- });
385
+ };
386
+ const wrappedPromise = invokeMaybeAPIPromise(
387
+ execPromise,
388
+ wrappedPromiseThen,
389
+ );
378
390
  return context.bind(execContext, wrappedPromise);
379
391
  };
380
392
  },
@@ -416,9 +428,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
416
428
  },
417
429
  );
418
430
  const execContext = getExecContext(span);
419
- const execPromise = safeExecuteInTheMiddle<
420
- ReturnType<ResponsesCreateType>
421
- >(
431
+ const execPromise = safeExecuteInTheMiddle(
422
432
  () => {
423
433
  return context.with(trace.setSpan(execContext, span), () => {
424
434
  return original.apply(this, args);
@@ -436,7 +446,9 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
436
446
  }
437
447
  },
438
448
  );
439
- const wrappedPromise = execPromise.then((result) => {
449
+ const wrappedPromiseThen = (
450
+ result: Stream<ResponseStreamEvent> | ResponseType,
451
+ ) => {
440
452
  const recordSpan = (result?: ResponseType) => {
441
453
  if (!result) {
442
454
  span.setStatus({ code: SpanStatusCode.ERROR });
@@ -469,7 +481,11 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
469
481
  }
470
482
 
471
483
  return result;
472
- });
484
+ };
485
+ const wrappedPromise = invokeMaybeAPIPromise(
486
+ execPromise,
487
+ wrappedPromiseThen,
488
+ );
473
489
  return context.bind(execContext, wrappedPromise);
474
490
  };
475
491
  },
@@ -926,3 +942,46 @@ function getToolAndFunctionCallAttributesFromStreamChunk(
926
942
  }
927
943
  return attributes;
928
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
+ }
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 = "3.0.0";
2
+ export const VERSION = "3.1.1";