AshBaml.Telemetry (ash_baml v0.2.0)

View Source

Telemetry integration for AshBaml.

Provides observability into BAML function calls through Elixir's :telemetry ecosystem. Integrates with baml_elixir's collector API to track token usage, performance, and execution details.

Events

This module emits three telemetry events per BAML function call:

  • [:ash_baml, :call, :start] - Before function execution
  • [:ash_baml, :call, :stop] - After successful execution
  • [:ash_baml, :call, :exception] - On error

Event names can be customized via the prefix DSL option.

Measurements

:start event

%{
  system_time: System.system_time(),
  monotonic_time: System.monotonic_time()
}

:stop event

%{
  duration: native_time,              # Time elapsed
  input_tokens: 150,                   # From collector
  output_tokens: 75,                   # From collector
  total_tokens: 225,                   # Sum of input + output
  monotonic_time: System.monotonic_time()
}

:exception event

%{
  duration: native_time,
  monotonic_time: System.monotonic_time()
}

Metadata

All events include standard metadata:

%{
  resource: MyApp.Assistant,
  action: :chat,
  function_name: "ChatAgent",
  collector_name: "MyApp.Assistant-ChatAgent-12345"
}

The :stop event additionally includes observability metadata:

%{
  model_name: "gpt-4",
  provider: "openai",
  client_name: "GPT4Client",
  num_attempts: 1,
  request_id: "req_abc123",
  raw_response: "The capital of France is Paris.",
  tags: %{"environment" => "production"},
  log_type: "call",
  http_request: %{
    url: "https://api.openai.com/v1/chat/completions",
    method: "POST",
    headers: %{"content-type" => "application/json"},
    body: "{...}"
  },
  http_response: %{
    status_code: 200,
    headers: %{"content-type" => "application/json"},
    body: "{...}"
  }
}

Additional metadata can be configured via the DSL.

Privacy

By default, only safe, aggregate data is included:

  • Token counts
  • Timing information
  • Resource/action/function names

Sensitive data (prompts, responses, arguments) is NEVER included without explicit configuration.

Performance

When telemetry is disabled (default), this module has near-zero overhead through early return checks. When enabled, overhead is minimal (~10µs per call for collector creation and usage reading).

Example

:telemetry.attach(
  "log-token-usage",
  [:ash_baml, :call, :stop],
  fn _event, measurements, metadata, _config ->
    Logger.info("BAML call completed",
      function: metadata.function_name,
      tokens: measurements.total_tokens,
      duration_ms: System.convert_time_unit(
        measurements.duration,
        :native,
        :millisecond
      )
    )
  end,
  nil
)

Summary

Functions

Wraps a BAML function call with telemetry.

Functions

with_telemetry(input, function_name, config, func)

Wraps a BAML function call with telemetry.

Emits :start, :stop, and :exception events with measurements and metadata. Creates a BamlElixir.Collector to track token usage and execution details.

Arguments

  • input - The Ash action input containing resource, action, and arguments
  • function_name - The BAML function name (atom)
  • config - Telemetry configuration (from DSL)
  • func - Function to wrap (receives collector options)

Returns

Returns a tuple {result, collector} where result is {:ok, term()} or {:error, term()} from the function call, and collector is the BamlElixir.Collector reference used for the call.

Examples

{result, collector} = AshBaml.Telemetry.with_telemetry(
  input,
  :ChatAgent,
  config,
  fn collector_opts ->
    ClientModule.ChatAgent.call(args, collector_opts)
  end
)