Module: Brute::Contrib::Otel
- Defined in:
- lib/brute/contrib/otel.rb
Overview
OpenTelemetry for a turn, as hooks rather than middleware.
Brute::Contrib::Otel.subscribe(agent)
agent.start("what changed?")
These are pure observers: they read the turn and write spans, and never touch what the agent does. That is why they are subscribers and not layers — a middleware earns its place in the stack by being able to alter or skip what is below it, and telemetry never should.
Every event it needs already exists:
turn_start / turn_end the span itself
llm_end token usage, from env[:metadata][:last_llm_usage]
tool_start / tool_end a span event per tool call and result
The tracer is injectable; without one it asks OpenTelemetry, and when
the SDK is not loaded subscribe does nothing at all.
Note the span is opened and finished by hand rather than through
tracer.in_span, which wants a block around the work. OpenTelemetry's
current context is fiber-local, so this does not attach the span as
current: under Async a turn's LLM call and its tools may run in other
fibers, and an attached-but-never-detached context leaks across them.
Constant Summary collapse
- SPAN_NAME =
"brute.turn"
Class Method Summary collapse
Class Method Details
.default_tracer ⇒ Object
65 66 67 68 69 70 71 |
# File 'lib/brute/contrib/otel.rb', line 65 def default_tracer if defined?(::OpenTelemetry) ::OpenTelemetry.tracer_provider.tracer("brute", Brute::VERSION) else nil end end |
.subscribe(agent, tracer: default_tracer) ⇒ Object
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/brute/contrib/otel.rb', line 37 def subscribe(agent, tracer: default_tracer) if tracer.nil? agent else agent .on(Brute::Hooks::TURN_START_EVENT) { |env| start(env, tracer) } .on(Brute::Hooks::TURN_END_EVENT) { |env| finish(env) } .on(Brute::Hooks::LLM_END_EVENT) { |env| record_usage(env) } .on(Brute::Hooks::TOOL_START_EVENT) { |env, call| tool_called(env, call) } .on(Brute::Hooks::TOOL_END_EVENT) { |env, call| tool_returned(env, call) } .on(Brute::Hooks::LLM_FAILURE_EVENT) { |env| failed(env) } .on(Brute::Hooks::MIDDLEWARE_DURATION_EVENT) { |env, started, finished, layer| layer_finished( env, started, finished, layer, ) } .on(Brute::Hooks::TURN_DURATION_EVENT) { |env, started, finished| timed(env, "turn", finished - started) } .on(Brute::Hooks::LLM_DURATION_EVENT) { |env, started, finished| timed(env, "llm", finished - started) } .on(Brute::Hooks::TOOL_DURATION_EVENT) { |env, started, finished, call| timed( env, "tool", finished - started, "tool.name" => call[:name].to_s, ) } end end |