activeagents-telemetry-ruby_llm 0.1.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 +7 -0
- data/README.md +92 -0
- data/lib/activeagents/telemetry/ruby_llm/version.rb +9 -0
- data/lib/activeagents/telemetry/ruby_llm.rb +268 -0
- metadata +83 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 74f798054c3b023ac181d541e9ce36d907733511e4abfd1a43d1565cbff09c3e
|
|
4
|
+
data.tar.gz: c651f96ca0fafc447d5e49090da2f84907fb4a04d33256ad1b87fee1d0e6d0d7
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 066163ce9683532ee6f2f3220592ede5a85c8a58208bdac182e169c32ccbe5a8c00682e1ac79c936da353399d6aeb024d132253b329d6dcf6e7b2227a0421577
|
|
7
|
+
data.tar.gz: d1ee5d365abac91ad386ff734095b21aa1b77ef1b0722c4a9d37e2742ab91cc9c9b5898db709f82cda59e7fd1663e224c19fa1691d34c095e6987f4e3d6783b3
|
data/README.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# activeagents-telemetry-ruby_llm
|
|
2
|
+
|
|
3
|
+
Reports [RubyLLM](https://github.com/crmne/ruby_llm) chats to an
|
|
4
|
+
ActiveAgents-compatible trace endpoint — the hosted platform, or any
|
|
5
|
+
self-hosted ActiveAgent dashboard.
|
|
6
|
+
|
|
7
|
+
For apps built directly on RubyLLM. Apps that can adopt `ActiveAgent::Base`
|
|
8
|
+
should use the framework's `ruby_llm` provider instead, which reports telemetry
|
|
9
|
+
on its own.
|
|
10
|
+
|
|
11
|
+
**[Full guide → the wiki](https://github.com/activeagents/activeagents-telemetry/wiki/RubyLLM)**
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
gem "activeagents-telemetry-ruby_llm"
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Use
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
# config/initializers/telemetry.rb
|
|
23
|
+
RubyLLM.configure do |config|
|
|
24
|
+
config.instrumenter = ActiveSupport::Notifications # RubyLLM 1.x; 2.x wires this up in Rails
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
ActiveAgents::Telemetry.configure do |config|
|
|
28
|
+
config.api_key = ENV["ACTIVEAGENTS_API_KEY"]
|
|
29
|
+
config.service_name = "my-app"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
ActiveAgents::Telemetry::RubyLLM.subscribe!
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The key is a platform API key (Settings → API Keys) or an account's legacy
|
|
36
|
+
`telemetry_api_key`. Delivery is fire-and-forget on a background thread, and
|
|
37
|
+
failures are logged and swallowed — telemetry never raises into the app.
|
|
38
|
+
|
|
39
|
+
## What a turn looks like
|
|
40
|
+
|
|
41
|
+
One trace per conversation turn: a `root` span (`Agent.action`), one `llm`
|
|
42
|
+
span covering the whole provider loop with `llm.rounds` and token totals, and
|
|
43
|
+
a `tool` span per tool call with real timings.
|
|
44
|
+
|
|
45
|
+
RubyLLM emits a `chat.ruby_llm` event per provider round, and the two
|
|
46
|
+
generations arrange them differently — 1.x nests a tool round inside the
|
|
47
|
+
enclosing event, 2.x drives a flat `step until complete?` loop whose rounds
|
|
48
|
+
are siblings with tool calls between them. Rounds are accumulated and flushed
|
|
49
|
+
on the round that ends the turn, so both produce the same trace.
|
|
50
|
+
|
|
51
|
+
Tool arguments and results are never sent; error messages are truncated.
|
|
52
|
+
|
|
53
|
+
## Naming the traffic
|
|
54
|
+
|
|
55
|
+
RubyLLM carries no application identity on the payload — neither a
|
|
56
|
+
`RubyLLM::Agent` class nor an `acts_as_chat` record reaches the instrumenter
|
|
57
|
+
— so unattributed traffic reports as `RubyLLM::Chat`:
|
|
58
|
+
|
|
59
|
+
```ruby
|
|
60
|
+
# Per call site
|
|
61
|
+
ActiveAgents::Telemetry::RubyLLM.with_agent("SupportBot", action: "respond") { chat.ask(...) }
|
|
62
|
+
|
|
63
|
+
# Or from the initializer, derived from the event payload
|
|
64
|
+
ActiveAgents::Telemetry::RubyLLM.subscribe!(
|
|
65
|
+
agent_resolver: ->(payload) { { name: "SupportBot", action: payload[:tools].present? ? "respond" : "summarize" } }
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# Or name every RubyLLM::Agent subclass by its class
|
|
69
|
+
module AgentTelemetryAttribution
|
|
70
|
+
def ask(...) = ActiveAgents::Telemetry::RubyLLM.with_agent(self.class.name) { super }
|
|
71
|
+
end
|
|
72
|
+
RubyLLM::Agent.prepend(AgentTelemetryAttribution)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Scope
|
|
76
|
+
|
|
77
|
+
Chat completions and tool calls. RubyLLM's `embedding`, `image`,
|
|
78
|
+
`moderation`, `speech`, `transcription`, `request`, and `models.refresh`
|
|
79
|
+
events are not reported yet — they carry their own token counts and are a
|
|
80
|
+
natural extension of the same subscriber.
|
|
81
|
+
|
|
82
|
+
Concurrent tool execution runs tools off the instrumented thread and is not
|
|
83
|
+
captured; sequential execution (the default) is fully covered. A turn left
|
|
84
|
+
open by a halted tool call, or by an app driving 2.x's `step`/`run_tools` by
|
|
85
|
+
hand, is flushed when the next chat reports, after `MAX_TURN_SECONDS`, or on
|
|
86
|
+
an explicit `flush!`.
|
|
87
|
+
|
|
88
|
+
## Tests
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
bundle exec rake test
|
|
92
|
+
```
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support"
|
|
4
|
+
require "active_support/notifications"
|
|
5
|
+
|
|
6
|
+
require "activeagents/telemetry"
|
|
7
|
+
|
|
8
|
+
require_relative "ruby_llm/version"
|
|
9
|
+
|
|
10
|
+
module ActiveAgents
|
|
11
|
+
module Telemetry
|
|
12
|
+
# Reports RubyLLM chats to an ActiveAgents-compatible trace endpoint.
|
|
13
|
+
#
|
|
14
|
+
# Requires RubyLLM.config.instrumenter = ActiveSupport::Notifications
|
|
15
|
+
# (RubyLLM 1.x; 2.x wires this up under Rails).
|
|
16
|
+
#
|
|
17
|
+
# One trace per conversation turn: a root span, an llm span covering the
|
|
18
|
+
# whole provider loop, and a tool span per tool_call.ruby_llm event.
|
|
19
|
+
#
|
|
20
|
+
# RubyLLM emits a chat.ruby_llm event per provider round, and the two
|
|
21
|
+
# generations of the gem arrange those rounds differently: through 1.x a
|
|
22
|
+
# tool round recurses inside the enclosing event, while 2.x drives a flat
|
|
23
|
+
# `step until complete?` loop whose rounds are siblings with tool calls
|
|
24
|
+
# firing between them. Rounds are therefore accumulated and flushed on the
|
|
25
|
+
# round that ends the turn — the one that errors or answers without
|
|
26
|
+
# requesting tools — which yields the same trace under both arrangements.
|
|
27
|
+
# Tokens are summed per round from the assistant messages that round added,
|
|
28
|
+
# so a repeated event-level count is never double counted.
|
|
29
|
+
#
|
|
30
|
+
# Tool arguments and results are never sent; error messages are truncated.
|
|
31
|
+
module RubyLLM
|
|
32
|
+
AGENT_KEY = :activeagents_telemetry_ruby_llm_agent
|
|
33
|
+
STATE_KEY = :activeagents_telemetry_ruby_llm_state
|
|
34
|
+
TOOL_STARTED_AT_KEY = :_activeagents_telemetry_started_at
|
|
35
|
+
SDK_NAME = "activeagents-telemetry-ruby_llm"
|
|
36
|
+
# A turn that never reaches a final round (a halted tool call, or an app
|
|
37
|
+
# driving RubyLLM 2.x's `step` by hand) would otherwise accumulate forever.
|
|
38
|
+
MAX_TURN_SECONDS = 600
|
|
39
|
+
|
|
40
|
+
DEFAULT_AGENT = { name: "RubyLLM::Chat", action: "chat" }.freeze
|
|
41
|
+
|
|
42
|
+
State = Struct.new(:depth, :started_at, :tool_spans, :rounds, :tokens, :chat_key)
|
|
43
|
+
|
|
44
|
+
class << self
|
|
45
|
+
# Subscribes to RubyLLM's instrumentation.
|
|
46
|
+
#
|
|
47
|
+
# Destination settings fall back to ActiveAgents::Telemetry.configuration,
|
|
48
|
+
# so an app that already called `ActiveAgents::Telemetry.configure` can
|
|
49
|
+
# call this with no arguments at all.
|
|
50
|
+
#
|
|
51
|
+
# agent_resolver: optional callable receiving the chat event payload and
|
|
52
|
+
# returning { name:, action: }, so traffic can be attributed from an
|
|
53
|
+
# initializer alone; an enclosing with_agent block still wins. RubyLLM
|
|
54
|
+
# carries no application identity on the payload — neither RubyLLM::Agent
|
|
55
|
+
# nor an acts_as_chat record reaches the instrumenter — so unattributed
|
|
56
|
+
# traffic reports as RubyLLM::Chat.
|
|
57
|
+
def subscribe!(api_key: nil, endpoint: nil, service_name: nil, environment: nil,
|
|
58
|
+
agent_resolver: nil, async: nil, configuration: nil)
|
|
59
|
+
@configuration = configuration || Telemetry.configuration.dup
|
|
60
|
+
@configuration.api_key = api_key unless api_key.nil?
|
|
61
|
+
@configuration.endpoint = endpoint unless endpoint.nil?
|
|
62
|
+
@configuration.service_name = service_name unless service_name.nil?
|
|
63
|
+
@configuration.environment = environment unless environment.nil?
|
|
64
|
+
@configuration.async = async unless async.nil?
|
|
65
|
+
|
|
66
|
+
@agent_resolver = agent_resolver
|
|
67
|
+
@reporter = Reporter.new(@configuration, sdk_name: SDK_NAME, sdk_version: VERSION)
|
|
68
|
+
|
|
69
|
+
@subscriptions ||= [
|
|
70
|
+
ActiveSupport::Notifications.subscribe("chat.ruby_llm", ChatSubscriber.new),
|
|
71
|
+
ActiveSupport::Notifications.subscribe("tool_call.ruby_llm", ToolCallSubscriber.new)
|
|
72
|
+
]
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def unsubscribe!
|
|
76
|
+
Array(@subscriptions).each { |subscription| ActiveSupport::Notifications.unsubscribe(subscription) }
|
|
77
|
+
@subscriptions = nil
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def configuration
|
|
81
|
+
@configuration ||= Telemetry.configuration
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def reporter
|
|
85
|
+
@reporter ||= Reporter.new(configuration, sdk_name: SDK_NAME, sdk_version: VERSION)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
attr_writer :reporter
|
|
89
|
+
|
|
90
|
+
# Attributes traces inside the block to a named agent/action.
|
|
91
|
+
def with_agent(name, action: "chat")
|
|
92
|
+
previous = Thread.current[AGENT_KEY]
|
|
93
|
+
Thread.current[AGENT_KEY] = { name: name, action: action }
|
|
94
|
+
yield
|
|
95
|
+
ensure
|
|
96
|
+
Thread.current[AGENT_KEY] = previous
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def state
|
|
100
|
+
Thread.current[STATE_KEY] ||= State.new(0, nil, [], 0, Span::ZERO_TOKENS.dup, nil)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def clear_state
|
|
104
|
+
Thread.current[STATE_KEY] = nil
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Reports whatever the current turn has accumulated. Apps that drive
|
|
108
|
+
# RubyLLM 2.x's `step`/`run_tools` themselves can call this to close a
|
|
109
|
+
# turn that ends while tool calls are still pending.
|
|
110
|
+
def flush!(payload = {})
|
|
111
|
+
turn = Thread.current[STATE_KEY]
|
|
112
|
+
return if turn.nil? || turn.rounds.zero?
|
|
113
|
+
|
|
114
|
+
clear_state
|
|
115
|
+
report_turn(payload, turn)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def begin_round(payload)
|
|
119
|
+
turn = state
|
|
120
|
+
if turn.depth.zero?
|
|
121
|
+
chat_key = payload[:chat].object_id
|
|
122
|
+
flush! if turn.rounds.positive? && (turn.chat_key != chat_key || turn_expired?(turn))
|
|
123
|
+
turn = state
|
|
124
|
+
turn.chat_key = chat_key
|
|
125
|
+
turn.started_at ||= Time.now
|
|
126
|
+
end
|
|
127
|
+
turn.depth += 1
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def finish_round(payload)
|
|
131
|
+
turn = state
|
|
132
|
+
turn.depth -= 1
|
|
133
|
+
return unless turn.depth.zero?
|
|
134
|
+
|
|
135
|
+
turn.rounds += 1
|
|
136
|
+
accumulate_tokens(turn, payload)
|
|
137
|
+
flush!(payload) if payload[:exception_object] || !payload[:tool_call]
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def build_tool_span(payload, started_at, finished_at)
|
|
141
|
+
error = payload[:exception_object]
|
|
142
|
+
span = Span.new(
|
|
143
|
+
"tool.#{payload[:tool_name]}",
|
|
144
|
+
type: "tool",
|
|
145
|
+
start_time: started_at,
|
|
146
|
+
attributes: { "tool.name" => payload[:tool_name].to_s, "tool.call_id" => payload[:tool_call_id].to_s }
|
|
147
|
+
)
|
|
148
|
+
span.record_error(error, message_limit: configuration.error_message_limit) if error
|
|
149
|
+
span.finish(at: finished_at)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
private
|
|
153
|
+
|
|
154
|
+
def report_turn(payload, turn)
|
|
155
|
+
agent = Thread.current[AGENT_KEY] || resolve_agent(payload) || DEFAULT_AGENT
|
|
156
|
+
started_at = turn.started_at || Time.now
|
|
157
|
+
finished_at = Time.now
|
|
158
|
+
error = payload[:exception_object]
|
|
159
|
+
|
|
160
|
+
trace = Trace.new(
|
|
161
|
+
service_name: configuration.resolved_service_name,
|
|
162
|
+
environment: configuration.resolved_environment,
|
|
163
|
+
resource_attributes: configuration.resource_attributes
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
root = trace.span(
|
|
167
|
+
"#{agent[:name]}.#{agent[:action]}", type: "root", start_time: started_at,
|
|
168
|
+
attributes: {
|
|
169
|
+
"agent.class" => agent[:name],
|
|
170
|
+
"agent.action" => agent[:action],
|
|
171
|
+
"agent.provider" => payload[:provider].to_s,
|
|
172
|
+
"agent.model" => payload[:model].to_s
|
|
173
|
+
}
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
llm = trace.span(
|
|
177
|
+
"llm.generate", type: "llm", parent: root, start_time: started_at,
|
|
178
|
+
attributes: {
|
|
179
|
+
"llm.provider" => payload[:provider].to_s,
|
|
180
|
+
"llm.model" => payload[:model].to_s,
|
|
181
|
+
"llm.rounds" => turn.rounds,
|
|
182
|
+
"llm.streaming" => payload[:streaming] || false
|
|
183
|
+
}
|
|
184
|
+
)
|
|
185
|
+
llm.add_tokens(turn.tokens)
|
|
186
|
+
|
|
187
|
+
[ root, llm ].each do |span|
|
|
188
|
+
span.record_error(error, message_limit: configuration.error_message_limit) if error
|
|
189
|
+
span.finish(at: finished_at)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
turn.tool_spans.each do |tool_span|
|
|
193
|
+
tool_span.parent_span_id = llm.span_id
|
|
194
|
+
trace.add_span(tool_span)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
reporter.report(trace)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def turn_expired?(turn)
|
|
201
|
+
turn.started_at.nil? || (Time.now - turn.started_at) > MAX_TURN_SECONDS
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def accumulate_tokens(turn, payload)
|
|
205
|
+
turn.tokens = turn.tokens.merge(token_totals(payload)) { |_key, carried, added| carried + added }
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def resolve_agent(payload)
|
|
209
|
+
agent = @agent_resolver&.call(payload)
|
|
210
|
+
return unless agent.is_a?(Hash) && !agent[:name].to_s.empty?
|
|
211
|
+
|
|
212
|
+
{ name: agent[:name], action: agent[:action] || "chat" }
|
|
213
|
+
rescue StandardError => e
|
|
214
|
+
warn "[#{SDK_NAME}] agent_resolver failed: #{e.class}: #{e.message}"
|
|
215
|
+
nil
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def token_totals(payload)
|
|
219
|
+
initial_count = Array(payload[:input_messages]).size
|
|
220
|
+
new_messages = Array(payload[:messages_after])[initial_count..] || []
|
|
221
|
+
assistant_messages = new_messages.select { |message| message.respond_to?(:role) && message.role.to_s == "assistant" }
|
|
222
|
+
|
|
223
|
+
tokens = {
|
|
224
|
+
"input" => sum_tokens(assistant_messages, :input_tokens),
|
|
225
|
+
"output" => sum_tokens(assistant_messages, :output_tokens),
|
|
226
|
+
"thinking" => sum_tokens(assistant_messages, :thinking_tokens)
|
|
227
|
+
}
|
|
228
|
+
tokens["total"] = tokens.values.sum
|
|
229
|
+
tokens
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def sum_tokens(messages, method_name)
|
|
233
|
+
messages.sum { |message| message.respond_to?(method_name) ? message.public_send(method_name).to_i : 0 }
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Evented ActiveSupport::Notifications subscriber; finish fires even when
|
|
238
|
+
# the instrumented block raises, with the exception on the payload.
|
|
239
|
+
class ChatSubscriber
|
|
240
|
+
def start(_name, _id, payload)
|
|
241
|
+
RubyLLM.begin_round(payload)
|
|
242
|
+
rescue StandardError => e
|
|
243
|
+
warn "[#{SDK_NAME}] #{e.class}: #{e.message}"
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def finish(_name, _id, payload)
|
|
247
|
+
RubyLLM.finish_round(payload)
|
|
248
|
+
rescue StandardError => e
|
|
249
|
+
warn "[#{SDK_NAME}] #{e.class}: #{e.message}"
|
|
250
|
+
RubyLLM.clear_state
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
class ToolCallSubscriber
|
|
255
|
+
def start(_name, _id, payload)
|
|
256
|
+
payload[TOOL_STARTED_AT_KEY] = Time.now
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def finish(_name, _id, payload)
|
|
260
|
+
started_at = payload.delete(TOOL_STARTED_AT_KEY) || Time.now
|
|
261
|
+
RubyLLM.state.tool_spans << RubyLLM.build_tool_span(payload, started_at, Time.now)
|
|
262
|
+
rescue StandardError => e
|
|
263
|
+
warn "[#{SDK_NAME}] #{e.class}: #{e.message}"
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: activeagents-telemetry-ruby_llm
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- ActiveAgents
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-08-10 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: activeagents-telemetry
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0.1'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0.1'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: activesupport
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '7.0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '7.0'
|
|
41
|
+
description: |
|
|
42
|
+
Subscribes to RubyLLM's instrumentation events and reports each chat turn
|
|
43
|
+
as a trace — a root span, an llm span, and a span per tool call — to the
|
|
44
|
+
ActiveAgents platform or any self-hosted ActiveAgent dashboard. Works with
|
|
45
|
+
apps built directly on RubyLLM: no ActiveAgent framework dependency.
|
|
46
|
+
email:
|
|
47
|
+
- hello@activeagents.ai
|
|
48
|
+
executables: []
|
|
49
|
+
extensions: []
|
|
50
|
+
extra_rdoc_files: []
|
|
51
|
+
files:
|
|
52
|
+
- README.md
|
|
53
|
+
- lib/activeagents/telemetry/ruby_llm.rb
|
|
54
|
+
- lib/activeagents/telemetry/ruby_llm/version.rb
|
|
55
|
+
homepage: https://github.com/activeagents/activeagents-telemetry
|
|
56
|
+
licenses:
|
|
57
|
+
- MIT
|
|
58
|
+
metadata:
|
|
59
|
+
homepage_uri: https://github.com/activeagents/activeagents-telemetry
|
|
60
|
+
source_code_uri: https://github.com/activeagents/activeagents-telemetry/tree/main/adapters/ruby_llm
|
|
61
|
+
documentation_uri: https://github.com/activeagents/activeagents-telemetry/wiki/RubyLLM
|
|
62
|
+
changelog_uri: https://github.com/activeagents/activeagents-telemetry/blob/main/CHANGELOG.md
|
|
63
|
+
rubygems_mfa_required: 'true'
|
|
64
|
+
post_install_message:
|
|
65
|
+
rdoc_options: []
|
|
66
|
+
require_paths:
|
|
67
|
+
- lib
|
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
69
|
+
requirements:
|
|
70
|
+
- - ">="
|
|
71
|
+
- !ruby/object:Gem::Version
|
|
72
|
+
version: 3.2.0
|
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
74
|
+
requirements:
|
|
75
|
+
- - ">="
|
|
76
|
+
- !ruby/object:Gem::Version
|
|
77
|
+
version: '0'
|
|
78
|
+
requirements: []
|
|
79
|
+
rubygems_version: 3.5.22
|
|
80
|
+
signing_key:
|
|
81
|
+
specification_version: 4
|
|
82
|
+
summary: Report RubyLLM chats to an ActiveAgents trace endpoint
|
|
83
|
+
test_files: []
|