Skip to content
Effect 4.0 is here. One ecosystem. Zero dependencies.

Telemetry unstable

Adds OpenTelemetry GenAI attributes to Effect AI spans.

This module models the gen_ai.* attributes used by language model and embedding providers. It includes attribute types, helpers for writing non-null attributes onto existing spans, and a CurrentSpanTransformer service for adding custom span annotations from provider options and response parts.

17 exports Since v4.0.0 Source

Annotations

Since v4.0.0

Applies GenAI telemetry attributes to an OpenTelemetry span.

When to use

Use when you need to write GenAI request, response, or usage attributes onto an existing OpenTelemetry span.

Details

This function adds standardized GenAI attributes to a span following OpenTelemetry semantic conventions.

Gotchas

This function mutates the provided span in-place.

Signature

declare const addGenAIAnnotations: {
(options: GenAITelemetryAttributeOptions): (span: Span) => void;
(span: Span, options: GenAITelemetryAttributeOptions): void;
}

Example Adding GenAI telemetry annotations

import { Effect } from "effect"
import { Telemetry } from "effect/ai"
const directUsage = Effect.gen(function*() {
const span = yield* Effect.currentSpan
Telemetry.addGenAIAnnotations(span, {
provider: { name: "openai" },
request: { model: "gpt-4", temperature: 0.7 },
usage: { inputTokens: 100, outputTokens: 50 }
})
return (span as { attributes: ReadonlyMap<string, unknown> }).attributes.size
})
await Effect.runPromise(Effect.withSpan(directUsage, "example")) // => 5
Since v4.0.0

Creates a reusable span-attribute writer for a key prefix and key transformer.

Details

The returned function mutates the supplied span by adding each non-nullish attribute as ${prefix}.${transformedKey}.

Signature

declare function addSpanAttributes(keyPrefix: string, transformKey: (key: string) => string): <Attributes extends Record<string, any>>(span: Span, attributes: Attributes) => void

Example Adding prefixed span attributes

import { Context, Option, String, Tracer } from "effect"
import { Telemetry } from "effect/ai"
const addCustomAttributes = Telemetry.addSpanAttributes(
"custom.ai",
String.camelToSnake
)
const span = new Tracer.NativeSpan({
name: "request",
parent: Option.none(),
annotations: Context.empty(),
links: [],
startTime: 0n,
kind: "internal",
sampled: true
})
addCustomAttributes(span, {
modelName: "gpt-4",
maxTokens: 1000
})
Array.from(span.attributes.keys()) // => ["custom.ai.model_name", "custom.ai.max_tokens"]

Models

Since v4.0.0

The attributes used to describe telemetry in the context of Generative Artificial Intelligence (GenAI) models requests and responses.

Details

These attributes follow the OpenTelemetry generative AI semantic conventions: https://github.com/open-telemetry/semantic-conventions-genai

Signature

type GenAITelemetryAttributes = Struct.Simplify<AttributesWithPrefix<ProviderAttributes, "gen_ai.provider"> & AttributesWithPrefix<OperationAttributes, "gen_ai.operation"> & AttributesWithPrefix<OutputAttributes, "gen_ai.output"> & AttributesWithPrefix<UsageAttributes, "gen_ai.usage"> & AttributesWithPrefix<RequestAttributes, "gen_ai.request"> & AttributesWithPrefix<ResponseAttributes, "gen_ai.response">>

OperationAttributes

interface unstable
Since v4.0.0

Telemetry attributes which are part of the GenAI specification and are namespaced by gen_ai.operation.

Signature

interface OperationAttributes {
readonly name?: string & {} | WellKnownOperationName | null;
}

OutputAttributes

interface unstable
Since v4.0.2

Telemetry attributes which are part of the GenAI specification and are namespaced by gen_ai.output.

Signature

interface OutputAttributes {
readonly type?: string & {} | WellKnownOutputType | null;
}

ProviderAttributes

interface unstable
Since v4.0.2

Telemetry attributes which are part of the GenAI specification and are namespaced by gen_ai.provider.

Signature

interface ProviderAttributes {
readonly name?: string & {} | WellKnownProviderName | null;
}

RequestAttributes

interface unstable
Since v4.0.0

Telemetry attributes which are part of the GenAI specification and are namespaced by gen_ai.request.

Signature

interface RequestAttributes {
readonly encodingFormats?: readonly Array<string> | null;
readonly frequencyPenalty?: number | null;
readonly maxTokens?: number | null;
readonly model?: string | null;
readonly presencePenalty?: number | null;
readonly seed?: number | null;
readonly stopSequences?: readonly Array<string> | null;
readonly temperature?: number | null;
readonly topK?: number | null;
readonly topP?: number | null;
}

ResponseAttributes

interface unstable
Since v4.0.0

Telemetry attributes which are part of the GenAI specification and are namespaced by gen_ai.response.

Signature

interface ResponseAttributes {
readonly finishReasons?: readonly Array<string> | null;
readonly id?: string | null;
readonly model?: string | null;
}

SpanTransformer

interface unstable
Since v4.0.0

A function that can transform OpenTelemetry spans based on AI operation data.

Details

Span transformers receive the complete request/response context from AI operations and can add custom telemetry attributes, metrics, or other observability data.

Signature

interface SpanTransformer {
(options: ProviderOptions & {
readonly response: readonly Array<AllParts<any>>;
}): void;
}

Example Transforming AI spans

import type { Telemetry } from "effect/ai"
const customTransformer: Telemetry.SpanTransformer = ({ response, span }) => {
// Add custom attributes based on the response
const textParts = response.filter((part) => part.type === "text")
const totalTextLength = textParts.reduce(
(sum, part) => sum + (part.type === "text" ? part.text.length : 0),
0
)
span.attribute("total_text_length", totalTextLength)
}
typeof customTransformer // => "function"

UsageAttributes

interface unstable
Since v4.0.0

Telemetry attributes which are part of the GenAI specification and are namespaced by gen_ai.usage.

Signature

interface UsageAttributes {
readonly inputTokens?: number | null;
readonly outputTokens?: number | null;
}
Since v4.0.0

The gen_ai.operation.name attribute has the following list of well-known values.

Details

If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used.

Signature

type WellKnownOperationName = "chat" | "embeddings" | "text_completion"

WellKnownOutputType

type unstable
Since v4.0.2

The gen_ai.output.type attribute has the following list of well-known values.

Signature

type WellKnownOutputType = "text" | "json" | "image" | "speech"
Since v4.0.2

The gen_ai.provider.name attribute has the following list of well-known values.

Details

If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used.

Signature

type WellKnownProviderName = "anthropic" | "aws.bedrock" | "azure.ai.inference" | "azure.ai.openai" | "cohere" | "deepseek" | "gcp.gemini" | "gcp.gen_ai" | "gcp.vertex_ai" | "groq" | "ibm.watsonx.ai" | "mistral_ai" | "moonshot_ai" | "openai" | "perplexity" | "x_ai"

Options

Configuration options for GenAI telemetry attributes.

Details

Groups attributes by their gen_ai.* namespace.

Signature

type GenAITelemetryAttributeOptions = {
readonly operation?: OperationAttributes;
readonly output?: OutputAttributes;
readonly provider?: ProviderAttributes;
readonly request?: RequestAttributes;
readonly response?: ResponseAttributes;
readonly usage?: UsageAttributes;
}

Example Configuring GenAI telemetry attributes

import type { Telemetry } from "effect/ai"
const telemetryOptions: Telemetry.GenAITelemetryAttributeOptions = {
provider: { name: "openai" },
operation: {
name: "chat"
},
request: {
model: "gpt-4-turbo",
temperature: 0.7,
maxTokens: 2000
},
response: {
id: "chatcmpl-123",
model: "gpt-4-turbo-2024-04-09",
finishReasons: ["stop"]
},
usage: {
inputTokens: 50,
outputTokens: 25
}
}
const result = [telemetryOptions.provider?.name, telemetryOptions.usage?.inputTokens] // => ["openai", 50]

Services

Service tag for providing a SpanTransformer to large language model operations.

When to use

Use to retrieve or provide the current SpanTransformer through context for language model span annotation.

See

Signature

declare class CurrentSpanTransformer extends Shape<"effect/ai/Telemetry/CurrentSpanTransformer", SpanTransformer, this> {
constructor(_: never);
}

Utility Types

Since v4.0.0

Utility type for prefixing attribute names with a namespace.

Details

Transforms attribute keys by adding a prefix and formatting them according to OpenTelemetry conventions (camelCase to snake_case).

Signature

type AttributesWithPrefix<Attributes extends Record<string, any>, Prefix extends string> = { [Name in keyof Attributes]: Attributes[Name] }

Example Prefixing telemetry attributes

import type { Telemetry } from "effect/ai"
type RequestAttrs = {
modelName: string
maxTokens: number
}
type PrefixedAttrs = Telemetry.AttributesWithPrefix<
RequestAttrs,
"gen_ai.request"
>
// Results in: {
// "gen_ai.request.model_name": string
// "gen_ai.request.max_tokens": number
// }
const attributes: PrefixedAttrs = {
"gen_ai.request.model_name": "gpt-4",
"gen_ai.request.max_tokens": 1000
}
Object.keys(attributes) // => ["gen_ai.request.model_name", "gen_ai.request.max_tokens"]

FormatAttributeName

type unstable
Since v4.0.0

Utility type for converting camelCase names to snake_case format.

Details

This type recursively transforms string literal types from camelCase to snake_case, which is the standard format for OpenTelemetry attributes.

Signature

type FormatAttributeName<T extends string | number | symbol> = T extends string ? T extends `${infer First}${infer Rest}` ? `${First extends Uppercase<First> ? "_" : ""}${Lowercase<First>}${FormatAttributeName<Rest>}` : T : never

Example Formatting attribute names

import type { Telemetry } from "effect/ai"
type Formatted1 = Telemetry.FormatAttributeName<"modelName"> // "model_name"
type Formatted2 = Telemetry.FormatAttributeName<"maxTokens"> // "max_tokens"
type Formatted3 = Telemetry.FormatAttributeName<"temperature"> // "temperature"
const formatted: [Formatted1, Formatted2, Formatted3] = [
"model_name",
"max_tokens",
"temperature"
]
formatted // => ["model_name", "max_tokens", "temperature"]