1/**
2 * The `AnthropicTelemetry` module defines Anthropic-specific telemetry
3 * attributes and a helper for adding them to a tracing span. It keeps the
4 * standard GenAI telemetry attributes and adds request and response metadata
5 * under the `gen_ai.anthropic.*` 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 AnthropicTelemetryAttributes = Simplify<
29 & Telemetry.GenAITelemetryAttributes
30 & Telemetry.AttributesWithPrefix<RequestAttributes, "gen_ai.anthropic.request">
31 & Telemetry.AttributesWithPrefix<ResponseAttributes, "gen_ai.anthropic.response">
32>
33
34/**
35 * All telemetry attributes which are part of the GenAI specification,
36 * including the Anthropic-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.anthropic.request`.
46 *
47 * @category models
48 * @since 4.0.0
49 */
50export interface RequestAttributes {
51 /**
52 * Whether extended thinking is enabled.
53 */
54 readonly extendedThinking?: boolean | null | undefined
55 /**
56 * The budget tokens for extended thinking.
57 */
58 readonly thinkingBudgetTokens?: number | null | undefined
59}
60
61/**
62 * Telemetry attributes which are part of the GenAI specification and are
63 * namespaced by `gen_ai.anthropic.response`.
64 *
65 * @category models
66 * @since 4.0.0
67 */
68export interface ResponseAttributes {
69 /**
70 * The stop reason from the response.
71 */
72 readonly stopReason?: string | null | undefined
73 /**
74 * Number of cache creation input tokens.
75 */
76 readonly cacheCreationInputTokens?: number | null | undefined
77 /**
78 * Number of cache read input tokens.
79 */
80 readonly cacheReadInputTokens?: number | null | undefined
81}
82
83/**
84 * Options accepted by `addGenAIAnnotations`, combining standard GenAI telemetry attributes with optional Anthropic request and response attributes.
85 *
86 * @category options
87 * @since 4.0.0
88 */
89export type AnthropicTelemetryAttributeOptions = Telemetry.GenAITelemetryAttributeOptions & {
90 anthropic?: {
91 request?: RequestAttributes | undefined
92 response?: ResponseAttributes | undefined
93 } | undefined
94}
95
96const addAnthropicRequestAttributes = Telemetry.addSpanAttributes("gen_ai.anthropic.request", String.camelToSnake)<
97 RequestAttributes
98>
99const addAnthropicResponseAttributes = Telemetry.addSpanAttributes("gen_ai.anthropic.response", String.camelToSnake)<
100 ResponseAttributes
101>
102
103/**
104 * Applies the specified Anthropic GenAI telemetry attributes to the provided
105 * `Span`.
106 *
107 * **When to use**
108 *
109 * Use to annotate an Anthropic model span with standard GenAI telemetry
110 * attributes and Anthropic-specific request or response metadata.
111 *
112 * **Gotchas**
113 *
114 * This method mutates the `Span` in place.
115 *
116 * @category tracing
117 * @since 4.0.0
118 */
119export const addGenAIAnnotations: {
120 (options: AnthropicTelemetryAttributeOptions): (span: Span) => void
121 (span: Span, options: AnthropicTelemetryAttributeOptions): void
122} = dual(2, (span: Span, options: AnthropicTelemetryAttributeOptions) => {
123 Telemetry.addGenAIAnnotations(span, options)
124 if (options.anthropic != null) {
125 if (options.anthropic.request != null) {
126 addAnthropicRequestAttributes(span, options.anthropic.request)
127 }
128 if (options.anthropic.response != null) {
129 addAnthropicResponseAttributes(span, options.anthropic.response)
130 }
131 }
132})