# `Gemini.Telemetry`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.15.0/lib/gemini/telemetry.ex#L1)

Telemetry instrumentation helpers for Gemini library.

This module provides functions to emit telemetry events for requests, streaming,
and other operations throughout the library. It supports the standard telemetry
events defined in the Gemini library specification:

- `[:gemini, :request, :start]` - HTTP request started
- `[:gemini, :request, :stop]` - HTTP request completed successfully
- `[:gemini, :request, :exception]` - HTTP request failed with exception
- `[:gemini, :stream, :start]` - Streaming request started
- `[:gemini, :stream, :chunk]` - Streaming chunk received
- `[:gemini, :stream, :stop]` - Streaming request completed
- `[:gemini, :stream, :exception]` - Streaming request failed with exception

All telemetry events respect the global telemetry configuration and can be
disabled by setting `telemetry_enabled: false` in the application config.

## Types

The module works with several key data types for telemetry metadata and measurements.

# `content_type`

```elixir
@type content_type() :: :text | :multimodal | :unknown
```

# `http_method`

```elixir
@type http_method() :: :get | :post | :put | :delete | :patch | atom()
```

# `stream_id`

```elixir
@type stream_id() :: binary()
```

# `telemetry_event`

```elixir
@type telemetry_event() :: [atom()]
```

# `telemetry_measurements`

```elixir
@type telemetry_measurements() :: map()
```

# `telemetry_metadata`

```elixir
@type telemetry_metadata() :: map()
```

# `build_request_metadata`

```elixir
@spec build_request_metadata(binary(), http_method(), keyword()) ::
  telemetry_metadata()
```

Build base metadata for HTTP requests with additional context.

Creates a standardized metadata map for telemetry events related to
HTTP requests, including URL, method, model, and other contextual information.

## Parameters

- `url` - The request URL
- `method` - The HTTP method (atom)
- `opts` - Optional keyword list with additional metadata

## Returns

A map containing standardized request metadata.

## Examples

    iex> metadata = Gemini.Telemetry.build_request_metadata("/api/generate", :post, model: "gemini-flash-lite-latest")
    iex> metadata.url
    "/api/generate"
    iex> metadata.method
    :post
    iex> metadata.model
    "gemini-flash-lite-latest"

# `build_stream_metadata`

```elixir
@spec build_stream_metadata(binary(), http_method(), stream_id(), keyword()) ::
  telemetry_metadata()
```

Build base metadata for streaming requests with additional context.

Creates a standardized metadata map for telemetry events related to
streaming requests, including all standard request metadata plus
stream-specific information like stream ID.

## Parameters

- `url` - The request URL
- `method` - The HTTP method (atom)
- `stream_id` - Unique identifier for the stream
- `opts` - Optional keyword list with additional metadata

## Returns

A map containing standardized streaming metadata.

## Examples

    iex> stream_id = "abc123def456"
    iex> metadata = Gemini.Telemetry.build_stream_metadata("/api/stream", :post, stream_id)
    iex> metadata.stream_id
    "abc123def456"
    iex> metadata.url
    "/api/stream"

# `calculate_duration`

```elixir
@spec calculate_duration(integer()) :: non_neg_integer()
```

Calculate duration in milliseconds from start time.

Computes the elapsed time between a start time (in native units)
and the current time, returning the duration in milliseconds.

## Parameters

- `start_time` - Start time in native time units (from `System.monotonic_time/0`)

## Returns

Duration in milliseconds as an integer.

## Examples

    iex> start_time = System.monotonic_time()
    iex> :timer.sleep(10)  # Sleep for 10ms
    iex> duration = Gemini.Telemetry.calculate_duration(start_time)
    iex> duration >= 10
    true

# `classify_contents`

```elixir
@spec classify_contents(term()) :: content_type()
```

Classify content types for telemetry metadata.

Analyzes the content structure to determine if it contains only text,
multimodal data (text + images/other media), or unknown content types.
This classification helps with telemetry analysis and monitoring.

## Parameters

- `contents` - The content to classify (string, list, or other)

## Returns

- `:text` - For plain text content
- `:multimodal` - For content containing non-text elements
- `:unknown` - For unrecognized content types

## Examples

    iex> Gemini.Telemetry.classify_contents("Hello world")
    :text

    iex> Gemini.Telemetry.classify_contents([%{parts: [%{text: "Hello"}]}])
    :text

    iex> Gemini.Telemetry.classify_contents([%{parts: [%{text: "Hello"}, %{image: "data"}]}])
    :multimodal

    iex> Gemini.Telemetry.classify_contents(%{unknown: "format"})
    :unknown

# `execute`

```elixir
@spec execute(telemetry_event(), telemetry_measurements(), telemetry_metadata()) ::
  :ok
```

Execute a telemetry event if telemetry is enabled.

This function conditionally emits telemetry events based on the global
telemetry configuration. If telemetry is disabled, the function returns
immediately without executing the event.

## Parameters

- `event` - A list of atoms representing the telemetry event name
- `measurements` - A map of numeric measurements (e.g., duration, size)
- `metadata` - A map of contextual information about the event

## Examples

    iex> Gemini.Telemetry.execute([:gemini, :request, :start], %{}, %{url: "/api"})
    :ok

    iex> # When telemetry is disabled, no event is emitted
    iex> Application.put_env(:gemini, :telemetry_enabled, false)
    iex> Gemini.Telemetry.execute([:gemini, :request, :start], %{}, %{})
    :ok

# `extract_model`

```elixir
@spec extract_model(keyword() | term()) :: binary()
```

Extract model name from options or use default.

Retrieves the model name from a keyword list of options, falling back
to the system default model if not specified.

## Parameters

- `opts` - Keyword list of options that may contain a `:model` key

## Returns

The model name as a string.

## Examples

    iex> Gemini.Telemetry.extract_model(model: "gemini-flash-lite-latest")
    "gemini-flash-lite-latest"

    iex> Gemini.Telemetry.extract_model([])
    Gemini.Config.get_model(:default)  # default model

    iex> Gemini.Telemetry.extract_model("not a keyword list")
    Gemini.Config.get_model(:default)  # fallback to default

# `generate_stream_id`

```elixir
@spec generate_stream_id() :: stream_id()
```

Generate unique stream IDs for telemetry tracking.

Creates a cryptographically secure random identifier for tracking
streaming operations across multiple telemetry events.

## Returns

A 16-character lowercase hexadecimal string representing a unique stream ID.

## Examples

    iex> stream_id = Gemini.Telemetry.generate_stream_id()
    iex> is_binary(stream_id) and byte_size(stream_id) == 16
    true

    iex> # Stream IDs should be unique
    iex> id1 = Gemini.Telemetry.generate_stream_id()
    iex> id2 = Gemini.Telemetry.generate_stream_id()
    iex> id1 != id2
    true

# `has_non_text_parts?`

```elixir
@spec has_non_text_parts?(term()) :: boolean()
```

Check if content has non-text parts (for multimodal classification).

Examines a content structure to determine if it contains any non-text
elements such as images, audio, or other media types.

## Parameters

- `content` - A content structure with parts to examine

## Returns

- `true` - If the content contains non-text parts
- `false` - If the content contains only text or is not recognized

## Examples

    iex> Gemini.Telemetry.has_non_text_parts?(%{parts: [%{text: "Hello"}]})
    false

    iex> Gemini.Telemetry.has_non_text_parts?(%{parts: [%{text: "Hello"}, %{image: "data"}]})
    true

    iex> Gemini.Telemetry.has_non_text_parts?("not a content structure")
    false

# `redact`

```elixir
@spec redact(term(), [String.t()]) :: term()
```

Recursively redact secret-bearing keys, credential query values, and exact
transient secret values from telemetry, errors, and retry surfaces.

# `redact_url`

```elixir
@spec redact_url(String.t(), [String.t()]) :: String.t()
```

Redact credential-like query parameters from a URL or path.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
