1/**
2 * The `OpenAiTelemetry` module defines OpenAI-specific telemetry attributes
3 * and a helper for adding them to a tracing span. It keeps the standard GenAI
4 * telemetry attributes and adds request and response metadata, such as response
5 * format, service tier, and system fingerprint, under the `gen_ai.openai.*`
6 * OpenTelemetry namespaces.
7 *
8 * @since 4.0.0
9 */
10import * as Telemetry from "effect/ai/Telemetry"
11import { dual } from "effect/Function"
12import * as String from "effect/String"
13import type { Span } from "effect/Tracer"
14import type { Simplify } from "effect/Types"
15
16/**
17 * The attributes used to describe telemetry in the context of Generative
18 * Artificial Intelligence (GenAI) Models requests and responses.
19 *
20 * **Details**
21 *
22 * These attributes follow the OpenTelemetry generative AI semantic
23 * conventions:
24 * https://opentelemetry.io/docs/specs/semconv/attributes-registry/gen-ai/
25 *
26 * @category models
27 * @since 4.0.0
28 */
29export type OpenAiTelemetryAttributes = Simplify<
30 & Telemetry.GenAITelemetryAttributes
31 & Telemetry.AttributesWithPrefix<RequestAttributes, "gen_ai.openai.request">
32 & Telemetry.AttributesWithPrefix<ResponseAttributes, "gen_ai.openai.response">
33>
34
35/**
36 * All telemetry attributes which are part of the GenAI specification,
37 * including the OpenAI-specific attributes.
38 *
39 * @category models
40 * @since 4.0.0
41 */
42export type AllAttributes = Telemetry.AllAttributes & RequestAttributes & ResponseAttributes
43
44/**
45 * Telemetry attributes which are part of the GenAI specification and are
46 * namespaced by `gen_ai.openai.request`.
47 *
48 * @category models
49 * @since 4.0.0
50 */
51export interface RequestAttributes {
52 /**
53 * The response format that is requested.
54 */
55 readonly responseFormat?: (string & {}) | WellKnownResponseFormat | null | undefined
56 /**
57 * The service tier requested. May be a specific tier, `default`, or `auto`.
58 */
59 readonly serviceTier?: (string & {}) | WellKnownServiceTier | null | undefined
60}
61
62/**
63 * Telemetry attributes which are part of the GenAI specification and are
64 * namespaced by `gen_ai.openai.response`.
65 *
66 * @category models
67 * @since 4.0.0
68 */
69export interface ResponseAttributes {
70 /**
71 * The service tier used for the response.
72 */
73 readonly serviceTier?: string | null | undefined
74 /**
75 * A fingerprint to track any eventual change in the Generative AI
76 * environment.
77 */
78 readonly systemFingerprint?: string | null | undefined
79}
80
81/**
82 * The `gen_ai.openai.request.response_format` attribute has the following
83 * list of well-known values.
84 *
85 * **Details**
86 *
87 * If one of them applies, then the respective value **MUST** be used;
88 * otherwise, a custom value **MAY** be used.
89 *
90 * @category models
91 * @since 4.0.0
92 */
93export type WellKnownResponseFormat = "json_object" | "json_schema" | "text"
94
95/**
96 * The `gen_ai.openai.request.service_tier` attribute has the following
97 * list of well-known values.
98 *
99 * **Details**
100 *
101 * If one of them applies, then the respective value **MUST** be used;
102 * otherwise, a custom value **MAY** be used.
103 *
104 * @category models
105 * @since 4.0.0
106 */
107export type WellKnownServiceTier = "auto" | "default"
108
109/**
110 * Options accepted by `addGenAIAnnotations`, combining standard GenAI
111 * telemetry attributes with optional OpenAI request and response attributes.
112 *
113 * @category options
114 * @since 4.0.0
115 */
116export type OpenAiTelemetryAttributeOptions = Telemetry.GenAITelemetryAttributeOptions & {
117 openai?: {
118 request?: RequestAttributes | undefined
119 response?: ResponseAttributes | undefined
120 } | undefined
121}
122
123const addOpenAiRequestAttributes = Telemetry.addSpanAttributes("gen_ai.openai.request", String.camelToSnake)<
124 RequestAttributes
125>
126const addOpenAiResponseAttributes = Telemetry.addSpanAttributes("gen_ai.openai.response", String.camelToSnake)<
127 ResponseAttributes
128>
129
130/**
131 * Applies the specified OpenAI GenAI telemetry attributes to the provided
132 * `Span`.
133 *
134 * **When to use**
135 *
136 * Use to annotate an existing OpenTelemetry span with standard GenAI attributes
137 * plus OpenAI-specific request and response metadata.
138 *
139 * **Gotchas**
140 *
141 * Mutates the supplied `Span` in place.
142 *
143 * @see {@link OpenAiTelemetryAttributeOptions} for the accepted telemetry attributes
144 * @see {@link Telemetry.addGenAIAnnotations} for the provider-neutral annotation helper
145 *
146 * @category tracing
147 * @since 4.0.0
148 */
149export const addGenAIAnnotations: {
150 (options: OpenAiTelemetryAttributeOptions): (span: Span) => void
151 (span: Span, options: OpenAiTelemetryAttributeOptions): void
152} = dual(2, (span: Span, options: OpenAiTelemetryAttributeOptions) => {
153 Telemetry.addGenAIAnnotations(span, options)
154 if (options.openai != null) {
155 if (options.openai.request != null) {
156 addOpenAiRequestAttributes(span, options.openai.request)
157 }
158 if (options.openai.response != null) {
159 addOpenAiResponseAttributes(span, options.openai.response)
160 }
161 }
162})