@arizeai/openinference-instrumentation-openai 0.0.3 → 0.0.4
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/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/instrumentation.d.ts +13 -19
- package/dist/esm/instrumentation.d.ts.map +1 -1
- package/dist/esm/instrumentation.js +98 -2
- package/dist/esm/instrumentation.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/esm/version.d.ts +2 -2
- package/dist/esm/version.js +2 -2
- package/dist/esnext/index.d.ts +1 -1
- package/dist/esnext/index.js +1 -1
- package/dist/esnext/instrumentation.d.ts +13 -19
- package/dist/esnext/instrumentation.d.ts.map +1 -1
- package/dist/esnext/instrumentation.js +93 -2
- package/dist/esnext/instrumentation.js.map +1 -1
- package/dist/esnext/tsconfig.esnext.tsbuildinfo +1 -1
- package/dist/esnext/version.d.ts +2 -2
- package/dist/esnext/version.js +2 -2
- package/dist/src/instrumentation.d.ts.map +1 -1
- package/dist/src/instrumentation.js +93 -2
- package/dist/src/instrumentation.js.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/dist/test/openai.test.js +43 -5
- package/dist/test/openai.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -2
- package/src/instrumentation.ts +135 -1
- package/src/version.ts +1 -1
package/package.json
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arizeai/openinference-instrumentation-openai",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "OpenInference instrumentation for OpenAI",
|
|
5
5
|
"private": false,
|
|
6
6
|
"main": "dist/src/index.js",
|
|
7
7
|
"module": "dist/esm/index.js",
|
|
8
8
|
"esnext": "dist/esnext/index.js",
|
|
9
9
|
"types": "dist/src/index.d.ts",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/Arize-ai/openinference.git"
|
|
13
|
+
},
|
|
10
14
|
"dependencies": {
|
|
11
15
|
"@opentelemetry/api": "^1.7.0",
|
|
12
16
|
"@opentelemetry/instrumentation": "^0.46.0",
|
|
13
|
-
"@arizeai/openinference-semantic-conventions": "0.0.
|
|
17
|
+
"@arizeai/openinference-semantic-conventions": "0.0.13"
|
|
14
18
|
},
|
|
15
19
|
"keywords": [],
|
|
16
20
|
"files": [
|
package/src/instrumentation.ts
CHANGED
|
@@ -26,6 +26,10 @@ import {
|
|
|
26
26
|
ChatCompletionCreateParamsBase,
|
|
27
27
|
} from "openai/resources/chat/completions";
|
|
28
28
|
import { Stream } from "openai/streaming";
|
|
29
|
+
import {
|
|
30
|
+
CreateEmbeddingResponse,
|
|
31
|
+
EmbeddingCreateParams,
|
|
32
|
+
} from "openai/resources";
|
|
29
33
|
|
|
30
34
|
const MODULE_NAME = "openai";
|
|
31
35
|
|
|
@@ -80,7 +84,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
80
84
|
const span = instrumentation.tracer.startSpan(
|
|
81
85
|
`OpenAI Chat Completions`,
|
|
82
86
|
{
|
|
83
|
-
kind: SpanKind.
|
|
87
|
+
kind: SpanKind.INTERNAL,
|
|
84
88
|
attributes: {
|
|
85
89
|
[SemanticConventions.OPENINFERENCE_SPAN_KIND]:
|
|
86
90
|
OpenInferenceSpanKind.LLM,
|
|
@@ -106,6 +110,11 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
106
110
|
// Push the error to the span
|
|
107
111
|
if (error) {
|
|
108
112
|
span.recordException(error);
|
|
113
|
+
span.setStatus({
|
|
114
|
+
code: SpanStatusCode.ERROR,
|
|
115
|
+
message: error.message,
|
|
116
|
+
});
|
|
117
|
+
span.end();
|
|
109
118
|
}
|
|
110
119
|
},
|
|
111
120
|
);
|
|
@@ -115,6 +124,12 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
115
124
|
span.setAttributes({
|
|
116
125
|
[SemanticConventions.OUTPUT_VALUE]: JSON.stringify(result),
|
|
117
126
|
[SemanticConventions.OUTPUT_MIME_TYPE]: MimeType.JSON,
|
|
127
|
+
// Override the model from the value sent by the server
|
|
128
|
+
[SemanticConventions.LLM_MODEL_NAME]: isChatCompletionResponse(
|
|
129
|
+
result,
|
|
130
|
+
)
|
|
131
|
+
? result.model
|
|
132
|
+
: body.model,
|
|
118
133
|
...getLLMOutputMessagesAttributes(result),
|
|
119
134
|
...getUsageAttributes(result),
|
|
120
135
|
});
|
|
@@ -127,6 +142,75 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
127
142
|
};
|
|
128
143
|
},
|
|
129
144
|
);
|
|
145
|
+
|
|
146
|
+
// Patch embeddings
|
|
147
|
+
type EmbeddingsCreateType =
|
|
148
|
+
typeof module.OpenAI.Embeddings.prototype.create;
|
|
149
|
+
this._wrap(
|
|
150
|
+
module.OpenAI.Embeddings.prototype,
|
|
151
|
+
"create",
|
|
152
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
153
|
+
(original: EmbeddingsCreateType): any => {
|
|
154
|
+
return function patchedEmbeddingCreate(
|
|
155
|
+
this: unknown,
|
|
156
|
+
...args: Parameters<typeof module.OpenAI.Embeddings.prototype.create>
|
|
157
|
+
) {
|
|
158
|
+
const body = args[0];
|
|
159
|
+
const { input } = body;
|
|
160
|
+
const isStringInput = typeof input == "string";
|
|
161
|
+
const span = instrumentation.tracer.startSpan(`OpenAI Embeddings`, {
|
|
162
|
+
kind: SpanKind.INTERNAL,
|
|
163
|
+
attributes: {
|
|
164
|
+
[SemanticConventions.OPENINFERENCE_SPAN_KIND]:
|
|
165
|
+
OpenInferenceSpanKind.EMBEDDING,
|
|
166
|
+
[SemanticConventions.EMBEDDING_MODEL_NAME]: body.model,
|
|
167
|
+
[SemanticConventions.INPUT_VALUE]: isStringInput
|
|
168
|
+
? input
|
|
169
|
+
: JSON.stringify(input),
|
|
170
|
+
[SemanticConventions.INPUT_MIME_TYPE]: isStringInput
|
|
171
|
+
? MimeType.TEXT
|
|
172
|
+
: MimeType.JSON,
|
|
173
|
+
...getEmbeddingTextAttributes(body),
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
const execContext = trace.setSpan(context.active(), span);
|
|
177
|
+
const execPromise = safeExecuteInTheMiddle<
|
|
178
|
+
ReturnType<EmbeddingsCreateType>
|
|
179
|
+
>(
|
|
180
|
+
() => {
|
|
181
|
+
return context.with(execContext, () => {
|
|
182
|
+
return original.apply(this, args);
|
|
183
|
+
});
|
|
184
|
+
},
|
|
185
|
+
(error) => {
|
|
186
|
+
// Push the error to the span
|
|
187
|
+
if (error) {
|
|
188
|
+
span.recordException(error);
|
|
189
|
+
span.setStatus({
|
|
190
|
+
code: SpanStatusCode.ERROR,
|
|
191
|
+
message: error.message,
|
|
192
|
+
});
|
|
193
|
+
span.end();
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
);
|
|
197
|
+
const wrappedPromise = execPromise.then((result) => {
|
|
198
|
+
if (result) {
|
|
199
|
+
// Record the results
|
|
200
|
+
span.setAttributes({
|
|
201
|
+
// Do not record the output data as it can be large
|
|
202
|
+
...getEmbeddingEmbeddingsAttributes(result),
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
206
|
+
span.end();
|
|
207
|
+
return result;
|
|
208
|
+
});
|
|
209
|
+
return context.bind(execContext, wrappedPromise);
|
|
210
|
+
};
|
|
211
|
+
},
|
|
212
|
+
);
|
|
213
|
+
|
|
130
214
|
module.openInferencePatched = true;
|
|
131
215
|
return module;
|
|
132
216
|
}
|
|
@@ -136,9 +220,19 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
136
220
|
private unpatch(moduleExports: typeof openai, moduleVersion?: string) {
|
|
137
221
|
diag.debug(`Removing patch for ${MODULE_NAME}@${moduleVersion}`);
|
|
138
222
|
this._unwrap(moduleExports.OpenAI.Chat.Completions.prototype, "create");
|
|
223
|
+
this._unwrap(moduleExports.OpenAI.Embeddings.prototype, "create");
|
|
139
224
|
}
|
|
140
225
|
}
|
|
141
226
|
|
|
227
|
+
/**
|
|
228
|
+
* type-guard that checks if the response is a chat completion response
|
|
229
|
+
*/
|
|
230
|
+
function isChatCompletionResponse(
|
|
231
|
+
response: Stream<ChatCompletionChunk> | ChatCompletion,
|
|
232
|
+
): response is ChatCompletion {
|
|
233
|
+
return "choices" in response;
|
|
234
|
+
}
|
|
235
|
+
|
|
142
236
|
/**
|
|
143
237
|
* Converts the body of the request to LLM input messages
|
|
144
238
|
*/
|
|
@@ -204,3 +298,43 @@ function getLLMOutputMessagesAttributes(
|
|
|
204
298
|
}
|
|
205
299
|
return {};
|
|
206
300
|
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Converts the embedding result payload to embedding attributes
|
|
304
|
+
*/
|
|
305
|
+
function getEmbeddingTextAttributes(
|
|
306
|
+
request: EmbeddingCreateParams,
|
|
307
|
+
): Attributes {
|
|
308
|
+
if (typeof request.input == "string") {
|
|
309
|
+
return {
|
|
310
|
+
[`${SemanticConventions.EMBEDDING_EMBEDDINGS}.0.${SemanticConventions.EMBEDDING_TEXT}`]:
|
|
311
|
+
request.input,
|
|
312
|
+
};
|
|
313
|
+
} else if (
|
|
314
|
+
Array.isArray(request.input) &&
|
|
315
|
+
request.input.length > 0 &&
|
|
316
|
+
typeof request.input[0] == "string"
|
|
317
|
+
) {
|
|
318
|
+
return request.input.reduce((acc, input, index) => {
|
|
319
|
+
const index_prefix = `${SemanticConventions.EMBEDDING_EMBEDDINGS}.${index}`;
|
|
320
|
+
acc[`${index_prefix}.${SemanticConventions.EMBEDDING_TEXT}`] = input;
|
|
321
|
+
return acc;
|
|
322
|
+
}, {} as Attributes);
|
|
323
|
+
}
|
|
324
|
+
// Ignore other cases where input is a number or an array of numbers
|
|
325
|
+
return {};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Converts the embedding result payload to embedding attributes
|
|
330
|
+
*/
|
|
331
|
+
function getEmbeddingEmbeddingsAttributes(
|
|
332
|
+
response: CreateEmbeddingResponse,
|
|
333
|
+
): Attributes {
|
|
334
|
+
return response.data.reduce((acc, embedding, index) => {
|
|
335
|
+
const index_prefix = `${SemanticConventions.EMBEDDING_EMBEDDINGS}.${index}`;
|
|
336
|
+
acc[`${index_prefix}.${SemanticConventions.EMBEDDING_VECTOR}`] =
|
|
337
|
+
embedding.embedding;
|
|
338
|
+
return acc;
|
|
339
|
+
}, {} as Attributes);
|
|
340
|
+
}
|
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.0.
|
|
2
|
+
export const VERSION = "0.0.4";
|