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