@arizeai/openinference-instrumentation-openai 4.0.0 → 4.0.6
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/README.md +1 -3
- package/dist/esm/instrumentation.d.ts +5 -4
- package/dist/esm/instrumentation.d.ts.map +1 -1
- package/dist/esm/instrumentation.js +23 -31
- package/dist/esm/instrumentation.js.map +1 -1
- package/dist/esm/responsesAttributes.d.ts +3 -3
- package/dist/esm/responsesAttributes.d.ts.map +1 -1
- package/dist/esm/responsesAttributes.js +30 -33
- package/dist/esm/responsesAttributes.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/esnext/instrumentation.d.ts +5 -4
- package/dist/esnext/instrumentation.d.ts.map +1 -1
- package/dist/esnext/instrumentation.js +21 -28
- package/dist/esnext/instrumentation.js.map +1 -1
- package/dist/esnext/responsesAttributes.d.ts +3 -3
- package/dist/esnext/responsesAttributes.d.ts.map +1 -1
- package/dist/esnext/responsesAttributes.js +30 -33
- package/dist/esnext/responsesAttributes.js.map +1 -1
- package/dist/esnext/tsconfig.esnext.tsbuildinfo +1 -1
- package/dist/esnext/version.d.ts +1 -1
- package/dist/esnext/version.js +1 -1
- package/dist/src/instrumentation.d.ts +5 -4
- package/dist/src/instrumentation.d.ts.map +1 -1
- package/dist/src/instrumentation.js +20 -27
- package/dist/src/instrumentation.js.map +1 -1
- package/dist/src/responsesAttributes.d.ts +3 -3
- package/dist/src/responsesAttributes.d.ts.map +1 -1
- package/dist/src/responsesAttributes.js +30 -33
- package/dist/src/responsesAttributes.js.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +19 -19
- package/src/instrumentation.ts +126 -254
- package/src/responsesAttributes.ts +49 -90
- package/src/version.ts +1 -1
package/src/instrumentation.ts
CHANGED
|
@@ -1,8 +1,35 @@
|
|
|
1
|
+
import type { Attributes, Span, Tracer, TracerProvider } from "@opentelemetry/api";
|
|
2
|
+
import { context, diag, SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
3
|
+
import { isTracingSuppressed } from "@opentelemetry/core";
|
|
4
|
+
import type {
|
|
5
|
+
InstrumentationConfig,
|
|
6
|
+
InstrumentationModuleDefinition,
|
|
7
|
+
} from "@opentelemetry/instrumentation";
|
|
1
8
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
} from "@
|
|
9
|
+
InstrumentationBase,
|
|
10
|
+
InstrumentationNodeModuleDefinition,
|
|
11
|
+
safeExecuteInTheMiddle,
|
|
12
|
+
} from "@opentelemetry/instrumentation";
|
|
13
|
+
import type openai from "openai";
|
|
14
|
+
import { APIPromise } from "openai";
|
|
15
|
+
import type { Completion, CreateEmbeddingResponse, EmbeddingCreateParams } from "openai/resources";
|
|
16
|
+
import type {
|
|
17
|
+
ChatCompletion,
|
|
18
|
+
ChatCompletionChunk,
|
|
19
|
+
ChatCompletionCreateParamsBase,
|
|
20
|
+
ChatCompletionMessage,
|
|
21
|
+
ChatCompletionMessageParam,
|
|
22
|
+
} from "openai/resources/chat/completions";
|
|
23
|
+
import type { CompletionCreateParamsBase } from "openai/resources/completions";
|
|
24
|
+
import type {
|
|
25
|
+
Response as ResponseType,
|
|
26
|
+
ResponseCreateParamsBase,
|
|
27
|
+
ResponseStreamEvent,
|
|
28
|
+
} from "openai/resources/responses/responses";
|
|
29
|
+
import type { Stream } from "openai/streaming";
|
|
30
|
+
|
|
31
|
+
import type { TraceConfigOptions } from "@arizeai/openinference-core";
|
|
32
|
+
import { OITracer, safelyJSONStringify } from "@arizeai/openinference-core";
|
|
6
33
|
import {
|
|
7
34
|
LLMProvider,
|
|
8
35
|
LLMSystem,
|
|
@@ -11,26 +38,6 @@ import {
|
|
|
11
38
|
SemanticConventions,
|
|
12
39
|
} from "@arizeai/openinference-semantic-conventions";
|
|
13
40
|
|
|
14
|
-
import {
|
|
15
|
-
Attributes,
|
|
16
|
-
context,
|
|
17
|
-
diag,
|
|
18
|
-
Span,
|
|
19
|
-
SpanKind,
|
|
20
|
-
SpanStatusCode,
|
|
21
|
-
trace,
|
|
22
|
-
Tracer,
|
|
23
|
-
TracerProvider,
|
|
24
|
-
} from "@opentelemetry/api";
|
|
25
|
-
import { isTracingSuppressed } from "@opentelemetry/core";
|
|
26
|
-
import {
|
|
27
|
-
InstrumentationBase,
|
|
28
|
-
InstrumentationConfig,
|
|
29
|
-
InstrumentationModuleDefinition,
|
|
30
|
-
InstrumentationNodeModuleDefinition,
|
|
31
|
-
safeExecuteInTheMiddle,
|
|
32
|
-
} from "@opentelemetry/instrumentation";
|
|
33
|
-
|
|
34
41
|
import {
|
|
35
42
|
consumeResponseStreamEvents,
|
|
36
43
|
getResponsesInputMessagesAttributes,
|
|
@@ -40,27 +47,6 @@ import {
|
|
|
40
47
|
import { assertUnreachable, isString } from "./typeUtils";
|
|
41
48
|
import { VERSION } from "./version";
|
|
42
49
|
|
|
43
|
-
import openai, { APIPromise } from "openai";
|
|
44
|
-
import {
|
|
45
|
-
Completion,
|
|
46
|
-
CreateEmbeddingResponse,
|
|
47
|
-
EmbeddingCreateParams,
|
|
48
|
-
} from "openai/resources";
|
|
49
|
-
import {
|
|
50
|
-
ChatCompletion,
|
|
51
|
-
ChatCompletionChunk,
|
|
52
|
-
ChatCompletionCreateParamsBase,
|
|
53
|
-
ChatCompletionMessage,
|
|
54
|
-
ChatCompletionMessageParam,
|
|
55
|
-
} from "openai/resources/chat/completions";
|
|
56
|
-
import { CompletionCreateParamsBase } from "openai/resources/completions";
|
|
57
|
-
import {
|
|
58
|
-
Response as ResponseType,
|
|
59
|
-
ResponseCreateParamsBase,
|
|
60
|
-
ResponseStreamEvent,
|
|
61
|
-
} from "openai/resources/responses/responses";
|
|
62
|
-
import { Stream } from "openai/streaming";
|
|
63
|
-
|
|
64
50
|
const MODULE_NAME = "openai";
|
|
65
51
|
|
|
66
52
|
const INSTRUMENTATION_NAME = "@arizeai/openinference-instrumentation-openai";
|
|
@@ -86,9 +72,7 @@ export function isPatched() {
|
|
|
86
72
|
function getExecContext(span: Span) {
|
|
87
73
|
const activeContext = context.active();
|
|
88
74
|
const suppressTracing = isTracingSuppressed(activeContext);
|
|
89
|
-
const execContext = suppressTracing
|
|
90
|
-
? trace.setSpan(context.active(), span)
|
|
91
|
-
: activeContext;
|
|
75
|
+
const execContext = suppressTracing ? trace.setSpan(context.active(), span) : activeContext;
|
|
92
76
|
// Drop the span from the context
|
|
93
77
|
if (suppressTracing) {
|
|
94
78
|
trace.deleteSpan(activeContext);
|
|
@@ -190,17 +174,11 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
190
174
|
*/
|
|
191
175
|
tracerProvider?: TracerProvider;
|
|
192
176
|
} = {}) {
|
|
193
|
-
super(
|
|
194
|
-
INSTRUMENTATION_NAME,
|
|
195
|
-
VERSION,
|
|
196
|
-
Object.assign({}, instrumentationConfig),
|
|
197
|
-
);
|
|
177
|
+
super(INSTRUMENTATION_NAME, VERSION, Object.assign({}, instrumentationConfig));
|
|
198
178
|
this.tracerProvider = tracerProvider;
|
|
199
179
|
this.traceConfig = traceConfig;
|
|
200
180
|
this.oiTracer = new OITracer({
|
|
201
|
-
tracer:
|
|
202
|
-
this.tracerProvider?.getTracer(INSTRUMENTATION_NAME, VERSION) ??
|
|
203
|
-
this.tracer,
|
|
181
|
+
tracer: this.tracerProvider?.getTracer(INSTRUMENTATION_NAME, VERSION) ?? this.tracer,
|
|
204
182
|
traceConfig,
|
|
205
183
|
});
|
|
206
184
|
}
|
|
@@ -227,10 +205,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
227
205
|
|
|
228
206
|
get tracer(): Tracer {
|
|
229
207
|
if (this.tracerProvider) {
|
|
230
|
-
return this.tracerProvider.getTracer(
|
|
231
|
-
this.instrumentationName,
|
|
232
|
-
this.instrumentationVersion,
|
|
233
|
-
);
|
|
208
|
+
return this.tracerProvider.getTracer(this.instrumentationName, this.instrumentationVersion);
|
|
234
209
|
}
|
|
235
210
|
return super.tracer;
|
|
236
211
|
}
|
|
@@ -259,8 +234,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
259
234
|
const instrumentation: OpenAIInstrumentation = this;
|
|
260
235
|
|
|
261
236
|
// Patch create chat completions
|
|
262
|
-
type ChatCompletionCreateType =
|
|
263
|
-
typeof module.OpenAI.Chat.Completions.prototype.create;
|
|
237
|
+
type ChatCompletionCreateType = typeof module.OpenAI.Chat.Completions.prototype.create;
|
|
264
238
|
|
|
265
239
|
this._wrap(
|
|
266
240
|
module.OpenAI.Chat.Completions.prototype,
|
|
@@ -273,25 +247,20 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
273
247
|
) {
|
|
274
248
|
const body = args[0];
|
|
275
249
|
const { messages: _messages, ...invocationParameters } = body;
|
|
276
|
-
const span = instrumentation.oiTracer.startSpan(
|
|
277
|
-
|
|
278
|
-
{
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
[SemanticConventions.LLM_SYSTEM]: LLMSystem.OPENAI,
|
|
289
|
-
[SemanticConventions.LLM_PROVIDER]: getLLMProvider(this),
|
|
290
|
-
...getLLMInputMessagesAttributes(body),
|
|
291
|
-
...getLLMToolsJSONSchema(body),
|
|
292
|
-
},
|
|
250
|
+
const span = instrumentation.oiTracer.startSpan(`OpenAI Chat Completions`, {
|
|
251
|
+
kind: SpanKind.INTERNAL,
|
|
252
|
+
attributes: {
|
|
253
|
+
[SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.LLM,
|
|
254
|
+
[SemanticConventions.LLM_MODEL_NAME]: body.model,
|
|
255
|
+
[SemanticConventions.INPUT_VALUE]: JSON.stringify(body),
|
|
256
|
+
[SemanticConventions.INPUT_MIME_TYPE]: MimeType.JSON,
|
|
257
|
+
[SemanticConventions.LLM_INVOCATION_PARAMETERS]: JSON.stringify(invocationParameters),
|
|
258
|
+
[SemanticConventions.LLM_SYSTEM]: LLMSystem.OPENAI,
|
|
259
|
+
[SemanticConventions.LLM_PROVIDER]: getLLMProvider(this),
|
|
260
|
+
...getLLMInputMessagesAttributes(body),
|
|
261
|
+
...getLLMToolsJSONSchema(body),
|
|
293
262
|
},
|
|
294
|
-
);
|
|
263
|
+
});
|
|
295
264
|
const execContext = getExecContext(span);
|
|
296
265
|
const execPromise = safeExecuteInTheMiddle(
|
|
297
266
|
() => {
|
|
@@ -340,46 +309,34 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
340
309
|
|
|
341
310
|
return result;
|
|
342
311
|
};
|
|
343
|
-
const wrappedPromise = invokeMaybeAPIPromise(
|
|
344
|
-
execPromise,
|
|
345
|
-
wrappedPromiseThen,
|
|
346
|
-
);
|
|
312
|
+
const wrappedPromise = invokeMaybeAPIPromise(execPromise, wrappedPromiseThen);
|
|
347
313
|
return context.bind(execContext, wrappedPromise);
|
|
348
314
|
};
|
|
349
315
|
},
|
|
350
316
|
);
|
|
351
317
|
|
|
352
318
|
// Patch create completions
|
|
353
|
-
type CompletionsCreateType =
|
|
354
|
-
typeof module.OpenAI.Completions.prototype.create;
|
|
319
|
+
type CompletionsCreateType = typeof module.OpenAI.Completions.prototype.create;
|
|
355
320
|
|
|
356
321
|
this._wrap(
|
|
357
322
|
module.OpenAI.Completions.prototype,
|
|
358
323
|
"create",
|
|
359
324
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
360
325
|
(original: CompletionsCreateType): any => {
|
|
361
|
-
return function patchedCreate(
|
|
362
|
-
this: unknown,
|
|
363
|
-
...args: Parameters<CompletionsCreateType>
|
|
364
|
-
) {
|
|
326
|
+
return function patchedCreate(this: unknown, ...args: Parameters<CompletionsCreateType>) {
|
|
365
327
|
const body = args[0];
|
|
366
328
|
const { prompt: _prompt, ...invocationParameters } = body;
|
|
367
|
-
const span = instrumentation.oiTracer.startSpan(
|
|
368
|
-
|
|
369
|
-
{
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
JSON.stringify(invocationParameters),
|
|
377
|
-
[SemanticConventions.LLM_SYSTEM]: LLMSystem.OPENAI,
|
|
378
|
-
[SemanticConventions.LLM_PROVIDER]: getLLMProvider(this),
|
|
379
|
-
...getCompletionInputValueAndMimeType(body),
|
|
380
|
-
},
|
|
329
|
+
const span = instrumentation.oiTracer.startSpan(`OpenAI Completions`, {
|
|
330
|
+
kind: SpanKind.INTERNAL,
|
|
331
|
+
attributes: {
|
|
332
|
+
[SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.LLM,
|
|
333
|
+
[SemanticConventions.LLM_MODEL_NAME]: body.model,
|
|
334
|
+
[SemanticConventions.LLM_INVOCATION_PARAMETERS]: JSON.stringify(invocationParameters),
|
|
335
|
+
[SemanticConventions.LLM_SYSTEM]: LLMSystem.OPENAI,
|
|
336
|
+
[SemanticConventions.LLM_PROVIDER]: getLLMProvider(this),
|
|
337
|
+
...getCompletionInputValueAndMimeType(body),
|
|
381
338
|
},
|
|
382
|
-
);
|
|
339
|
+
});
|
|
383
340
|
const execContext = getExecContext(span);
|
|
384
341
|
|
|
385
342
|
const execPromise = safeExecuteInTheMiddle(
|
|
@@ -400,9 +357,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
400
357
|
}
|
|
401
358
|
},
|
|
402
359
|
);
|
|
403
|
-
const wrappedPromiseThen = (
|
|
404
|
-
result: Completion | Stream<Completion>,
|
|
405
|
-
) => {
|
|
360
|
+
const wrappedPromiseThen = (result: Completion | Stream<Completion>) => {
|
|
406
361
|
if (isCompletionResponse(result)) {
|
|
407
362
|
// Record the results
|
|
408
363
|
span.setAttributes({
|
|
@@ -418,18 +373,14 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
418
373
|
}
|
|
419
374
|
return result;
|
|
420
375
|
};
|
|
421
|
-
const wrappedPromise = invokeMaybeAPIPromise(
|
|
422
|
-
execPromise,
|
|
423
|
-
wrappedPromiseThen,
|
|
424
|
-
);
|
|
376
|
+
const wrappedPromise = invokeMaybeAPIPromise(execPromise, wrappedPromiseThen);
|
|
425
377
|
return context.bind(execContext, wrappedPromise);
|
|
426
378
|
};
|
|
427
379
|
},
|
|
428
380
|
);
|
|
429
381
|
|
|
430
382
|
// Patch embeddings
|
|
431
|
-
type EmbeddingsCreateType =
|
|
432
|
-
typeof module.OpenAI.Embeddings.prototype.create;
|
|
383
|
+
type EmbeddingsCreateType = typeof module.OpenAI.Embeddings.prototype.create;
|
|
433
384
|
this._wrap(
|
|
434
385
|
module.OpenAI.Embeddings.prototype,
|
|
435
386
|
"create",
|
|
@@ -445,15 +396,10 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
445
396
|
const span = instrumentation.oiTracer.startSpan(`OpenAI Embeddings`, {
|
|
446
397
|
kind: SpanKind.INTERNAL,
|
|
447
398
|
attributes: {
|
|
448
|
-
[SemanticConventions.OPENINFERENCE_SPAN_KIND]:
|
|
449
|
-
OpenInferenceSpanKind.EMBEDDING,
|
|
399
|
+
[SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.EMBEDDING,
|
|
450
400
|
[SemanticConventions.EMBEDDING_MODEL_NAME]: body.model,
|
|
451
|
-
[SemanticConventions.INPUT_VALUE]: isStringInput
|
|
452
|
-
|
|
453
|
-
: JSON.stringify(input),
|
|
454
|
-
[SemanticConventions.INPUT_MIME_TYPE]: isStringInput
|
|
455
|
-
? MimeType.TEXT
|
|
456
|
-
: MimeType.JSON,
|
|
401
|
+
[SemanticConventions.INPUT_VALUE]: isStringInput ? input : JSON.stringify(input),
|
|
402
|
+
[SemanticConventions.INPUT_MIME_TYPE]: isStringInput ? MimeType.TEXT : MimeType.JSON,
|
|
457
403
|
[SemanticConventions.LLM_PROVIDER]: getLLMProvider(this),
|
|
458
404
|
...getEmbeddingTextAttributes(body),
|
|
459
405
|
},
|
|
@@ -489,10 +435,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
489
435
|
span.end();
|
|
490
436
|
return result;
|
|
491
437
|
};
|
|
492
|
-
const wrappedPromise = invokeMaybeAPIPromise(
|
|
493
|
-
execPromise,
|
|
494
|
-
wrappedPromiseThen,
|
|
495
|
-
);
|
|
438
|
+
const wrappedPromise = invokeMaybeAPIPromise(execPromise, wrappedPromiseThen);
|
|
496
439
|
return context.bind(execContext, wrappedPromise);
|
|
497
440
|
};
|
|
498
441
|
},
|
|
@@ -500,39 +443,31 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
500
443
|
|
|
501
444
|
// Patch responses (if the patched module contains the Responses interface)
|
|
502
445
|
if (module.OpenAI.Responses) {
|
|
503
|
-
type ResponsesCreateType =
|
|
504
|
-
typeof module.OpenAI.Responses.prototype.create;
|
|
446
|
+
type ResponsesCreateType = typeof module.OpenAI.Responses.prototype.create;
|
|
505
447
|
|
|
506
448
|
this._wrap(
|
|
507
449
|
module.OpenAI.Responses.prototype,
|
|
508
450
|
"create",
|
|
509
451
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
510
452
|
(original: ResponsesCreateType): any => {
|
|
511
|
-
return function patchedCreate(
|
|
512
|
-
this: unknown,
|
|
513
|
-
...args: Parameters<ResponsesCreateType>
|
|
514
|
-
) {
|
|
453
|
+
return function patchedCreate(this: unknown, ...args: Parameters<ResponsesCreateType>) {
|
|
515
454
|
const body = args[0];
|
|
516
455
|
const { input: _messages, ...invocationParameters } = body;
|
|
517
|
-
const span = instrumentation.oiTracer.startSpan(
|
|
518
|
-
|
|
519
|
-
{
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
[SemanticConventions.LLM_PROVIDER]: getLLMProvider(this),
|
|
531
|
-
...getResponsesInputMessagesAttributes(body),
|
|
532
|
-
...getLLMToolsJSONSchema(body),
|
|
533
|
-
},
|
|
456
|
+
const span = instrumentation.oiTracer.startSpan(`OpenAI Responses`, {
|
|
457
|
+
kind: SpanKind.INTERNAL,
|
|
458
|
+
attributes: {
|
|
459
|
+
[SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.LLM,
|
|
460
|
+
[SemanticConventions.LLM_MODEL_NAME]: body.model,
|
|
461
|
+
[SemanticConventions.INPUT_VALUE]: JSON.stringify(body),
|
|
462
|
+
[SemanticConventions.INPUT_MIME_TYPE]: MimeType.JSON,
|
|
463
|
+
[SemanticConventions.LLM_INVOCATION_PARAMETERS]:
|
|
464
|
+
JSON.stringify(invocationParameters),
|
|
465
|
+
[SemanticConventions.LLM_SYSTEM]: LLMSystem.OPENAI,
|
|
466
|
+
[SemanticConventions.LLM_PROVIDER]: getLLMProvider(this),
|
|
467
|
+
...getResponsesInputMessagesAttributes(body),
|
|
468
|
+
...getLLMToolsJSONSchema(body),
|
|
534
469
|
},
|
|
535
|
-
);
|
|
470
|
+
});
|
|
536
471
|
const execContext = getExecContext(span);
|
|
537
472
|
const execPromise = safeExecuteInTheMiddle(
|
|
538
473
|
() => {
|
|
@@ -552,9 +487,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
552
487
|
}
|
|
553
488
|
},
|
|
554
489
|
);
|
|
555
|
-
const wrappedPromiseThen = (
|
|
556
|
-
result: Stream<ResponseStreamEvent> | ResponseType,
|
|
557
|
-
) => {
|
|
490
|
+
const wrappedPromiseThen = (result: Stream<ResponseStreamEvent> | ResponseType) => {
|
|
558
491
|
const recordSpan = (result?: ResponseType) => {
|
|
559
492
|
if (!result) {
|
|
560
493
|
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
@@ -588,10 +521,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
588
521
|
|
|
589
522
|
return result;
|
|
590
523
|
};
|
|
591
|
-
const wrappedPromise = invokeMaybeAPIPromise(
|
|
592
|
-
execPromise,
|
|
593
|
-
wrappedPromiseThen,
|
|
594
|
-
);
|
|
524
|
+
const wrappedPromise = invokeMaybeAPIPromise(execPromise, wrappedPromiseThen);
|
|
595
525
|
return context.bind(execContext, wrappedPromise);
|
|
596
526
|
};
|
|
597
527
|
},
|
|
@@ -648,9 +578,7 @@ function isChatCompletionResponse(
|
|
|
648
578
|
/**
|
|
649
579
|
* type-guard that checks if the response is a completion response
|
|
650
580
|
*/
|
|
651
|
-
function isCompletionResponse(
|
|
652
|
-
response: Stream<Completion> | Completion,
|
|
653
|
-
): response is Completion {
|
|
581
|
+
function isCompletionResponse(response: Stream<Completion> | Completion): response is Completion {
|
|
654
582
|
return "choices" in response;
|
|
655
583
|
}
|
|
656
584
|
|
|
@@ -660,17 +588,13 @@ function isCompletionResponse(
|
|
|
660
588
|
function isPromptStringArray(
|
|
661
589
|
prompt: CompletionCreateParamsBase["prompt"],
|
|
662
590
|
): prompt is Array<string> {
|
|
663
|
-
return (
|
|
664
|
-
Array.isArray(prompt) && prompt.every((item) => typeof item === "string")
|
|
665
|
-
);
|
|
591
|
+
return Array.isArray(prompt) && prompt.every((item) => typeof item === "string");
|
|
666
592
|
}
|
|
667
593
|
|
|
668
594
|
/**
|
|
669
595
|
* Converts the body of a chat completions request to LLM input messages
|
|
670
596
|
*/
|
|
671
|
-
function getLLMInputMessagesAttributes(
|
|
672
|
-
body: ChatCompletionCreateParamsBase,
|
|
673
|
-
): Attributes {
|
|
597
|
+
function getLLMInputMessagesAttributes(body: ChatCompletionCreateParamsBase): Attributes {
|
|
674
598
|
return body.messages.reduce((acc, message, index) => {
|
|
675
599
|
const messageAttributes = getChatCompletionInputMessageAttributes(message);
|
|
676
600
|
const indexPrefix = `${SemanticConventions.LLM_INPUT_MESSAGES}.${index}.`;
|
|
@@ -702,9 +626,7 @@ function getLLMToolsJSONSchema(
|
|
|
702
626
|
}, {});
|
|
703
627
|
}
|
|
704
628
|
|
|
705
|
-
function getChatCompletionInputMessageAttributes(
|
|
706
|
-
message: ChatCompletionMessageParam,
|
|
707
|
-
): Attributes {
|
|
629
|
+
function getChatCompletionInputMessageAttributes(message: ChatCompletionMessageParam): Attributes {
|
|
708
630
|
const role = message.role;
|
|
709
631
|
const attributes: Attributes = {
|
|
710
632
|
[SemanticConventions.MESSAGE_ROLE]: role,
|
|
@@ -716,16 +638,10 @@ function getChatCompletionInputMessageAttributes(
|
|
|
716
638
|
message.content.forEach((part, index) => {
|
|
717
639
|
const contentsIndexPrefix = `${SemanticConventions.MESSAGE_CONTENTS}.${index}.`;
|
|
718
640
|
if (part.type === "text") {
|
|
719
|
-
attributes[
|
|
720
|
-
|
|
721
|
-
] = "text";
|
|
722
|
-
attributes[
|
|
723
|
-
`${contentsIndexPrefix}${SemanticConventions.MESSAGE_CONTENT_TEXT}`
|
|
724
|
-
] = part.text;
|
|
641
|
+
attributes[`${contentsIndexPrefix}${SemanticConventions.MESSAGE_CONTENT_TYPE}`] = "text";
|
|
642
|
+
attributes[`${contentsIndexPrefix}${SemanticConventions.MESSAGE_CONTENT_TEXT}`] = part.text;
|
|
725
643
|
} else if (part.type === "image_url") {
|
|
726
|
-
attributes[
|
|
727
|
-
`${contentsIndexPrefix}${SemanticConventions.MESSAGE_CONTENT_TYPE}`
|
|
728
|
-
] = "image";
|
|
644
|
+
attributes[`${contentsIndexPrefix}${SemanticConventions.MESSAGE_CONTENT_TYPE}`] = "image";
|
|
729
645
|
attributes[
|
|
730
646
|
`${contentsIndexPrefix}${SemanticConventions.MESSAGE_CONTENT_IMAGE}.${SemanticConventions.IMAGE_URL}`
|
|
731
647
|
] = part.image_url.url;
|
|
@@ -743,15 +659,12 @@ function getChatCompletionInputMessageAttributes(
|
|
|
743
659
|
|
|
744
660
|
// Add the tool call id if it exists
|
|
745
661
|
if (toolCall.id) {
|
|
746
|
-
attributes[
|
|
747
|
-
`${toolCallIndexPrefix}${SemanticConventions.TOOL_CALL_ID}`
|
|
748
|
-
] = toolCall.id;
|
|
662
|
+
attributes[`${toolCallIndexPrefix}${SemanticConventions.TOOL_CALL_ID}`] = toolCall.id;
|
|
749
663
|
}
|
|
750
664
|
// Make sure the tool call has a function
|
|
751
665
|
if (toolCall.type === "function") {
|
|
752
|
-
attributes[
|
|
753
|
-
|
|
754
|
-
] = toolCall.function.name;
|
|
666
|
+
attributes[`${toolCallIndexPrefix}${SemanticConventions.TOOL_CALL_FUNCTION_NAME}`] =
|
|
667
|
+
toolCall.function.name;
|
|
755
668
|
attributes[
|
|
756
669
|
`${toolCallIndexPrefix}${SemanticConventions.TOOL_CALL_FUNCTION_ARGUMENTS_JSON}`
|
|
757
670
|
] = toolCall.function.arguments;
|
|
@@ -767,8 +680,7 @@ function getChatCompletionInputMessageAttributes(
|
|
|
767
680
|
break;
|
|
768
681
|
case "tool":
|
|
769
682
|
if (message.tool_call_id) {
|
|
770
|
-
attributes[`${SemanticConventions.MESSAGE_TOOL_CALL_ID}`] =
|
|
771
|
-
message.tool_call_id;
|
|
683
|
+
attributes[`${SemanticConventions.MESSAGE_TOOL_CALL_ID}`] = message.tool_call_id;
|
|
772
684
|
}
|
|
773
685
|
break;
|
|
774
686
|
case "system":
|
|
@@ -787,9 +699,7 @@ function getChatCompletionInputMessageAttributes(
|
|
|
787
699
|
/**
|
|
788
700
|
* Converts the body of a completions request to input attributes
|
|
789
701
|
*/
|
|
790
|
-
function getCompletionInputValueAndMimeType(
|
|
791
|
-
body: CompletionCreateParamsBase,
|
|
792
|
-
): Attributes {
|
|
702
|
+
function getCompletionInputValueAndMimeType(body: CompletionCreateParamsBase): Attributes {
|
|
793
703
|
if (typeof body.prompt === "string") {
|
|
794
704
|
return {
|
|
795
705
|
[SemanticConventions.INPUT_VALUE]: body.prompt,
|
|
@@ -812,17 +722,12 @@ function getCompletionInputValueAndMimeType(
|
|
|
812
722
|
/**
|
|
813
723
|
* Get usage attributes
|
|
814
724
|
*/
|
|
815
|
-
function getUsageAttributes(
|
|
816
|
-
completion: ChatCompletion | Completion,
|
|
817
|
-
): Attributes {
|
|
725
|
+
function getUsageAttributes(completion: ChatCompletion | Completion): Attributes {
|
|
818
726
|
if (completion.usage) {
|
|
819
727
|
const usageAttributes: Attributes = {
|
|
820
|
-
[SemanticConventions.LLM_TOKEN_COUNT_COMPLETION]:
|
|
821
|
-
|
|
822
|
-
[SemanticConventions.
|
|
823
|
-
completion.usage.prompt_tokens,
|
|
824
|
-
[SemanticConventions.LLM_TOKEN_COUNT_TOTAL]:
|
|
825
|
-
completion.usage.total_tokens,
|
|
728
|
+
[SemanticConventions.LLM_TOKEN_COUNT_COMPLETION]: completion.usage.completion_tokens,
|
|
729
|
+
[SemanticConventions.LLM_TOKEN_COUNT_PROMPT]: completion.usage.prompt_tokens,
|
|
730
|
+
[SemanticConventions.LLM_TOKEN_COUNT_TOTAL]: completion.usage.total_tokens,
|
|
826
731
|
[SemanticConventions.LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_READ]:
|
|
827
732
|
completion.usage.prompt_tokens_details?.cached_tokens,
|
|
828
733
|
[SemanticConventions.LLM_TOKEN_COUNT_PROMPT_DETAILS_AUDIO]:
|
|
@@ -840,9 +745,7 @@ function getUsageAttributes(
|
|
|
840
745
|
/**
|
|
841
746
|
* Converts the chat completion result to LLM output attributes
|
|
842
747
|
*/
|
|
843
|
-
function getChatCompletionLLMOutputMessagesAttributes(
|
|
844
|
-
chatCompletion: ChatCompletion,
|
|
845
|
-
): Attributes {
|
|
748
|
+
function getChatCompletionLLMOutputMessagesAttributes(chatCompletion: ChatCompletion): Attributes {
|
|
846
749
|
// Right now support just the first choice
|
|
847
750
|
const choice = chatCompletion.choices[0];
|
|
848
751
|
if (!choice) {
|
|
@@ -859,9 +762,7 @@ function getChatCompletionLLMOutputMessagesAttributes(
|
|
|
859
762
|
}, {} as Attributes);
|
|
860
763
|
}
|
|
861
764
|
|
|
862
|
-
function getChatCompletionOutputMessageAttributes(
|
|
863
|
-
message: ChatCompletionMessage,
|
|
864
|
-
): Attributes {
|
|
765
|
+
function getChatCompletionOutputMessageAttributes(message: ChatCompletionMessage): Attributes {
|
|
865
766
|
const role = message.role;
|
|
866
767
|
const attributes: Attributes = {
|
|
867
768
|
[SemanticConventions.MESSAGE_ROLE]: role,
|
|
@@ -874,18 +775,13 @@ function getChatCompletionOutputMessageAttributes(
|
|
|
874
775
|
const toolCallIndexPrefix = `${SemanticConventions.MESSAGE_TOOL_CALLS}.${index}.`;
|
|
875
776
|
// Add the tool call id if it exists
|
|
876
777
|
if (toolCall.id) {
|
|
877
|
-
attributes[
|
|
878
|
-
`${toolCallIndexPrefix}${SemanticConventions.TOOL_CALL_ID}`
|
|
879
|
-
] = toolCall.id;
|
|
778
|
+
attributes[`${toolCallIndexPrefix}${SemanticConventions.TOOL_CALL_ID}`] = toolCall.id;
|
|
880
779
|
}
|
|
881
780
|
if (toolCall.type === "function") {
|
|
882
|
-
attributes[
|
|
883
|
-
|
|
884
|
-
] =
|
|
885
|
-
|
|
886
|
-
toolCallIndexPrefix +
|
|
887
|
-
SemanticConventions.TOOL_CALL_FUNCTION_ARGUMENTS_JSON
|
|
888
|
-
] = toolCall.function.arguments;
|
|
781
|
+
attributes[toolCallIndexPrefix + SemanticConventions.TOOL_CALL_FUNCTION_NAME] =
|
|
782
|
+
toolCall.function.name;
|
|
783
|
+
attributes[toolCallIndexPrefix + SemanticConventions.TOOL_CALL_FUNCTION_ARGUMENTS_JSON] =
|
|
784
|
+
toolCall.function.arguments;
|
|
889
785
|
} else {
|
|
890
786
|
// TODO: switch to exhaustive checks
|
|
891
787
|
diag.warn(`Unsupported tool type: ${toolCall.type}`);
|
|
@@ -893,8 +789,7 @@ function getChatCompletionOutputMessageAttributes(
|
|
|
893
789
|
});
|
|
894
790
|
}
|
|
895
791
|
if (message.function_call) {
|
|
896
|
-
attributes[SemanticConventions.MESSAGE_FUNCTION_CALL_NAME] =
|
|
897
|
-
message.function_call.name;
|
|
792
|
+
attributes[SemanticConventions.MESSAGE_FUNCTION_CALL_NAME] = message.function_call.name;
|
|
898
793
|
attributes[SemanticConventions.MESSAGE_FUNCTION_CALL_ARGUMENTS_JSON] =
|
|
899
794
|
message.function_call.arguments;
|
|
900
795
|
}
|
|
@@ -904,9 +799,7 @@ function getChatCompletionOutputMessageAttributes(
|
|
|
904
799
|
/**
|
|
905
800
|
* Converts the completion result to output attributes
|
|
906
801
|
*/
|
|
907
|
-
function getCompletionOutputValueAndMimeType(
|
|
908
|
-
completion: Completion,
|
|
909
|
-
): Attributes {
|
|
802
|
+
function getCompletionOutputValueAndMimeType(completion: Completion): Attributes {
|
|
910
803
|
// Right now support just the first choice
|
|
911
804
|
const choice = completion.choices[0];
|
|
912
805
|
if (!choice) {
|
|
@@ -921,9 +814,7 @@ function getCompletionOutputValueAndMimeType(
|
|
|
921
814
|
/**
|
|
922
815
|
* Converts the embedding result payload to embedding attributes
|
|
923
816
|
*/
|
|
924
|
-
function getEmbeddingTextAttributes(
|
|
925
|
-
request: EmbeddingCreateParams,
|
|
926
|
-
): Attributes {
|
|
817
|
+
function getEmbeddingTextAttributes(request: EmbeddingCreateParams): Attributes {
|
|
927
818
|
if (typeof request.input === "string") {
|
|
928
819
|
return {
|
|
929
820
|
[`${SemanticConventions.EMBEDDING_EMBEDDINGS}.0.${SemanticConventions.EMBEDDING_TEXT}`]:
|
|
@@ -947,13 +838,10 @@ function getEmbeddingTextAttributes(
|
|
|
947
838
|
/**
|
|
948
839
|
* Converts the embedding result payload to embedding attributes
|
|
949
840
|
*/
|
|
950
|
-
function getEmbeddingEmbeddingsAttributes(
|
|
951
|
-
response: CreateEmbeddingResponse,
|
|
952
|
-
): Attributes {
|
|
841
|
+
function getEmbeddingEmbeddingsAttributes(response: CreateEmbeddingResponse): Attributes {
|
|
953
842
|
return response.data.reduce((acc, embedding, index) => {
|
|
954
843
|
const indexPrefix = `${SemanticConventions.EMBEDDING_EMBEDDINGS}.${index}.`;
|
|
955
|
-
acc[`${indexPrefix}${SemanticConventions.EMBEDDING_VECTOR}`] =
|
|
956
|
-
embedding.embedding;
|
|
844
|
+
acc[`${indexPrefix}${SemanticConventions.EMBEDDING_VECTOR}`] = embedding.embedding;
|
|
957
845
|
return acc;
|
|
958
846
|
}, {} as Attributes);
|
|
959
847
|
}
|
|
@@ -961,10 +849,7 @@ function getEmbeddingEmbeddingsAttributes(
|
|
|
961
849
|
/**
|
|
962
850
|
* Consumes the stream chunks and adds them to the span
|
|
963
851
|
*/
|
|
964
|
-
async function consumeChatCompletionStreamChunks(
|
|
965
|
-
stream: Stream<ChatCompletionChunk>,
|
|
966
|
-
span: Span,
|
|
967
|
-
) {
|
|
852
|
+
async function consumeChatCompletionStreamChunks(stream: Stream<ChatCompletionChunk>, span: Span) {
|
|
968
853
|
let streamResponse = "";
|
|
969
854
|
// Tool and function call attributes can also arrive in the stream
|
|
970
855
|
// NB: the tools and function calls arrive in partial diffs
|
|
@@ -983,9 +868,7 @@ async function consumeChatCompletionStreamChunks(
|
|
|
983
868
|
// Accumulate the tool and function call attributes
|
|
984
869
|
const toolAndFunctionCallAttributesDiff =
|
|
985
870
|
getToolAndFunctionCallAttributesFromStreamChunk(chunk);
|
|
986
|
-
for (const [key, value] of Object.entries(
|
|
987
|
-
toolAndFunctionCallAttributesDiff,
|
|
988
|
-
)) {
|
|
871
|
+
for (const [key, value] of Object.entries(toolAndFunctionCallAttributesDiff)) {
|
|
989
872
|
if (isString(toolAndFunctionCallAttributes[key]) && isString(value)) {
|
|
990
873
|
toolAndFunctionCallAttributes[key] += value;
|
|
991
874
|
} else if (isString(value)) {
|
|
@@ -999,8 +882,7 @@ async function consumeChatCompletionStreamChunks(
|
|
|
999
882
|
const attributes: Attributes = {
|
|
1000
883
|
[SemanticConventions.OUTPUT_VALUE]: streamResponse,
|
|
1001
884
|
[SemanticConventions.OUTPUT_MIME_TYPE]: MimeType.TEXT,
|
|
1002
|
-
[`${messageIndexPrefix}${SemanticConventions.MESSAGE_CONTENT}`]:
|
|
1003
|
-
streamResponse,
|
|
885
|
+
[`${messageIndexPrefix}${SemanticConventions.MESSAGE_CONTENT}`]: streamResponse,
|
|
1004
886
|
[`${messageIndexPrefix}${SemanticConventions.MESSAGE_ROLE}`]: "assistant",
|
|
1005
887
|
};
|
|
1006
888
|
// Add the tool and function call attributes
|
|
@@ -1014,9 +896,7 @@ async function consumeChatCompletionStreamChunks(
|
|
|
1014
896
|
/**
|
|
1015
897
|
* Extracts the semantic attributes from the stream chunk for tool_calls and function_calls
|
|
1016
898
|
*/
|
|
1017
|
-
function getToolAndFunctionCallAttributesFromStreamChunk(
|
|
1018
|
-
chunk: ChatCompletionChunk,
|
|
1019
|
-
): Attributes {
|
|
899
|
+
function getToolAndFunctionCallAttributesFromStreamChunk(chunk: ChatCompletionChunk): Attributes {
|
|
1020
900
|
if (chunk.choices.length <= 0) {
|
|
1021
901
|
return {};
|
|
1022
902
|
}
|
|
@@ -1027,26 +907,20 @@ function getToolAndFunctionCallAttributesFromStreamChunk(
|
|
|
1027
907
|
const toolCallIndexPrefix = `${SemanticConventions.MESSAGE_TOOL_CALLS}.${index}.`;
|
|
1028
908
|
// Add the tool call id if it exists
|
|
1029
909
|
if (toolCall.id) {
|
|
1030
|
-
attributes[
|
|
1031
|
-
`${toolCallIndexPrefix}${SemanticConventions.TOOL_CALL_ID}`
|
|
1032
|
-
] = toolCall.id;
|
|
910
|
+
attributes[`${toolCallIndexPrefix}${SemanticConventions.TOOL_CALL_ID}`] = toolCall.id;
|
|
1033
911
|
}
|
|
1034
912
|
// Double check that the tool call has a function
|
|
1035
913
|
// NB: OpenAI only supports tool calls with functions right now but this may change
|
|
1036
914
|
if (toolCall.function) {
|
|
1037
|
-
attributes[
|
|
1038
|
-
|
|
1039
|
-
] =
|
|
1040
|
-
|
|
1041
|
-
toolCallIndexPrefix +
|
|
1042
|
-
SemanticConventions.TOOL_CALL_FUNCTION_ARGUMENTS_JSON
|
|
1043
|
-
] = toolCall.function.arguments;
|
|
915
|
+
attributes[toolCallIndexPrefix + SemanticConventions.TOOL_CALL_FUNCTION_NAME] =
|
|
916
|
+
toolCall.function.name;
|
|
917
|
+
attributes[toolCallIndexPrefix + SemanticConventions.TOOL_CALL_FUNCTION_ARGUMENTS_JSON] =
|
|
918
|
+
toolCall.function.arguments;
|
|
1044
919
|
}
|
|
1045
920
|
});
|
|
1046
921
|
}
|
|
1047
922
|
if (choice.delta.function_call) {
|
|
1048
|
-
attributes[SemanticConventions.MESSAGE_FUNCTION_CALL_NAME] =
|
|
1049
|
-
choice.delta.function_call.name;
|
|
923
|
+
attributes[SemanticConventions.MESSAGE_FUNCTION_CALL_NAME] = choice.delta.function_call.name;
|
|
1050
924
|
attributes[SemanticConventions.MESSAGE_FUNCTION_CALL_ARGUMENTS_JSON] =
|
|
1051
925
|
choice.delta.function_call.arguments;
|
|
1052
926
|
}
|
|
@@ -1089,9 +963,7 @@ function invokeMaybeAPIPromise<T>(
|
|
|
1089
963
|
return promise.then(then) as T;
|
|
1090
964
|
} else {
|
|
1091
965
|
// eslint-disable-next-line no-console
|
|
1092
|
-
console.warn(
|
|
1093
|
-
"Promise is not an APIPromise or a regular promise, cannot instrument.",
|
|
1094
|
-
);
|
|
966
|
+
console.warn("Promise is not an APIPromise or a regular promise, cannot instrument.");
|
|
1095
967
|
return promise;
|
|
1096
968
|
}
|
|
1097
969
|
}
|