Skip to content

Commit 6a5b676

Browse files
committed
feat(instrumentation-openai): update semconv, events, and types
1 parent 86eef0d commit 6a5b676

File tree

4 files changed

+1079
-157
lines changed

4 files changed

+1079
-157
lines changed

packages/instrumentation-openai/src/instrumentation.ts

Lines changed: 46 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ import type {
4343
import type { Stream } from 'openai/streaming';
4444

4545
import {
46-
ATTR_EVENT_NAME,
4746
ATTR_GEN_AI_OPERATION_NAME,
4847
ATTR_GEN_AI_REQUEST_ENCODING_FORMATS,
4948
ATTR_GEN_AI_REQUEST_FREQUENCY_PENALTY,
@@ -62,12 +61,22 @@ import {
6261
ATTR_GEN_AI_USAGE_OUTPUT_TOKENS,
6362
METRIC_GEN_AI_CLIENT_OPERATION_DURATION,
6463
METRIC_GEN_AI_CLIENT_TOKEN_USAGE,
64+
EVENT_GEN_AI_CHOICE,
65+
EVENT_GEN_AI_SYSTEM_MESSAGE,
66+
EVENT_GEN_AI_USER_MESSAGE,
67+
EVENT_GEN_AI_ASSISTANT_MESSAGE,
68+
EVENT_GEN_AI_TOOL_MESSAGE,
69+
GEN_AI_TOKEN_TYPE_VALUE_INPUT,
70+
GEN_AI_TOKEN_TYPE_VALUE_OUTPUT,
71+
GEN_AI_OPERATION_NAME_VALUE_CHAT,
72+
GEN_AI_OPERATION_NAME_VALUE_EMBEDDINGS,
73+
GEN_AI_PROVIDER_NAME_VALUE_OPENAI,
6574
} from './semconv';
6675
/** @knipignore */
6776
import { PACKAGE_NAME, PACKAGE_VERSION } from './version';
6877
import { getEnvBool, getAttrsFromBaseURL } from './utils';
69-
import { OpenAIInstrumentationConfig } from './types';
70-
import {
78+
import type { OpenAIInstrumentationConfig } from './types';
79+
import type {
7180
APIPromise,
7281
GenAIMessage,
7382
GenAIChoiceEventBody,
@@ -78,14 +87,6 @@ import {
7887
GenAIToolCall,
7988
} from './internal-types';
8089

81-
// The JS semconv package doesn't yet emit constants for event names.
82-
// TODO: otel-js issue for semconv pkg not including event names
83-
const EVENT_GEN_AI_SYSTEM_MESSAGE = 'gen_ai.system.message';
84-
const EVENT_GEN_AI_USER_MESSAGE = 'gen_ai.user.message';
85-
const EVENT_GEN_AI_ASSISTANT_MESSAGE = 'gen_ai.assistant.message';
86-
const EVENT_GEN_AI_TOOL_MESSAGE = 'gen_ai.tool.message';
87-
const EVENT_GEN_AI_CHOICE = 'gen_ai.choice';
88-
8990
export class OpenAIInstrumentation extends InstrumentationBase<OpenAIInstrumentationConfig> {
9091
private _genaiClientOperationDuration!: Histogram;
9192
private _genaiClientTokenUsage!: Histogram;
@@ -169,6 +170,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<OpenAIInstrumenta
169170
);
170171
}
171172

173+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
172174
_getPatchedChatCompletionsCreate(): any {
173175
const self = this;
174176
return (original: ChatCompletions['create']) => {
@@ -186,7 +188,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<OpenAIInstrumenta
186188
const config = self.getConfig();
187189
const startNow = performance.now();
188190

189-
let startInfo;
191+
let startInfo: ReturnType<OpenAIInstrumentation['_startChatCompletionsSpan']>;
190192
try {
191193
startInfo = self._startChatCompletionsSpan(
192194
params,
@@ -258,9 +260,9 @@ export class OpenAIInstrumentation extends InstrumentationBase<OpenAIInstrumenta
258260
) {
259261
// Attributes common to span, metrics, log events.
260262
const commonAttrs: Attributes = {
261-
[ATTR_GEN_AI_OPERATION_NAME]: 'chat',
263+
[ATTR_GEN_AI_OPERATION_NAME]: GEN_AI_OPERATION_NAME_VALUE_CHAT,
262264
[ATTR_GEN_AI_REQUEST_MODEL]: params.model,
263-
[ATTR_GEN_AI_SYSTEM]: 'openai',
265+
[ATTR_GEN_AI_SYSTEM]: GEN_AI_PROVIDER_NAME_VALUE_OPENAI,
264266
};
265267
Object.assign(commonAttrs, getAttrsFromBaseURL(baseURL, this._diag));
266268

@@ -271,16 +273,14 @@ export class OpenAIInstrumentation extends InstrumentationBase<OpenAIInstrumenta
271273
if (params.frequency_penalty != null) {
272274
attrs[ATTR_GEN_AI_REQUEST_FREQUENCY_PENALTY] = params.frequency_penalty;
273275
}
274-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
275-
if ((params as any).max_completion_tokens != null) {
276+
if (typeof params.max_completion_tokens === 'number') {
276277
attrs[ATTR_GEN_AI_REQUEST_MAX_TOKENS] =
277-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
278-
(params as any).max_completion_tokens;
279-
} else if (params.max_tokens != null) {
278+
params.max_completion_tokens;
279+
} else if (typeof params.max_tokens === 'number') {
280280
// `max_tokens` is deprecated in favour of `max_completion_tokens`.
281281
attrs[ATTR_GEN_AI_REQUEST_MAX_TOKENS] = params.max_tokens;
282282
}
283-
if (params.presence_penalty != null) {
283+
if (typeof params.presence_penalty === 'number') {
284284
attrs[ATTR_GEN_AI_REQUEST_PRESENCE_PENALTY] = params.presence_penalty;
285285
}
286286
if (params.stop != null) {
@@ -323,9 +323,9 @@ export class OpenAIInstrumentation extends InstrumentationBase<OpenAIInstrumenta
323323
timestamp,
324324
context: ctx,
325325
severityNumber: SeverityNumber.INFO,
326+
eventName: EVENT_GEN_AI_SYSTEM_MESSAGE,
326327
attributes: {
327-
[ATTR_EVENT_NAME]: EVENT_GEN_AI_SYSTEM_MESSAGE,
328-
[ATTR_GEN_AI_SYSTEM]: 'openai',
328+
[ATTR_GEN_AI_SYSTEM]: GEN_AI_PROVIDER_NAME_VALUE_OPENAI,
329329
},
330330
body,
331331
});
@@ -347,9 +347,9 @@ export class OpenAIInstrumentation extends InstrumentationBase<OpenAIInstrumenta
347347
timestamp,
348348
context: ctx,
349349
severityNumber: SeverityNumber.INFO,
350+
eventName: EVENT_GEN_AI_USER_MESSAGE,
350351
attributes: {
351-
[ATTR_EVENT_NAME]: EVENT_GEN_AI_USER_MESSAGE,
352-
[ATTR_GEN_AI_SYSTEM]: 'openai',
352+
[ATTR_GEN_AI_SYSTEM]: GEN_AI_PROVIDER_NAME_VALUE_OPENAI,
353353
},
354354
body,
355355
});
@@ -403,9 +403,9 @@ export class OpenAIInstrumentation extends InstrumentationBase<OpenAIInstrumenta
403403
timestamp,
404404
context: ctx,
405405
severityNumber: SeverityNumber.INFO,
406+
eventName: EVENT_GEN_AI_ASSISTANT_MESSAGE,
406407
attributes: {
407-
[ATTR_EVENT_NAME]: EVENT_GEN_AI_ASSISTANT_MESSAGE,
408-
[ATTR_GEN_AI_SYSTEM]: 'openai',
408+
[ATTR_GEN_AI_SYSTEM]: GEN_AI_PROVIDER_NAME_VALUE_OPENAI,
409409
},
410410
body,
411411
});
@@ -426,9 +426,9 @@ export class OpenAIInstrumentation extends InstrumentationBase<OpenAIInstrumenta
426426
timestamp,
427427
context: ctx,
428428
severityNumber: SeverityNumber.INFO,
429+
eventName: EVENT_GEN_AI_TOOL_MESSAGE,
429430
attributes: {
430-
[ATTR_EVENT_NAME]: EVENT_GEN_AI_TOOL_MESSAGE,
431-
[ATTR_GEN_AI_SYSTEM]: 'openai',
431+
[ATTR_GEN_AI_SYSTEM]: GEN_AI_PROVIDER_NAME_VALUE_OPENAI,
432432
},
433433
body,
434434
});
@@ -450,19 +450,19 @@ export class OpenAIInstrumentation extends InstrumentationBase<OpenAIInstrumenta
450450
* data from those chunks, then end the span.
451451
*/
452452
async *_onChatCompletionsStreamIterator(
453-
streamIter: AsyncIterator<ChatCompletionChunk>,
453+
iterator: AsyncIterator<ChatCompletionChunk>,
454454
span: Span,
455455
startNow: number,
456456
config: OpenAIInstrumentationConfig,
457457
commonAttrs: Attributes,
458458
ctx: Context
459459
) {
460-
let id;
461-
let model;
460+
const iterable = { [Symbol.asyncIterator]: () => iterator };
461+
let id: string | undefined;
462+
let model: string | undefined;
462463
const finishReasons: string[] = [];
463464
const choices = [];
464-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
465-
for await (const chunk of streamIter as any) {
465+
for await (const chunk of iterable) {
466466
yield chunk;
467467

468468
// Gather telemetry from this chunk.
@@ -542,12 +542,12 @@ export class OpenAIInstrumentation extends InstrumentationBase<OpenAIInstrumenta
542542
this._genaiClientTokenUsage.record(chunk.usage.prompt_tokens, {
543543
...commonAttrs,
544544
[ATTR_GEN_AI_RESPONSE_MODEL]: model,
545-
[ATTR_GEN_AI_TOKEN_TYPE]: 'input',
545+
[ATTR_GEN_AI_TOKEN_TYPE]: GEN_AI_TOKEN_TYPE_VALUE_INPUT,
546546
});
547547
this._genaiClientTokenUsage.record(chunk.usage.completion_tokens, {
548548
...commonAttrs,
549549
[ATTR_GEN_AI_RESPONSE_MODEL]: model,
550-
[ATTR_GEN_AI_TOKEN_TYPE]: 'output',
550+
[ATTR_GEN_AI_TOKEN_TYPE]: GEN_AI_TOKEN_TYPE_VALUE_OUTPUT,
551551
});
552552
}
553553
}
@@ -581,9 +581,9 @@ export class OpenAIInstrumentation extends InstrumentationBase<OpenAIInstrumenta
581581
timestamp: Date.now(),
582582
context: ctx,
583583
severityNumber: SeverityNumber.INFO,
584+
eventName: EVENT_GEN_AI_CHOICE,
584585
attributes: {
585-
[ATTR_EVENT_NAME]: EVENT_GEN_AI_CHOICE,
586-
[ATTR_GEN_AI_SYSTEM]: 'openai',
586+
[ATTR_GEN_AI_SYSTEM]: GEN_AI_PROVIDER_NAME_VALUE_OPENAI,
587587
},
588588
body: {
589589
finish_reason: finishReasons[idx],
@@ -660,9 +660,9 @@ export class OpenAIInstrumentation extends InstrumentationBase<OpenAIInstrumenta
660660
timestamp: Date.now(),
661661
context: ctx,
662662
severityNumber: SeverityNumber.INFO,
663+
eventName: EVENT_GEN_AI_CHOICE,
663664
attributes: {
664-
[ATTR_EVENT_NAME]: EVENT_GEN_AI_CHOICE,
665-
[ATTR_GEN_AI_SYSTEM]: 'openai',
665+
[ATTR_GEN_AI_SYSTEM]: GEN_AI_PROVIDER_NAME_VALUE_OPENAI,
666666
},
667667
body: {
668668
finish_reason: choice.finish_reason,
@@ -684,13 +684,13 @@ export class OpenAIInstrumentation extends InstrumentationBase<OpenAIInstrumenta
684684
this._genaiClientTokenUsage.record(result.usage.prompt_tokens, {
685685
...commonAttrs,
686686
[ATTR_GEN_AI_RESPONSE_MODEL]: result.model,
687-
[ATTR_GEN_AI_TOKEN_TYPE]: 'input',
687+
[ATTR_GEN_AI_TOKEN_TYPE]: GEN_AI_TOKEN_TYPE_VALUE_INPUT,
688688
});
689689

690690
this._genaiClientTokenUsage.record(result.usage.completion_tokens, {
691691
...commonAttrs,
692692
[ATTR_GEN_AI_RESPONSE_MODEL]: result.model,
693-
[ATTR_GEN_AI_TOKEN_TYPE]: 'output',
693+
[ATTR_GEN_AI_TOKEN_TYPE]: GEN_AI_TOKEN_TYPE_VALUE_OUTPUT,
694694
});
695695
}
696696
} catch (err) {
@@ -734,6 +734,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<OpenAIInstrumenta
734734
};
735735
}
736736

737+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
737738
_getPatchedEmbeddingsCreate(): any {
738739
const self = this;
739740
return (original: Embeddings['create']) => {
@@ -750,7 +751,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<OpenAIInstrumenta
750751
const params = args[0];
751752
const startNow = performance.now();
752753

753-
let startInfo;
754+
let startInfo: ReturnType<OpenAIInstrumentation['_startEmbeddingsSpan']>;
754755
try {
755756
startInfo = self._startEmbeddingsSpan(params, this?._client?.baseURL);
756757
} catch (err) {
@@ -784,9 +785,9 @@ export class OpenAIInstrumentation extends InstrumentationBase<OpenAIInstrumenta
784785
) {
785786
// Attributes common to span, metrics, log events.
786787
const commonAttrs: Attributes = {
787-
[ATTR_GEN_AI_OPERATION_NAME]: 'embeddings',
788+
[ATTR_GEN_AI_OPERATION_NAME]: GEN_AI_OPERATION_NAME_VALUE_EMBEDDINGS,
788789
[ATTR_GEN_AI_REQUEST_MODEL]: params.model,
789-
[ATTR_GEN_AI_SYSTEM]: 'openai',
790+
[ATTR_GEN_AI_SYSTEM]: GEN_AI_PROVIDER_NAME_VALUE_OPENAI,
790791
};
791792
Object.assign(commonAttrs, getAttrsFromBaseURL(baseURL, this._diag));
792793

@@ -835,7 +836,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<OpenAIInstrumenta
835836
this._genaiClientTokenUsage.record(result.usage.prompt_tokens, {
836837
...commonAttrs,
837838
[ATTR_GEN_AI_RESPONSE_MODEL]: result.model,
838-
[ATTR_GEN_AI_TOKEN_TYPE]: 'input',
839+
[ATTR_GEN_AI_TOKEN_TYPE]: GEN_AI_TOKEN_TYPE_VALUE_INPUT,
839840
});
840841
} catch (err) {
841842
this._diag.error(

packages/instrumentation-openai/src/internal-types.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
import { AnyValue } from '@opentelemetry/api-logs';
16+
import type { AnyValue } from '@opentelemetry/api-logs';
1717

1818
// This mimicks `APIPromise` from `openai` sufficiently for usage in this
1919
// instrumentation. OpenAI's APIPromise adds some methods, but we don't use
@@ -22,13 +22,13 @@ import { AnyValue } from '@opentelemetry/api-logs';
2222
export type APIPromise<T> = Promise<T>;
2323

2424
export type GenAIFunction = {
25-
name: string;
25+
name: string | undefined;
2626
arguments?: AnyValue;
2727
};
2828

2929
export type GenAIToolCall = {
3030
id: string;
31-
type: string;
31+
type: string | undefined;
3232
function?: GenAIFunction;
3333
};
3434

0 commit comments

Comments
 (0)