Skip to main content

lib/dot_prompt/telemetry.ex

defmodule DotPrompt.Telemetry do
  @moduledoc """
  Emits telemetry events.
  """
  use Agent

  def execute(event, measurements, metadata) do
    # Store event for UI
    store_event(event, measurements, metadata)
    :telemetry.execute([:dot_prompt | event], measurements, metadata)
  end

  def list_events do
    Agent.get(__MODULE__, & &1)
  end

  defp store_event(event, measurements, metadata) do
    if Process.whereis(__MODULE__) do
      Agent.update(__MODULE__, fn state ->
        Enum.take(
          [
            %{
              event: event,
              measurements: measurements,
              metadata: metadata,
              time: DateTime.utc_now()
            }
            | state
          ],
          20
        )
      end)
    end
  end

  def start_link(_) do
    Agent.start_link(fn -> [] end, name: __MODULE__)
  end

  def start_render(prompt_name, params) do
    execute([:render, :start], %{system_time: System.system_time()}, %{
      prompt: prompt_name,
      params: params
    })
  end

  def stop_render(prompt_name, params, duration_ms, measurements) do
    metadata =
      %{prompt: prompt_name, params: params}
      |> maybe_put(:vary_selections, measurements[:vary_selections])
      |> maybe_put(:cache_hit, measurements[:cache_hit])
      |> maybe_put(:compiled_tokens, measurements[:compiled_tokens])

    measurements =
      measurements
      |> Map.drop([:vary_selections, :cache_hit])
      |> Map.put(:duration, duration_ms)

    execute([:render, :stop], measurements, metadata)
  end

  defp maybe_put(map, _key, nil), do: map
  defp maybe_put(map, key, value), do: Map.put(map, key, value)
end