Module: Sentry::Agents::Instrumentation

Defined in:
lib/sentry/agents/instrumentation.rb

Overview

Core instrumentation module that provides span helper methods

Include this module in any class that needs to create Sentry Gen AI spans. All methods are designed to gracefully degrade when Sentry is not available.

Examples:

Basic usage

class MyAgent
  include Sentry::Agents::Instrumentation

  def process(message)
    with_agent_span(agent_name: "MyAgent", model: "claude-3-5-sonnet") do
      with_chat_span(model: "claude-3-5-sonnet") do
        llm_client.chat(message)
      end
    end
  end
end

Instance Method Summary collapse

Instance Method Details

#with_agent_span(agent_name:, model:, system: nil) { ... } ⇒ Object

Wrap an agent invocation (e.g., full conversation lifecycle)

Creates a gen_ai.invoke_agent span that captures the overall agent execution. Token usage is automatically captured if the block result responds to :input_tokens and :output_tokens.

Examples:

with_agent_span(agent_name: "Emily", model: "claude-3-5-sonnet") do
  process_conversation
end

Parameters:

  • agent_name (String)

    name of the agent (e.g., "Emily", "CustomerService")

  • model (String)

    LLM model identifier (e.g., "claude-3-5-sonnet")

  • system (String, nil) (defaults to: nil)

    override default LLM provider system name

Yields:

  • the agent logic

Returns:

  • (Object)

    the block result



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/sentry/agents/instrumentation.rb', line 41

def with_agent_span(agent_name:, model:, system: nil)
  return yield unless sentry_tracing_available?

  SpanBuilder.build(
    operation: :invoke_agent,
    description: "invoke_agent #{agent_name}",
    attributes: {
      "gen_ai.operation.name" => "invoke_agent",
      "gen_ai.system" => system_name(system),
      "gen_ai.request.model" => model,
      "gen_ai.agent.name" => agent_name
    }
  ) do |span|
    result = yield
    capture_token_usage(span, result)
    result
  end
end

#with_chat_span(model:, messages: nil, system: nil) { ... } ⇒ Object

Wrap an LLM chat API call

Creates a gen_ai.chat span that captures a single LLM API call. Automatically captures token usage and response text if available.

Examples:

with_chat_span(model: "claude-3-5-sonnet", messages: conversation_history) do
  llm_client.chat(messages)
end

Parameters:

  • model (String)

    LLM model identifier

  • messages (Array<Hash>, nil) (defaults to: nil)

    optional message array for the request

  • system (String, nil) (defaults to: nil)

    override default LLM provider system name

Yields:

  • the LLM call

Returns:

  • (Object)

    the block result (should respond to :input_tokens, :output_tokens, :content)



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/sentry/agents/instrumentation.rb', line 76

def with_chat_span(model:, messages: nil, system: nil)
  return yield unless sentry_tracing_available?

  attributes = {
    "gen_ai.operation.name" => "chat",
    "gen_ai.system" => system_name(system),
    "gen_ai.request.model" => model
  }

  attributes["gen_ai.request.messages"] = Serializer.serialize(messages) if messages

  SpanBuilder.build(
    operation: :chat,
    description: "chat #{model}",
    attributes: attributes
  ) do |span|
    result = yield
    capture_token_usage(span, result)
    capture_response_text(span, result)
    result
  end
end

#with_handoff_span(from_stage:, to_stage:, system: nil) { ... } ⇒ Object

Track agent stage transitions or handoffs

Creates a gen_ai.handoff span that captures transitions between stages or handoffs between agents.

Examples:

with_handoff_span(from_stage: "greeting", to_stage: "qualification") do
  update_conversation_stage!
end

Parameters:

  • from_stage (String)

    source stage/agent

  • to_stage (String)

    destination stage/agent

  • system (String, nil) (defaults to: nil)

    override default LLM provider system name

Yields:

  • the transition logic

Returns:

  • (Object)

    the block result



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/sentry/agents/instrumentation.rb', line 153

def with_handoff_span(from_stage:, to_stage:, system: nil)
  return yield unless sentry_tracing_available?

  SpanBuilder.build(
    operation: :handoff,
    description: "handoff from #{from_stage} to #{to_stage}",
    attributes: {
      "gen_ai.operation.name" => "handoff",
      "gen_ai.system" => system_name(system),
      "gen_ai.handoff.from" => from_stage,
      "gen_ai.handoff.to" => to_stage
    }
  ) do |_span|
    yield
  end
end

#with_tool_span(tool_name:, tool_input: nil, system: nil) { ... } ⇒ Object

Wrap a tool/function execution

Creates a gen_ai.execute_tool span that captures tool execution. The tool output is automatically captured from the block result.

Examples:

with_tool_span(tool_name: "search", tool_input: { query: "flights" }) do
  search_service.search("flights")
end

Parameters:

  • tool_name (String)

    name of the tool being executed

  • tool_input (Hash, String, nil) (defaults to: nil)

    tool input parameters

  • system (String, nil) (defaults to: nil)

    override default LLM provider system name

Yields:

  • the tool execution

Returns:

  • (Object)

    the block result



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/sentry/agents/instrumentation.rb', line 115

def with_tool_span(tool_name:, tool_input: nil, system: nil)
  return yield unless sentry_tracing_available?

  attributes = {
    "gen_ai.operation.name" => "execute_tool",
    "gen_ai.system" => system_name(system),
    "gen_ai.tool.name" => tool_name
  }

  attributes["gen_ai.tool.input"] = Serializer.serialize(tool_input) if tool_input

  SpanBuilder.build(
    operation: :execute_tool,
    description: "execute_tool #{tool_name}",
    attributes: attributes
  ) do |span|
    result = yield
    capture_tool_output(span, result)
    result
  end
end