activeagents-telemetry-ruby_llm 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +41 -0
- data/lib/activeagents/telemetry/ruby_llm/version.rb +1 -1
- data/lib/activeagents/telemetry/ruby_llm.rb +36 -10
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5abf7d85a5deec9d008170f9c9a3e50d619024b7bad8c1f01eea3eda3d3befc6
|
|
4
|
+
data.tar.gz: ea10cb9f3cb45700fe8ad0642c72eb3c6e01576c1e4353d274998ead5f3f7243
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 67e5b7d06a3de8510dead200b6ec9a4da44172d24b46c3ea02646cfd4fad1fc3c5acb960798e17a5957f61d8dcd4bd7efc392ae08f8e46ef19b084014e357073
|
|
7
|
+
data.tar.gz: 16dde44b30870de36415b0958b85eabff49caf639238baf140107e6b2a986effe21bf0eea529c78b5810d90816098cac8c35f09800304c3c5cddd3c7ab6b3c22
|
data/README.md
CHANGED
|
@@ -92,3 +92,44 @@ an explicit `flush!`.
|
|
|
92
92
|
```bash
|
|
93
93
|
bundle exec rake test
|
|
94
94
|
```
|
|
95
|
+
|
|
96
|
+
## Correlating evaluation traces
|
|
97
|
+
|
|
98
|
+
Use a separate identity for judge calls and attach stable evaluation identifiers
|
|
99
|
+
without changing the application's default agent resolver:
|
|
100
|
+
|
|
101
|
+
```ruby
|
|
102
|
+
trace_ids = []
|
|
103
|
+
ActiveAgents::Telemetry::RubyLLM.with_agent(
|
|
104
|
+
"EvaluationJudge", action: "score",
|
|
105
|
+
attributes: { "eval.run_id" => run_id, "eval.result_id" => result_id },
|
|
106
|
+
on_trace: ->(trace) { trace_ids << trace.trace_id },
|
|
107
|
+
synchronous: true
|
|
108
|
+
) do
|
|
109
|
+
judge_chat.ask(prompt)
|
|
110
|
+
end
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
The callback receives each trace the reporter accepted for delivery, so an
|
|
114
|
+
evaluation result can retain the trace ID of that delivery attempt. Acceptance is
|
|
115
|
+
not ingestion: the trace passed the enabled, configured and sampling checks, but
|
|
116
|
+
a delivery that then fails is logged by the reporter rather than announced here,
|
|
117
|
+
and an asynchronous delivery can outlive a process that exits right away. A trace
|
|
118
|
+
dropped by `sample_rate` or by a disabled configuration is never announced.
|
|
119
|
+
`synchronous: true` delivers in the calling thread for this scope only, through
|
|
120
|
+
the same sampling and configuration checks as ordinary delivery; it does not
|
|
121
|
+
mutate the shared async configuration. Normal reporter error logging still
|
|
122
|
+
applies: synchronous delivery does not turn telemetry failures into application
|
|
123
|
+
exceptions. A turn keeps the scope it started under, so a turn left open by a
|
|
124
|
+
pending tool call and closed later by `flush!` still reports with this agent,
|
|
125
|
+
attributes and callback, and a turn that started outside any scope never adopts
|
|
126
|
+
one. Context
|
|
127
|
+
is restored after the block, including when it raises, and nested scopes use
|
|
128
|
+
their own attributes. A callback error is logged by exception class without
|
|
129
|
+
dropping the trace. Attribute redaction and content-capture settings continue to
|
|
130
|
+
apply.
|
|
131
|
+
|
|
132
|
+
Report publication and trace ingestion are separate operations. Persist each run
|
|
133
|
+
and result ID in the evaluation report and attach the same IDs to its response and
|
|
134
|
+
judge trace attributes. The application chooses whether to publish full report
|
|
135
|
+
content; this API does not enable body capture or upload reports automatically.
|
|
@@ -43,7 +43,7 @@ module ActiveAgents
|
|
|
43
43
|
|
|
44
44
|
DEFAULT_AGENT = { name: "RubyLLM::Chat", action: "chat" }.freeze
|
|
45
45
|
|
|
46
|
-
State = Struct.new(:depth, :started_at, :tool_spans, :rounds, :tokens, :chat_key)
|
|
46
|
+
State = Struct.new(:depth, :started_at, :tool_spans, :rounds, :tokens, :chat_key, :agent)
|
|
47
47
|
|
|
48
48
|
class << self
|
|
49
49
|
# Subscribes to RubyLLM's instrumentation.
|
|
@@ -91,17 +91,30 @@ module ActiveAgents
|
|
|
91
91
|
|
|
92
92
|
attr_writer :reporter
|
|
93
93
|
|
|
94
|
-
# Attributes traces inside the block to a named agent/action.
|
|
95
|
-
|
|
94
|
+
# Attributes traces inside the block to a named agent/action. Correlation
|
|
95
|
+
# attributes and the callback apply to this scope only. Short-lived
|
|
96
|
+
# evaluation commands can deliver synchronously without changing the
|
|
97
|
+
# application's shared reporter configuration.
|
|
98
|
+
#
|
|
99
|
+
# A turn keeps the scope it started under, so a turn left open by a
|
|
100
|
+
# pending tool call and closed later by `flush!` still reports as this
|
|
101
|
+
# agent, and a turn that started outside any scope never adopts one.
|
|
102
|
+
# `on_trace` runs only for a trace the reporter accepted, which means
|
|
103
|
+
# it passed the enabled, configured and sampling checks: a delivery
|
|
104
|
+
# that then fails is logged by the reporter, not announced here.
|
|
105
|
+
def with_agent(name, action: "chat", attributes: {}, on_trace: nil, synchronous: false)
|
|
96
106
|
previous = Thread.current[AGENT_KEY]
|
|
97
|
-
Thread.current[AGENT_KEY] = {
|
|
107
|
+
Thread.current[AGENT_KEY] = {
|
|
108
|
+
name: name, action: action, attributes: attributes.to_h.transform_keys(&:to_s),
|
|
109
|
+
on_trace: on_trace, synchronous: synchronous
|
|
110
|
+
}
|
|
98
111
|
yield
|
|
99
112
|
ensure
|
|
100
113
|
Thread.current[AGENT_KEY] = previous
|
|
101
114
|
end
|
|
102
115
|
|
|
103
116
|
def state
|
|
104
|
-
Thread.current[STATE_KEY] ||= State.new(0, nil, [], 0, Span::ZERO_TOKENS.dup, nil)
|
|
117
|
+
Thread.current[STATE_KEY] ||= State.new(0, nil, [], 0, Span::ZERO_TOKENS.dup, nil, nil)
|
|
105
118
|
end
|
|
106
119
|
|
|
107
120
|
def clear_state
|
|
@@ -126,7 +139,13 @@ module ActiveAgents
|
|
|
126
139
|
flush! if turn.rounds.positive? && (turn.chat_key != chat_key || turn_expired?(turn))
|
|
127
140
|
turn = state
|
|
128
141
|
turn.chat_key = chat_key
|
|
129
|
-
turn.started_at
|
|
142
|
+
if turn.started_at.nil?
|
|
143
|
+
turn.started_at = Time.now
|
|
144
|
+
# Captured once, on the turn's first round, whether or not a scope
|
|
145
|
+
# is active: a turn that started unscoped stays unscoped even when
|
|
146
|
+
# a later scope's chat is what flushes it.
|
|
147
|
+
turn.agent = Thread.current[AGENT_KEY]
|
|
148
|
+
end
|
|
130
149
|
end
|
|
131
150
|
turn.depth += 1
|
|
132
151
|
end
|
|
@@ -161,7 +180,7 @@ module ActiveAgents
|
|
|
161
180
|
private
|
|
162
181
|
|
|
163
182
|
def report_turn(payload, turn)
|
|
164
|
-
agent =
|
|
183
|
+
agent = turn.agent || resolve_agent(payload) || DEFAULT_AGENT
|
|
165
184
|
started_at = turn.started_at || Time.now
|
|
166
185
|
finished_at = Time.now
|
|
167
186
|
error = payload[:exception_object]
|
|
@@ -172,12 +191,12 @@ module ActiveAgents
|
|
|
172
191
|
resource_attributes: configuration.resource_attributes
|
|
173
192
|
)
|
|
174
193
|
|
|
175
|
-
root_attributes = {
|
|
194
|
+
root_attributes = (agent[:attributes] || {}).merge(
|
|
176
195
|
"agent.class" => agent[:name],
|
|
177
196
|
"agent.action" => agent[:action],
|
|
178
197
|
"agent.provider" => payload[:provider].to_s,
|
|
179
198
|
"agent.model" => payload[:model].to_s
|
|
180
|
-
|
|
199
|
+
)
|
|
181
200
|
root_attributes.merge!(conversation_attributes(payload)) if configuration.capture_bodies?
|
|
182
201
|
|
|
183
202
|
root = trace.span(
|
|
@@ -206,7 +225,14 @@ module ActiveAgents
|
|
|
206
225
|
trace.add_span(tool_span)
|
|
207
226
|
end
|
|
208
227
|
|
|
209
|
-
reporter.report(trace)
|
|
228
|
+
accepted = agent[:synchronous] ? reporter.report(trace, sync: true) : reporter.report(trace)
|
|
229
|
+
notify_trace(agent[:on_trace], trace) if accepted
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def notify_trace(callback, trace)
|
|
233
|
+
callback&.call(trace)
|
|
234
|
+
rescue StandardError => e
|
|
235
|
+
warn "[#{SDK_NAME}] on_trace failed: #{e.class}"
|
|
210
236
|
end
|
|
211
237
|
|
|
212
238
|
def turn_expired?(turn)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activeagents-telemetry-ruby_llm
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ActiveAgents
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-09-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activeagents-telemetry
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '0.
|
|
19
|
+
version: '0.3'
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '0.
|
|
26
|
+
version: '0.3'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: activesupport
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|