@arizeai/openinference-instrumentation-openai 4.2.0 → 4.2.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arizeai/openinference-instrumentation-openai",
3
- "version": "4.2.0",
3
+ "version": "4.2.1",
4
4
  "private": false,
5
5
  "description": "OpenInference instrumentation for OpenAI",
6
6
  "keywords": [],
@@ -28,7 +28,7 @@
28
28
  "@opentelemetry/api": "^1.9.0",
29
29
  "@opentelemetry/core": "^2.8.0",
30
30
  "@opentelemetry/instrumentation": "^0.46.0",
31
- "@arizeai/openinference-core": "2.5.3",
31
+ "@arizeai/openinference-core": "2.5.4",
32
32
  "@arizeai/openinference-semantic-conventions": "2.8.0"
33
33
  },
34
34
  "devDependencies": {
@@ -99,6 +99,19 @@ export function getProviderFromHost(host: string): LLMProvider | undefined {
99
99
  */
100
100
  let _isOpenInferencePatched = false;
101
101
 
102
+ /**
103
+ * The OpenAI classes that have already been patched, tracked by identity.
104
+ * The SDK ships separate CJS and ESM builds with separate class objects, so a
105
+ * module-global boolean cannot guard them independently: whichever build was
106
+ * patched first would block the other one forever (#3557). A Set is
107
+ * scoped to the object, and needs no write to the module, so it also keeps
108
+ * the double-patch guard working when the module is immutable (e.g. Deno,
109
+ * webpack) and the `openInferencePatched` property cannot be set. Entries
110
+ * are removed when their wrappers are removed, so the Set does not retain
111
+ * unpatched SDK builds.
112
+ */
113
+ const _patchedModules = new Set<object>();
114
+
102
115
  /**
103
116
  * function to check if instrumentation is enabled / disabled
104
117
  */
@@ -167,6 +180,7 @@ function getLLMProvider(clientInstance: unknown): LLMProvider | undefined {
167
180
  } catch (error) {
168
181
  diag.debug("Failed to determine LLM provider from instance", error);
169
182
  }
183
+ return undefined;
170
184
  }
171
185
 
172
186
  /**
@@ -178,6 +192,10 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
178
192
  private oiTracer: OITracer;
179
193
  private tracerProvider?: TracerProvider;
180
194
  private traceConfig?: TraceConfigOptions;
195
+ private readonly patchedModuleExports = new Map<
196
+ typeof openai.OpenAI,
197
+ typeof openai & { openInferencePatched?: boolean }
198
+ >();
181
199
  constructor({
182
200
  instrumentationConfig,
183
201
  traceConfig,
@@ -230,6 +248,13 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
230
248
  this.patch(module);
231
249
  }
232
250
 
251
+ disable(): void {
252
+ super.disable();
253
+ for (const moduleExports of [...this.patchedModuleExports.values()]) {
254
+ this.unpatch(moduleExports);
255
+ }
256
+ }
257
+
233
258
  get tracer(): Tracer {
234
259
  if (this.tracerProvider) {
235
260
  return this.tracerProvider.getTracer(this.instrumentationName, this.instrumentationVersion);
@@ -254,7 +279,9 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
254
279
  moduleVersion?: string,
255
280
  ) {
256
281
  diag.debug(`Applying patch for ${MODULE_NAME}@${moduleVersion}`);
257
- if (module?.openInferencePatched || _isOpenInferencePatched) {
282
+ // WeakSet.has() returns false for non-objects, so an unexpected module
283
+ // shape falls through here and fails loudly below instead.
284
+ if (module?.openInferencePatched || _patchedModules.has(module.OpenAI)) {
258
285
  return module;
259
286
  }
260
287
  // eslint-disable-next-line @typescript-eslint/no-this-alias
@@ -558,6 +585,8 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
558
585
  }
559
586
 
560
587
  _isOpenInferencePatched = true;
588
+ _patchedModules.add(module.OpenAI);
589
+ this.patchedModuleExports.set(module.OpenAI, module);
561
590
  try {
562
591
  // This can fail if the module is made immutable via the runtime or bundler
563
592
  module.openInferencePatched = true;
@@ -579,7 +608,10 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
579
608
  this._unwrap(moduleExports.OpenAI.Completions.prototype, "create");
580
609
  this._unwrap(moduleExports.OpenAI.Embeddings.prototype, "create");
581
610
 
582
- _isOpenInferencePatched = false;
611
+ // Keyed the same way patch() keys it, so a re-patch is possible after.
612
+ _patchedModules.delete(moduleExports.OpenAI);
613
+ this.patchedModuleExports.delete(moduleExports.OpenAI);
614
+ _isOpenInferencePatched = _patchedModules.size > 0;
583
615
  try {
584
616
  // This can fail if the module is made immutable via the runtime or bundler
585
617
  moduleExports.openInferencePatched = false;
@@ -624,7 +656,7 @@ function isPromptStringArray(
624
656
  * Converts the body of a chat completions request to LLM input messages
625
657
  */
626
658
  function getLLMInputMessagesAttributes(body: ChatCompletionCreateParamsBase): Attributes {
627
- return body.messages.reduce((acc, message, index) => {
659
+ return body.messages.reduce<Attributes>((acc, message, index) => {
628
660
  const messageAttributes = getChatCompletionInputMessageAttributes(message);
629
661
  const indexPrefix = `${SemanticConventions.LLM_INPUT_MESSAGES}.${index}.`;
630
662
  // Flatten the attributes on the index prefix
@@ -632,7 +664,7 @@ function getLLMInputMessagesAttributes(body: ChatCompletionCreateParamsBase): At
632
664
  acc[`${indexPrefix}${key}`] = value;
633
665
  }
634
666
  return acc;
635
- }, {} as Attributes);
667
+ }, {});
636
668
  }
637
669
 
638
670
  /**
@@ -780,7 +812,7 @@ function getChatCompletionLLMOutputMessagesAttributes(chatCompletion: ChatComple
780
812
  if (!choice) {
781
813
  return {};
782
814
  }
783
- return [choice.message].reduce((acc, message, index) => {
815
+ return [choice.message].reduce<Attributes>((acc, message, index) => {
784
816
  const indexPrefix = `${SemanticConventions.LLM_OUTPUT_MESSAGES}.${index}.`;
785
817
  const messageAttributes = getChatCompletionOutputMessageAttributes(message);
786
818
  // Flatten the attributes on the index prefix
@@ -788,7 +820,7 @@ function getChatCompletionLLMOutputMessagesAttributes(chatCompletion: ChatComple
788
820
  acc[`${indexPrefix}${key}`] = value;
789
821
  }
790
822
  return acc;
791
- }, {} as Attributes);
823
+ }, {});
792
824
  }
793
825
 
794
826
  /**
@@ -867,11 +899,11 @@ function getEmbeddingTextAttributes(request: EmbeddingCreateParams): Attributes
867
899
  request.input.length > 0 &&
868
900
  typeof request.input[0] === "string"
869
901
  ) {
870
- return request.input.reduce((acc, input, index) => {
902
+ return request.input.reduce<Attributes>((acc, input, index) => {
871
903
  const indexPrefix = `${SemanticConventions.EMBEDDING_EMBEDDINGS}.${index}.`;
872
904
  acc[`${indexPrefix}${SemanticConventions.EMBEDDING_TEXT}`] = input;
873
905
  return acc;
874
- }, {} as Attributes);
906
+ }, {});
875
907
  }
876
908
  // Ignore other cases where input is a number or an array of numbers
877
909
  return {};
@@ -881,11 +913,11 @@ function getEmbeddingTextAttributes(request: EmbeddingCreateParams): Attributes
881
913
  * Converts the embedding result payload to embedding attributes
882
914
  */
883
915
  function getEmbeddingEmbeddingsAttributes(response: CreateEmbeddingResponse): Attributes {
884
- return response.data.reduce((acc, embedding, index) => {
916
+ return response.data.reduce<Attributes>((acc, embedding, index) => {
885
917
  const indexPrefix = `${SemanticConventions.EMBEDDING_EMBEDDINGS}.${index}.`;
886
918
  acc[`${indexPrefix}${SemanticConventions.EMBEDDING_VECTOR}`] = embedding.embedding;
887
919
  return acc;
888
- }, {} as Attributes);
920
+ }, {});
889
921
  }
890
922
 
891
923
  /**
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 = "4.2.0";
2
+ export const VERSION = "4.2.1";