Condukt.Telemetry (Condukt v0.20.0)

Copy Markdown View Source

Telemetry integration for Condukt.

Condukt emits telemetry events that can be used for monitoring, logging, and observability.

Events

Every event emitted from a Condukt.Session (and the runtime entry points that spin one up) carries a :session_id field in metadata. Sessions generate a UUIDv7 on start unless the caller supplies an :id option; downstream consumers can use it to correlate every event for a single run in their observability stack. See guides/telemetry.md for details.

Agent Events

  • [:condukt, :agent, :start] - Agent started processing a prompt

    • Measurements: %{system_time: integer}
    • Metadata: %{agent: module, session_id: String.t()}
  • [:condukt, :agent, :stop] - Agent finished processing

    • Measurements: %{duration: integer}
    • Metadata: %{agent: module, session_id: String.t()}
  • [:condukt, :agent, :exception] - Agent raised an exception

    • Measurements: %{duration: integer}
    • Metadata: %{agent: module, session_id: String.t(), kind: atom, reason: term, stacktrace: list}

LLM Turn Events

Wrap a single LLM round-trip inside an agent loop iteration. One :llm_turn pair fires per call to the underlying provider, so multi-turn agent runs emit one pair per turn (the same :session_id appears on each pair, with :turn increasing).

  • [:condukt, :llm_turn, :start] - LLM call started

    • Measurements: %{system_time: integer}
    • Metadata: %{agent: module, session_id: String.t(), model: term, turn: non_neg_integer, streaming?: boolean, messages: [Condukt.Message.t()], tool_count: non_neg_integer}
  • [:condukt, :llm_turn, :stop] - LLM call returned

    • Measurements: %{duration: integer}
    • Metadata: same as :start plus %{status: :ok | :error, assistant_message: Condukt.Message.t() | nil, usage: map | nil, finish_reason: atom | nil, error: term | nil}

  • [:condukt, :llm_turn, :exception] - LLM call raised an exception

    • Measurements: %{duration: integer}
    • Metadata: same as :start plus %{kind: atom, reason: term, stacktrace: list}

:messages is the conversation context handed to the model for this turn. :assistant_message is the model's response, including any tool calls it issued. Together they let downstream consumers persist a complete transcript of an agentic run.

Tool Events

  • [:condukt, :tool_call, :start] - Tool call started

    • Measurements: %{system_time: integer}
    • Metadata: %{tool: string, tool_call_id: string, args: map, agent: module, session_id: String.t()}
  • [:condukt, :tool_call, :stop] - Tool call completed

    • Measurements: %{duration: integer}
    • Metadata: %{tool: string, tool_call_id: string, args: map, agent: module, session_id: String.t(), status: :ok | :error, result: term}

  • [:condukt, :tool_call, :exception] - Tool call raised an exception

    • Measurements: %{duration: integer}
    • Metadata: %{tool: string, tool_call_id: string, args: map, agent: module, session_id: String.t(), kind: atom, reason: term, stacktrace: list}

:args is the parsed argument map the model passed to the tool. :result on :stop is the tool's return value after session-secret redaction; on errors it is the {:error, reason} tuple. Consumers that ship payloads to external systems should size-limit or sample these fields themselves.

Sub-agent Events

These events wrap the explicit sub-agent delegation lifecycle. They do not include task text, structured input values, or structured output values.

  • [:condukt, :subagent, :start] - Sub-agent delegation started

    • Measurements: %{system_time: integer}
    • Metadata: %{agent: module | pid, role: atom, child_agent: module, input?: boolean, output?: boolean, parent_session_id: String.t() | nil}

  • [:condukt, :subagent, :stop] - Sub-agent delegation finished

    • Measurements: %{duration: integer}
    • Metadata: %{agent: module | pid, role: atom, child_agent: module, input?: boolean, output?: boolean, status: :ok | :error, parent_session_id: String.t() | nil, session_id: String.t() | nil}

    • Error metadata: %{error: atom}

:parent_session_id is the calling session's id; :session_id (only on :stop) is the child session's id, present when the child started successfully.

Secret Events

These events never include secret values.

  • [:condukt, :secrets, :resolve] - Session secrets resolved

    • Measurements: %{count: non_neg_integer}
    • Metadata: %{agent: module, names: [String.t()], session_id: String.t()}
  • [:condukt, :secrets, :access] - A tool received resolved session secrets

    • Measurements: %{count: non_neg_integer}
    • Metadata: %{agent: module, tool: String.t(), names: [String.t()], session_id: String.t()}
    • Optional metadata: %{tool_call_id: String.t()}

Operation Events

Wrap a full Condukt.Operation.run/4 call (input validation, transient session run, output validation). The inner LLM loop still emits the [:condukt, :agent, ...] events for free.

  • [:condukt, :operation, :start] - Operation invocation started

    • Measurements: %{system_time: integer}
    • Metadata: %{agent: module, operation: atom, session_id: String.t()}
  • [:condukt, :operation, :stop] - Operation invocation finished

    • Measurements: %{duration: integer}
    • Metadata: %{agent: module, operation: atom, session_id: String.t()}
  • [:condukt, :operation, :exception] - Operation raised an exception

    • Measurements: %{duration: integer}
    • Metadata: %{agent: module, operation: atom, session_id: String.t(), kind: atom, reason: term, stacktrace: list}

Anonymous Run Events

Wrap a Condukt.run/2 call (the runtime entry point that does not require an agent module). The inner agent and tool events still fire for free.

  • [:condukt, :run, :start] - Anonymous run started

    • Measurements: %{system_time: integer}
    • Metadata: %{structured?: boolean, input?: boolean, session_id: String.t()}
  • [:condukt, :run, :stop] - Anonymous run finished

    • Measurements: %{duration: integer}
    • Metadata: %{structured?: boolean, input?: boolean, session_id: String.t()}
  • [:condukt, :run, :exception] - Anonymous run raised an exception

    • Measurements: %{duration: integer}
    • Metadata: %{structured?: boolean, input?: boolean, session_id: String.t(), kind: atom, reason: term, stacktrace: list}

Example: Attaching Handlers

:telemetry.attach_many(
  "my-agent-handler",
  [
    [:condukt, :agent, :start],
    [:condukt, :agent, :stop],
    [:condukt, :tool_call, :stop],
    [:condukt, :subagent, :start],
    [:condukt, :subagent, :stop],
    [:condukt, :secrets, :resolve],
    [:condukt, :secrets, :access]
  ],
  &MyApp.Telemetry.handle_event/4,
  nil
)

Summary

Functions

emit(event, measurements \\ %{}, metadata \\ %{})

Emits a telemetry event.

span(event, metadata, fun, augment_stop \\ fn _result -> %{} end)

Executes a function within a telemetry span.

Emits start, stop, and exception events for the given event name.

An optional augment_stop function receives the result of fun and returns a map of extra metadata to merge into the :stop event. Use it to attach result-derived fields (status, payload, …) that are only known once the wrapped work has finished.