Class: Agents::Instrumentation::TracingCallbacks
- Inherits:
-
Object
- Object
- Agents::Instrumentation::TracingCallbacks
show all
- Includes:
- Constants
- Defined in:
- lib/agents/instrumentation/tracing_callbacks.rb
Overview
Produces OTel spans for agent execution, compatible with Langfuse.
Span hierarchy:
root (<trace_name>)
├── agent.<name> ← container per agent (no gen_ai.request.model)
│ ├── .generation ← GENERATION with model + tokens
│ └── .tool.<name> ← TOOL observation
└── .handoff ← point event on root
Only GENERATION spans carry gen_ai.request.model to avoid Langfuse double-counting costs.
Tracing state lives in context, unique per run (thread-safe).
Constant Summary
Constants included
from Constants
Constants::ATTR_GEN_AI_PROVIDER, Constants::ATTR_GEN_AI_REQUEST_MODEL, Constants::ATTR_GEN_AI_REQUEST_TEMPERATURE, Constants::ATTR_GEN_AI_USAGE_INPUT, Constants::ATTR_GEN_AI_USAGE_OUTPUT, Constants::ATTR_LANGFUSE_OBS_INPUT, Constants::ATTR_LANGFUSE_OBS_METADATA_PREFIX, Constants::ATTR_LANGFUSE_OBS_OUTPUT, Constants::ATTR_LANGFUSE_OBS_TYPE, Constants::ATTR_LANGFUSE_PREFIX, Constants::ATTR_LANGFUSE_SESSION_ID, Constants::ATTR_LANGFUSE_TRACE_INPUT, Constants::ATTR_LANGFUSE_TRACE_METADATA_PREFIX, Constants::ATTR_LANGFUSE_TRACE_OUTPUT, Constants::ATTR_LANGFUSE_TRACE_TAGS, Constants::ATTR_LANGFUSE_USER_ID, Constants::EVENT_HANDOFF, Constants::SPAN_LLM_CALL, Constants::SPAN_RUN, Constants::SPAN_TOOL
Instance Method Summary
collapse
-
#initialize(tracer:, trace_name: SPAN_RUN, span_attributes: {}, attribute_provider: nil) ⇒ TracingCallbacks
constructor
A new instance of TracingCallbacks.
-
#on_agent_complete(_agent_name, _result, _error, context_wrapper) ⇒ Object
-
#on_agent_handoff(from_agent, to_agent, reason, context_wrapper) ⇒ Object
-
#on_agent_thinking(agent_name, input, context_wrapper) ⇒ Object
-
#on_chat_created(chat, agent_name, model, context_wrapper, temperature = nil) ⇒ Object
-
#on_llm_call_complete(_agent_name, _model, _response, _context_wrapper) ⇒ Object
No-op: LLM spans are handled by on_end_message hook (see on_chat_created).
-
#on_run_complete(_agent_name, result, context_wrapper) ⇒ Object
-
#on_run_start(agent_name, input, context_wrapper) ⇒ Object
-
#on_tool_complete(_tool_name, result, context_wrapper) ⇒ Object
-
#on_tool_start(tool_name, args, context_wrapper) ⇒ Object
Constructor Details
#initialize(tracer:, trace_name: SPAN_RUN, span_attributes: {}, attribute_provider: nil) ⇒ TracingCallbacks
Returns a new instance of TracingCallbacks.
30
31
32
33
34
35
36
37
38
39
|
# File 'lib/agents/instrumentation/tracing_callbacks.rb', line 30
def initialize(tracer:, trace_name: SPAN_RUN, span_attributes: {}, attribute_provider: nil)
@tracer = tracer
@trace_name = trace_name
@llm_span_name = "#{trace_name}.generation"
@tool_span_name = "#{trace_name}.tool.%s"
@agent_span_name = "#{trace_name}.agent.%s"
@handoff_event_name = "#{trace_name}.handoff"
@span_attributes = span_attributes
@attribute_provider = attribute_provider
end
|
Instance Method Details
#on_agent_complete(_agent_name, _result, _error, context_wrapper) ⇒ Object
73
74
75
76
77
78
|
# File 'lib/agents/instrumentation/tracing_callbacks.rb', line 73
def on_agent_complete(_agent_name, _result, _error, context_wrapper)
tracing = tracing_state(context_wrapper)
return unless tracing
finish_agent_span(tracing)
end
|
#on_agent_handoff(from_agent, to_agent, reason, context_wrapper) ⇒ Object
124
125
126
127
128
129
130
131
132
133
134
135
136
|
# File 'lib/agents/instrumentation/tracing_callbacks.rb', line 124
def on_agent_handoff(from_agent, to_agent, reason, context_wrapper)
tracing = tracing_state(context_wrapper)
return unless tracing
tracing[:root_span]&.add_event(
@handoff_event_name,
attributes: {
"handoff.from" => from_agent,
"handoff.to" => to_agent,
"handoff.reason" => reason.to_s
}
)
end
|
#on_agent_thinking(agent_name, input, context_wrapper) ⇒ Object
58
59
60
61
62
63
64
65
66
67
|
# File 'lib/agents/instrumentation/tracing_callbacks.rb', line 58
def on_agent_thinking(agent_name, input, context_wrapper)
tracing = tracing_state(context_wrapper)
return unless tracing
tracing[:pending_llm_input] = serialize_output(input)
return if tracing[:current_agent_name] == agent_name
start_agent_span(tracing, agent_name)
end
|
#on_chat_created(chat, agent_name, model, context_wrapper, temperature = nil) ⇒ Object
80
81
82
83
84
85
86
87
88
89
|
# File 'lib/agents/instrumentation/tracing_callbacks.rb', line 80
def on_chat_created(chat, agent_name, model, context_wrapper, temperature = nil)
tracing = tracing_state(context_wrapper)
return unless tracing
request_attributes = { model: model, temperature: temperature }
chat.on_end_message do |message|
handle_end_message(chat, agent_name, request_attributes, message, context_wrapper)
end
end
|
#on_llm_call_complete(_agent_name, _model, _response, _context_wrapper) ⇒ Object
No-op: LLM spans are handled by on_end_message hook (see on_chat_created).
Kept because the callback interface requires it.
71
|
# File 'lib/agents/instrumentation/tracing_callbacks.rb', line 71
def on_llm_call_complete(_agent_name, _model, _response, _context_wrapper); end
|
#on_run_complete(_agent_name, result, context_wrapper) ⇒ Object
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|
# File 'lib/agents/instrumentation/tracing_callbacks.rb', line 138
def on_run_complete(_agent_name, result, context_wrapper)
tracing = tracing_state(context_wrapper)
return unless tracing
finish_dangling_spans(tracing)
root_span = tracing[:root_span]
return unless root_span
set_run_output_attributes(root_span, result)
set_run_error_status(root_span, result)
root_span.finish
cleanup_tracing_state(context_wrapper)
end
|
#on_run_start(agent_name, input, context_wrapper) ⇒ Object
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
# File 'lib/agents/instrumentation/tracing_callbacks.rb', line 41
def on_run_start(agent_name, input, context_wrapper)
attributes = build_root_attributes(agent_name, input, context_wrapper)
child_attributes = build_child_langfuse_attributes(attributes)
root_span = @tracer.start_span(@trace_name, attributes: attributes)
root_context = OpenTelemetry::Trace.context_with_span(root_span)
store_tracing_state(context_wrapper,
root_span: root_span,
root_context: root_context,
child_langfuse_attributes: child_attributes,
current_tool_span: nil,
current_agent_name: nil,
current_agent_span: nil,
current_agent_context: nil)
end
|
112
113
114
115
116
117
118
119
120
121
122
|
# File 'lib/agents/instrumentation/tracing_callbacks.rb', line 112
def on_tool_complete(_tool_name, result, context_wrapper)
tracing = tracing_state(context_wrapper)
return unless tracing
tool_span = tracing[:current_tool_span]
return unless tool_span
tool_span.set_attribute(ATTR_LANGFUSE_OBS_OUTPUT, serialize_output(result))
tool_span.finish
tracing[:current_tool_span] = nil
end
|
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
# File 'lib/agents/instrumentation/tracing_callbacks.rb', line 91
def on_tool_start(tool_name, args, context_wrapper)
tracing = tracing_state(context_wrapper)
return unless tracing
span_name = format(@tool_span_name, tool_name)
attributes = {
ATTR_LANGFUSE_OBS_TYPE => "tool",
ATTR_LANGFUSE_OBS_INPUT => serialize_output(args)
}
attributes.merge!(tracing[:child_langfuse_attributes])
parent = handoff_tool?(tool_name) ? tracing[:root_context] : parent_context(tracing)
tool_span = @tracer.start_span(
span_name,
with_parent: parent,
attributes: attributes
)
tracing[:current_tool_span] = tool_span
end
|