activeagent 1.1.0 → 1.2.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/CHANGELOG.md +61 -0
- data/README.md +24 -14
- data/lib/active_agent/telemetry/configuration.rb +13 -9
- data/lib/active_agent/telemetry/instrumentation.rb +4 -0
- data/lib/active_agent/telemetry/tool_origin.rb +90 -0
- data/lib/active_agent/telemetry.rb +1 -0
- data/lib/active_agent/version.rb +1 -1
- data/lib/active_agent.rb +0 -5
- metadata +22 -32
- data/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/traces_controller.rb +0 -138
- data/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/application_controller.rb +0 -64
- data/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/dashboard_controller.rb +0 -129
- data/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/traces_controller.rb +0 -123
- data/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/agent_execution_job.rb +0 -56
- data/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/application_job.rb +0 -14
- data/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/sandbox_cleanup_job.rb +0 -49
- data/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/sandbox_provision_job.rb +0 -65
- data/lib/active_agent/dashboard/app/jobs/active_agent/process_telemetry_traces_job.rb +0 -86
- data/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent.rb +0 -256
- data/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_run.rb +0 -113
- data/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_template.rb +0 -208
- data/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_version.rb +0 -60
- data/lib/active_agent/dashboard/app/models/active_agent/dashboard/application_record.rb +0 -46
- data/lib/active_agent/dashboard/app/models/active_agent/dashboard/recording_action.rb +0 -125
- data/lib/active_agent/dashboard/app/models/active_agent/dashboard/recording_snapshot.rb +0 -83
- data/lib/active_agent/dashboard/app/models/active_agent/dashboard/sandbox_run.rb +0 -52
- data/lib/active_agent/dashboard/app/models/active_agent/dashboard/sandbox_session.rb +0 -169
- data/lib/active_agent/dashboard/app/models/active_agent/dashboard/session_recording.rb +0 -193
- data/lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb +0 -214
- data/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/_trace_detail.html.erb +0 -117
- data/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/index.html.erb +0 -135
- data/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/metrics.html.erb +0 -145
- data/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/show.html.erb +0 -36
- data/lib/active_agent/dashboard/app/views/layouts/active_agent/dashboard/application.html.erb +0 -94
- data/lib/active_agent/dashboard/config/routes.rb +0 -19
- data/lib/active_agent/dashboard/engine.rb +0 -43
- data/lib/active_agent/dashboard.rb +0 -161
- data/lib/generators/active_agent/dashboard/install_generator.rb +0 -92
- data/lib/generators/active_agent/dashboard/templates/active_agent_dashboard.rb.erb +0 -67
- data/lib/generators/active_agent/dashboard/templates/create_active_agent_telemetry_traces.rb.erb +0 -46
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 867cf01603bb86b99c7abdbe6cc9e731264dc60bfdf3e89ca19a4d25fca5ea3d
|
|
4
|
+
data.tar.gz: c8b86f8988ff27931f4a82f8aa71aa04168bceae00f8f9cab6b98507dd3d2b94
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8c81f0537fddda60bbe3049cdb36b0478130d969ba0d35af408f3a5c527e2448d8e733638cd94b6c72c8f4b8d1ec13da32877baae3101e0373cc7d173253d06c
|
|
7
|
+
data.tar.gz: 6af0f6a1879a9e291581a51beb5b6b96ac2ec641a9ebc007cb2345c695e1f746741fbb667bd761f889591d15c7ffeb917e7ecb7c5d1151f4d6687af3f87a3930
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,67 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.2.0] - Unreleased
|
|
9
|
+
|
|
10
|
+
### ⚠️ The dashboard has moved to its own gem
|
|
11
|
+
|
|
12
|
+
The dashboard engine that shipped inside `activeagent` is now a separate
|
|
13
|
+
gem, **`actionagent`**. Nothing is gone — the dashboard is the same
|
|
14
|
+
dashboard, and it gained a great deal in this release — but it comes from a
|
|
15
|
+
different gem now. `activeagent` is the framework alone: it no longer
|
|
16
|
+
defines `ActiveAgent::Dashboard`, and no longer pulls Active Record into
|
|
17
|
+
apps that do not use it.
|
|
18
|
+
|
|
19
|
+
**If you mount the dashboard, add the new gem in the same change that
|
|
20
|
+
upgrades `activeagent`:**
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
gem "activeagent", "~> 1.2"
|
|
24
|
+
gem "actionagent", "~> 1.2" # required if you mount the dashboard
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
This is a minor version, so a `~> 1.0` or `~> 1.1` constraint **will** pick
|
|
28
|
+
it up on the next `bundle update`. If you mount the dashboard and do not add
|
|
29
|
+
`actionagent` at the same time, the app fails at boot with
|
|
30
|
+
`NameError: uninitialized constant ActiveAgent::Dashboard`, raised by your
|
|
31
|
+
own initializer or by the `mount ActiveAgent::Dashboard::Engine` line in
|
|
32
|
+
`config/routes.rb`. Adding the gem is the whole fix — your existing
|
|
33
|
+
configuration keeps working through the compatibility shims below.
|
|
34
|
+
|
|
35
|
+
If you do not mount the dashboard, there is nothing to do: the framework API
|
|
36
|
+
is unchanged, and the gem is 95% smaller.
|
|
37
|
+
|
|
38
|
+
With `actionagent` installed, the old constants keep resolving through
|
|
39
|
+
`ActionAgent::Compatibility` with a deprecation warning:
|
|
40
|
+
|
|
41
|
+
- `ActiveAgent::Dashboard` → `ActionAgent`
|
|
42
|
+
- `ActiveAgent::TelemetryTrace` → `ActionAgent::TelemetryTrace`
|
|
43
|
+
- `ActiveAgent::ProcessTelemetryTracesJob` → `ActionAgent::ProcessTelemetryTracesJob`
|
|
44
|
+
|
|
45
|
+
That last one matters beyond tidiness: Active Job serializes the class name
|
|
46
|
+
into the queue payload, so jobs enqueued before the upgrade still resolve
|
|
47
|
+
after it.
|
|
48
|
+
|
|
49
|
+
Other changes for mounted installs:
|
|
50
|
+
|
|
51
|
+
- **The server-rendered traces console moves from `/traces` to
|
|
52
|
+
`/console/traces`.** `/traces` is now the React traces view — the same
|
|
53
|
+
data, with more of it.
|
|
54
|
+
- **The mount is authenticated everywhere but development and test.** The
|
|
55
|
+
sandbox API, the session-recording capture endpoints and the template
|
|
56
|
+
endpoints previously allowed anonymous access; they no longer do. The
|
|
57
|
+
`GET /api/session_recordings/demo` endpoint is removed.
|
|
58
|
+
- **`current_user_method` / `current_account_method` are superseded by
|
|
59
|
+
`current_user_resolver` / `current_account_resolver`.** The engine's
|
|
60
|
+
controllers are their own base class, so a host app's `current_user`
|
|
61
|
+
helper is not available to them.
|
|
62
|
+
- **An unresolved owner now scopes to nothing rather than to everything.**
|
|
63
|
+
If you configure `user_class` or `account_class`, make sure the matching
|
|
64
|
+
resolver actually returns a record, or the dashboard will show no data.
|
|
65
|
+
- Existing installs upgrading from the in-gem dashboard: re-run
|
|
66
|
+
`rails generate action_agent:install`. It detects the migrations you
|
|
67
|
+
already have and emits only what is missing.
|
|
68
|
+
|
|
8
69
|
## [1.1.0] - 2026-08-12
|
|
9
70
|
|
|
10
71
|
### Dashboard — self-hosted (enterprise) mount readiness
|
data/README.md
CHANGED
|
@@ -25,6 +25,10 @@ Use bundler to add activeagent to your Gemfile and install:
|
|
|
25
25
|
bundle add activeagent
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
That is the framework — agents, providers, generation and telemetry
|
|
29
|
+
reporting. The dashboard is a second gem, `actionagent`, added separately
|
|
30
|
+
when you want it; see [Dashboard & Dev Console](#dashboard--dev-console).
|
|
31
|
+
|
|
28
32
|
Add the generation provider gem you want to use:
|
|
29
33
|
|
|
30
34
|
```bash
|
|
@@ -127,14 +131,19 @@ development:
|
|
|
127
131
|
service: "RubyLLM"
|
|
128
132
|
```
|
|
129
133
|
|
|
130
|
-
## Dev Console
|
|
134
|
+
## Dashboard & Dev Console
|
|
131
135
|
|
|
132
|
-
|
|
133
|
-
token usage
|
|
134
|
-
|
|
136
|
+
The dashboard is its own gem, `actionagent`: a mountable Rails engine with
|
|
137
|
+
traces and span waterfalls, token usage and per-agent metrics, plus the agent
|
|
138
|
+
builder, runs, conversations, evaluations, scorecards and cost estimates — so
|
|
139
|
+
you can watch and drive your agents while you build. It ships separately
|
|
140
|
+
because its models are Active Record models and it runs agents through
|
|
141
|
+
[solid_agent](https://github.com/activeagents/solid_agent) — neither of which
|
|
142
|
+
`activeagent` depends on, so an app that only runs agents installs neither.
|
|
135
143
|
|
|
136
144
|
```bash
|
|
137
|
-
|
|
145
|
+
bundle add actionagent
|
|
146
|
+
rails generate action_agent:install
|
|
138
147
|
rails db:migrate
|
|
139
148
|
```
|
|
140
149
|
|
|
@@ -145,13 +154,14 @@ telemetry:
|
|
|
145
154
|
local_storage: true
|
|
146
155
|
```
|
|
147
156
|
|
|
148
|
-
|
|
157
|
+
The generator mounts the engine at `/activeagents` — open it and every
|
|
158
|
+
generation appears as a trace. See
|
|
149
159
|
[docs/framework/dashboard.md](docs/framework/dashboard.md) for
|
|
150
|
-
authentication, remote ingestion, and multi-tenant mode.
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
free low-volume trial.
|
|
160
|
+
authentication, remote ingestion, and multi-tenant mode. The hosted
|
|
161
|
+
platform at [activeagents.ai](https://activeagents.ai) runs this same
|
|
162
|
+
engine multi-tenant, adding what a hosted product has to have — accounts,
|
|
163
|
+
plans, billing, quotas and managed sandboxes; every workspace starts with
|
|
164
|
+
a free low-volume trial.
|
|
155
165
|
|
|
156
166
|
## Features
|
|
157
167
|
|
|
@@ -199,12 +209,12 @@ response = prompt.generate_now
|
|
|
199
209
|
|
|
200
210
|
- [Documentation](https://docs.activeagents.ai)
|
|
201
211
|
- [Getting Started Guide](https://docs.activeagents.ai/getting_started)
|
|
202
|
-
- [API Reference](https://docs.activeagents.ai/
|
|
203
|
-
- [Examples](https://docs.activeagents.ai/
|
|
212
|
+
- [API Reference](https://docs.activeagents.ai/framework)
|
|
213
|
+
- [Examples](https://docs.activeagents.ai/agents)
|
|
204
214
|
|
|
205
215
|
## Contributing
|
|
206
216
|
|
|
207
|
-
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.
|
|
217
|
+
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.MD) for details.
|
|
208
218
|
|
|
209
219
|
## License
|
|
210
220
|
|
|
@@ -78,15 +78,19 @@ module ActiveAgent
|
|
|
78
78
|
private
|
|
79
79
|
|
|
80
80
|
# Persists a trace through the dashboard's trace model, honoring
|
|
81
|
-
#
|
|
82
|
-
#
|
|
81
|
+
# ActionAgent.trace_model_class overrides. Idempotent on trace_id, as
|
|
82
|
+
# HTTP ingest is.
|
|
83
|
+
#
|
|
84
|
+
# The dashboard is the `actionagent` gem, which the framework does not
|
|
85
|
+
# depend on — every reference to it here is guarded, so local_storage
|
|
86
|
+
# simply reports that it has nowhere to write when it isn't installed.
|
|
83
87
|
def dashboard_store
|
|
84
88
|
@dashboard_store ||= lambda do |trace, sdk|
|
|
85
89
|
model = local_trace_model
|
|
86
90
|
unless model
|
|
87
91
|
resolved_logger.error(
|
|
88
92
|
"[ActiveAgent::Telemetry] local_storage is enabled but no trace model is available — " \
|
|
89
|
-
"run `rails generate
|
|
93
|
+
"add the actionagent gem and run `rails generate action_agent:install` first"
|
|
90
94
|
)
|
|
91
95
|
next
|
|
92
96
|
end
|
|
@@ -98,10 +102,10 @@ module ActiveAgent
|
|
|
98
102
|
end
|
|
99
103
|
|
|
100
104
|
def local_trace_model
|
|
101
|
-
if defined?(
|
|
102
|
-
|
|
103
|
-
elsif defined?(
|
|
104
|
-
|
|
105
|
+
if defined?(::ActionAgent) && ::ActionAgent.respond_to?(:trace_model)
|
|
106
|
+
::ActionAgent.trace_model
|
|
107
|
+
elsif defined?(::ActionAgent::TelemetryTrace)
|
|
108
|
+
::ActionAgent::TelemetryTrace
|
|
105
109
|
end
|
|
106
110
|
rescue NameError
|
|
107
111
|
nil
|
|
@@ -124,10 +128,10 @@ module ActiveAgent
|
|
|
124
128
|
# isn't mounted there. Separate from #dashboard_mount_path so it can be
|
|
125
129
|
# exercised against a route set directly.
|
|
126
130
|
public def mount_path_in(route_set)
|
|
127
|
-
return nil unless defined?(::
|
|
131
|
+
return nil unless defined?(::ActionAgent::Engine)
|
|
128
132
|
|
|
129
133
|
route = route_set.routes.find do |candidate|
|
|
130
|
-
mounted_engine(candidate.app) == ::
|
|
134
|
+
mounted_engine(candidate.app) == ::ActionAgent::Engine
|
|
131
135
|
end
|
|
132
136
|
return nil unless route
|
|
133
137
|
|
|
@@ -147,6 +147,7 @@ module ActiveAgent
|
|
|
147
147
|
tool_span = span.add_span("tool.#{tool_call[:name]}", span_type: :tool)
|
|
148
148
|
tool_span.set_attribute("tool.name", tool_call[:name])
|
|
149
149
|
tool_span.set_attribute("tool.id", tool_call[:id]) if tool_call[:id]
|
|
150
|
+
ToolOrigin.annotate(tool_span, tool_call[:name])
|
|
150
151
|
tool_span.finish
|
|
151
152
|
end
|
|
152
153
|
end
|
|
@@ -202,6 +203,9 @@ module ActiveAgent
|
|
|
202
203
|
|
|
203
204
|
tool_span = parent.add_span("tool.#{tool_name}", span_type: :tool)
|
|
204
205
|
tool_span.set_attribute("tool.name", tool_name.to_s)
|
|
206
|
+
# Records which MCP server (if any) serves this tool, so tool
|
|
207
|
+
# traffic can be grouped by service downstream.
|
|
208
|
+
ToolOrigin.annotate(tool_span, tool_name)
|
|
205
209
|
arguments = kwargs.presence || (args.length == 1 ? args.first : args.presence)
|
|
206
210
|
if arguments.present?
|
|
207
211
|
tool_span.set_attribute("tool.input.args", agent.send(:telemetry_truncate, JSON.generate(arguments)))
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveAgent
|
|
4
|
+
module Telemetry
|
|
5
|
+
# Classifies where a tool call came from, so dashboards can group tool
|
|
6
|
+
# traffic by the service that serves it instead of showing a flat list
|
|
7
|
+
# of names.
|
|
8
|
+
#
|
|
9
|
+
# Telemetry only ever sees the name a provider used to invoke the tool,
|
|
10
|
+
# so the origin has to be recovered from naming conventions. The MCP
|
|
11
|
+
# ecosystem settled on a namespaced form — +mcp__<server>__<tool>+ —
|
|
12
|
+
# which most clients (Claude Code, Cursor, the Ruby MCP clients) emit
|
|
13
|
+
# verbatim, and that is the strongest signal available. Everything else
|
|
14
|
+
# falls back to "the agent class defines this method".
|
|
15
|
+
#
|
|
16
|
+
# Classification happens at instrumentation time rather than at read
|
|
17
|
+
# time so the attribution is recorded in the span itself: a trace stays
|
|
18
|
+
# self-describing, and consumers (the gem dashboard, activeagents.ai,
|
|
19
|
+
# any OTLP exporter) all read the same fields instead of each
|
|
20
|
+
# re-implementing the guess.
|
|
21
|
+
#
|
|
22
|
+
# @example Namespaced MCP tool
|
|
23
|
+
# ToolOrigin.classify("mcp__playwright__browser_navigate")
|
|
24
|
+
# # => { origin: "mcp", server: "playwright", tool: "browser_navigate" }
|
|
25
|
+
#
|
|
26
|
+
# @example Agent-defined method
|
|
27
|
+
# ToolOrigin.classify("lookup_order")
|
|
28
|
+
# # => { origin: "agent", server: nil, tool: "lookup_order" }
|
|
29
|
+
module ToolOrigin
|
|
30
|
+
# +mcp__<server>__<tool>+. The tool half may itself contain "__", so
|
|
31
|
+
# only the first two segments are structural.
|
|
32
|
+
MCP_PATTERN = /\Amcp__([^_]+(?:_[^_]+)*?)__(.+)\z/
|
|
33
|
+
|
|
34
|
+
# Origin values written to +tool.origin+.
|
|
35
|
+
MCP = "mcp"
|
|
36
|
+
AGENT = "agent"
|
|
37
|
+
|
|
38
|
+
module_function
|
|
39
|
+
|
|
40
|
+
# Classifies a tool name.
|
|
41
|
+
#
|
|
42
|
+
# @param name [String, Symbol] the tool name as the provider invoked it
|
|
43
|
+
# @return [Hash] +:origin+ ("mcp" or "agent"), +:server+ (MCP server
|
|
44
|
+
# name or nil), and +:tool+ (the bare tool name with any namespace
|
|
45
|
+
# stripped)
|
|
46
|
+
def classify(name)
|
|
47
|
+
name = name.to_s
|
|
48
|
+
|
|
49
|
+
if (match = MCP_PATTERN.match(name))
|
|
50
|
+
{ origin: MCP, server: match[1], tool: match[2] }
|
|
51
|
+
else
|
|
52
|
+
{ origin: AGENT, server: nil, tool: name }
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Whether a tool name is namespaced to an MCP server.
|
|
57
|
+
#
|
|
58
|
+
# @param name [String, Symbol]
|
|
59
|
+
# @return [Boolean]
|
|
60
|
+
def mcp?(name)
|
|
61
|
+
MCP_PATTERN.match?(name.to_s)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# The MCP server a tool belongs to, if any.
|
|
65
|
+
#
|
|
66
|
+
# @param name [String, Symbol]
|
|
67
|
+
# @return [String, nil]
|
|
68
|
+
def server_for(name)
|
|
69
|
+
classify(name)[:server]
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Writes the classification onto a span. Only sets +tool.mcp_server+
|
|
73
|
+
# when there is a server to name, so agent-defined tools don't carry
|
|
74
|
+
# an empty attribute.
|
|
75
|
+
#
|
|
76
|
+
# @param span [ActiveAgent::Telemetry::Span]
|
|
77
|
+
# @param name [String, Symbol] the tool name
|
|
78
|
+
# @return [Hash] the classification, for callers that also want it
|
|
79
|
+
def annotate(span, name)
|
|
80
|
+
classification = classify(name)
|
|
81
|
+
span.set_attribute("tool.origin", classification[:origin])
|
|
82
|
+
if classification[:server]
|
|
83
|
+
span.set_attribute("tool.mcp_server", classification[:server])
|
|
84
|
+
span.set_attribute("tool.base_name", classification[:tool])
|
|
85
|
+
end
|
|
86
|
+
classification
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
data/lib/active_agent/version.rb
CHANGED
data/lib/active_agent.rb
CHANGED
|
@@ -16,10 +16,6 @@ require "active_support/lazy_load_hooks"
|
|
|
16
16
|
# Module Level Extensions
|
|
17
17
|
require "active_agent/configuration"
|
|
18
18
|
require "active_agent/railtie" if defined?(Rails)
|
|
19
|
-
# The dashboard is a Rails engine; engines must be defined when the gem is
|
|
20
|
-
# required (before the host app collects railtie initializers), so it
|
|
21
|
-
# cannot be left to the Dashboard autoload above.
|
|
22
|
-
require "active_agent/dashboard" if defined?(Rails)
|
|
23
19
|
|
|
24
20
|
# ActiveAgent is a framework for building AI agents with Rails-like conventions.
|
|
25
21
|
#
|
|
@@ -112,7 +108,6 @@ module ActiveAgent
|
|
|
112
108
|
autoload :Tooling, "active_agent/concerns/tooling"
|
|
113
109
|
autoload :View, "active_agent/concerns/view"
|
|
114
110
|
autoload :Telemetry
|
|
115
|
-
autoload :Dashboard
|
|
116
111
|
|
|
117
112
|
class << self
|
|
118
113
|
# Eagerly loads all ActiveAgent components and descendant agent classes.
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activeagent
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Justin Bowen
|
|
@@ -109,6 +109,26 @@ dependencies:
|
|
|
109
109
|
- - "<="
|
|
110
110
|
- !ruby/object:Gem::Version
|
|
111
111
|
version: '9.0'
|
|
112
|
+
- !ruby/object:Gem::Dependency
|
|
113
|
+
name: railties
|
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
|
115
|
+
requirements:
|
|
116
|
+
- - ">="
|
|
117
|
+
- !ruby/object:Gem::Version
|
|
118
|
+
version: '7.2'
|
|
119
|
+
- - "<="
|
|
120
|
+
- !ruby/object:Gem::Version
|
|
121
|
+
version: '9.0'
|
|
122
|
+
type: :runtime
|
|
123
|
+
prerelease: false
|
|
124
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
125
|
+
requirements:
|
|
126
|
+
- - ">="
|
|
127
|
+
- !ruby/object:Gem::Version
|
|
128
|
+
version: '7.2'
|
|
129
|
+
- - "<="
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: '9.0'
|
|
112
132
|
- !ruby/object:Gem::Dependency
|
|
113
133
|
name: activeagents-telemetry
|
|
114
134
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -400,34 +420,6 @@ files:
|
|
|
400
420
|
- lib/active_agent/concerns/tooling.rb
|
|
401
421
|
- lib/active_agent/concerns/view.rb
|
|
402
422
|
- lib/active_agent/configuration.rb
|
|
403
|
-
- lib/active_agent/dashboard.rb
|
|
404
|
-
- lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/traces_controller.rb
|
|
405
|
-
- lib/active_agent/dashboard/app/controllers/active_agent/dashboard/application_controller.rb
|
|
406
|
-
- lib/active_agent/dashboard/app/controllers/active_agent/dashboard/dashboard_controller.rb
|
|
407
|
-
- lib/active_agent/dashboard/app/controllers/active_agent/dashboard/traces_controller.rb
|
|
408
|
-
- lib/active_agent/dashboard/app/jobs/active_agent/dashboard/agent_execution_job.rb
|
|
409
|
-
- lib/active_agent/dashboard/app/jobs/active_agent/dashboard/application_job.rb
|
|
410
|
-
- lib/active_agent/dashboard/app/jobs/active_agent/dashboard/sandbox_cleanup_job.rb
|
|
411
|
-
- lib/active_agent/dashboard/app/jobs/active_agent/dashboard/sandbox_provision_job.rb
|
|
412
|
-
- lib/active_agent/dashboard/app/jobs/active_agent/process_telemetry_traces_job.rb
|
|
413
|
-
- lib/active_agent/dashboard/app/models/active_agent/dashboard/agent.rb
|
|
414
|
-
- lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_run.rb
|
|
415
|
-
- lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_template.rb
|
|
416
|
-
- lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_version.rb
|
|
417
|
-
- lib/active_agent/dashboard/app/models/active_agent/dashboard/application_record.rb
|
|
418
|
-
- lib/active_agent/dashboard/app/models/active_agent/dashboard/recording_action.rb
|
|
419
|
-
- lib/active_agent/dashboard/app/models/active_agent/dashboard/recording_snapshot.rb
|
|
420
|
-
- lib/active_agent/dashboard/app/models/active_agent/dashboard/sandbox_run.rb
|
|
421
|
-
- lib/active_agent/dashboard/app/models/active_agent/dashboard/sandbox_session.rb
|
|
422
|
-
- lib/active_agent/dashboard/app/models/active_agent/dashboard/session_recording.rb
|
|
423
|
-
- lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb
|
|
424
|
-
- lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/_trace_detail.html.erb
|
|
425
|
-
- lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/index.html.erb
|
|
426
|
-
- lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/metrics.html.erb
|
|
427
|
-
- lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/show.html.erb
|
|
428
|
-
- lib/active_agent/dashboard/app/views/layouts/active_agent/dashboard/application.html.erb
|
|
429
|
-
- lib/active_agent/dashboard/config/routes.rb
|
|
430
|
-
- lib/active_agent/dashboard/engine.rb
|
|
431
423
|
- lib/active_agent/deprecator.rb
|
|
432
424
|
- lib/active_agent/generation.rb
|
|
433
425
|
- lib/active_agent/generation_job.rb
|
|
@@ -551,14 +543,12 @@ files:
|
|
|
551
543
|
- lib/active_agent/telemetry/instrumentation.rb
|
|
552
544
|
- lib/active_agent/telemetry/reporter.rb
|
|
553
545
|
- lib/active_agent/telemetry/span.rb
|
|
546
|
+
- lib/active_agent/telemetry/tool_origin.rb
|
|
554
547
|
- lib/active_agent/telemetry/tracer.rb
|
|
555
548
|
- lib/active_agent/version.rb
|
|
556
549
|
- lib/activeagent.rb
|
|
557
550
|
- lib/generators/active_agent/agent/USAGE
|
|
558
551
|
- lib/generators/active_agent/agent/agent_generator.rb
|
|
559
|
-
- lib/generators/active_agent/dashboard/install_generator.rb
|
|
560
|
-
- lib/generators/active_agent/dashboard/templates/active_agent_dashboard.rb.erb
|
|
561
|
-
- lib/generators/active_agent/dashboard/templates/create_active_agent_telemetry_traces.rb.erb
|
|
562
552
|
- lib/generators/active_agent/install/USAGE
|
|
563
553
|
- lib/generators/active_agent/install/install_generator.rb
|
|
564
554
|
- lib/generators/active_agent/templates/active_agent.yml
|
data/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/traces_controller.rb
DELETED
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module ActiveAgent
|
|
4
|
-
module Dashboard
|
|
5
|
-
module Api
|
|
6
|
-
# Telemetry ingestion endpoint.
|
|
7
|
-
#
|
|
8
|
-
# Receives traces from ActiveAgent::Telemetry::Reporter and stores them
|
|
9
|
-
# for analysis and visualization in the dashboard.
|
|
10
|
-
#
|
|
11
|
-
# Supports two modes:
|
|
12
|
-
# - Local mode: synchronous processing; unauthenticated unless
|
|
13
|
-
# ActiveAgent::Dashboard.ingest_api_key is set (set it whenever the
|
|
14
|
-
# mount is reachable beyond your own machine)
|
|
15
|
-
# - Multi-tenant mode: per-account Bearer token auth, async processing
|
|
16
|
-
# via job
|
|
17
|
-
#
|
|
18
|
-
# @example Local mode request
|
|
19
|
-
# POST <mount>/api/traces (e.g. /activeagents/api/traces)
|
|
20
|
-
# Content-Type: application/json
|
|
21
|
-
#
|
|
22
|
-
# {
|
|
23
|
-
# "traces": [...],
|
|
24
|
-
# "sdk": { "name": "activeagent", "version": "0.5.0" }
|
|
25
|
-
# }
|
|
26
|
-
#
|
|
27
|
-
# @example Multi-tenant mode request
|
|
28
|
-
# POST <mount>/api/traces (e.g. /activeagents/api/traces)
|
|
29
|
-
# Authorization: Bearer <api_key>
|
|
30
|
-
# Content-Type: application/json
|
|
31
|
-
#
|
|
32
|
-
# {
|
|
33
|
-
# "traces": [...],
|
|
34
|
-
# "sdk": { "name": "activeagent", "version": "0.5.0" }
|
|
35
|
-
# }
|
|
36
|
-
#
|
|
37
|
-
class TracesController < ActionController::API
|
|
38
|
-
before_action :authenticate_api_key!, if: -> { ActiveAgent::Dashboard.multi_tenant? }
|
|
39
|
-
before_action :authenticate_ingest_key!, unless: -> { ActiveAgent::Dashboard.multi_tenant? }
|
|
40
|
-
|
|
41
|
-
# Maximum traces accepted per request (mirrors
|
|
42
|
-
# ProcessTelemetryTracesJob::MAX_TRACES_PER_JOB).
|
|
43
|
-
MAX_TRACES_PER_REQUEST = 100
|
|
44
|
-
|
|
45
|
-
# POST <mount>/api/traces (e.g. /activeagents/api/traces)
|
|
46
|
-
def create
|
|
47
|
-
traces = Array(params[:traces]).take(MAX_TRACES_PER_REQUEST)
|
|
48
|
-
sdk_info = params[:sdk] || {}
|
|
49
|
-
|
|
50
|
-
return head :accepted if traces.empty?
|
|
51
|
-
|
|
52
|
-
if ActiveAgent::Dashboard.multi_tenant?
|
|
53
|
-
# Multi-tenant mode: process in background
|
|
54
|
-
ActiveAgent::ProcessTelemetryTracesJob.perform_later(
|
|
55
|
-
account_id: @account&.id,
|
|
56
|
-
traces: traces.as_json,
|
|
57
|
-
sdk_info: sdk_info.as_json,
|
|
58
|
-
received_at: Time.current.iso8601(6)
|
|
59
|
-
)
|
|
60
|
-
else
|
|
61
|
-
# Local mode: process synchronously
|
|
62
|
-
process_traces_synchronously(traces, sdk_info)
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
head :accepted
|
|
66
|
-
rescue ActionController::ParameterMissing => e
|
|
67
|
-
render json: { error: e.message }, status: :bad_request
|
|
68
|
-
rescue StandardError => e
|
|
69
|
-
Rails.logger.error("[ActiveAgent::Dashboard] Trace ingestion error: #{e.message}")
|
|
70
|
-
render json: { error: "Internal server error" }, status: :internal_server_error
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
private
|
|
74
|
-
|
|
75
|
-
# Authenticates the request using Bearer token from Authorization header.
|
|
76
|
-
# Only used in multi-tenant mode.
|
|
77
|
-
def authenticate_api_key!
|
|
78
|
-
token = extract_bearer_token
|
|
79
|
-
|
|
80
|
-
if token.blank?
|
|
81
|
-
render json: { error: "Missing Authorization header" }, status: :unauthorized
|
|
82
|
-
return
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
account_class = ActiveAgent::Dashboard.account_class.constantize
|
|
86
|
-
@account = account_class.find_by(telemetry_api_key: token)
|
|
87
|
-
|
|
88
|
-
if @account.nil?
|
|
89
|
-
render json: { error: "Invalid API key" }, status: :unauthorized
|
|
90
|
-
return
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
# Track usage for rate limiting (if the account responds to it)
|
|
94
|
-
@account.increment_telemetry_usage! if @account.respond_to?(:increment_telemetry_usage!)
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
# Requires the configured single-tenant ingest key when one is set.
|
|
98
|
-
# The telemetry reporter and ruby_llm_telemetry both send their
|
|
99
|
-
# api_key as a Bearer header, so remote apps work unchanged.
|
|
100
|
-
def authenticate_ingest_key!
|
|
101
|
-
expected = ActiveAgent::Dashboard.ingest_api_key
|
|
102
|
-
return if expected.blank?
|
|
103
|
-
|
|
104
|
-
token = extract_bearer_token
|
|
105
|
-
return if token.present? && ActiveSupport::SecurityUtils.secure_compare(token, expected)
|
|
106
|
-
|
|
107
|
-
render json: { error: "Invalid API key" }, status: :unauthorized
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
# Extracts Bearer token from Authorization header.
|
|
111
|
-
def extract_bearer_token
|
|
112
|
-
auth_header = request.headers["Authorization"]
|
|
113
|
-
return nil if auth_header.blank?
|
|
114
|
-
|
|
115
|
-
match = auth_header.match(/^Bearer\s+(.+)$/i)
|
|
116
|
-
match[1] if match
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
# Process traces synchronously for local development.
|
|
120
|
-
def process_traces_synchronously(traces, sdk_info)
|
|
121
|
-
model = ActiveAgent::Dashboard.trace_model
|
|
122
|
-
|
|
123
|
-
traces.each do |trace|
|
|
124
|
-
# Skip if trace already exists (idempotency)
|
|
125
|
-
next if model.exists?(trace_id: trace["trace_id"])
|
|
126
|
-
|
|
127
|
-
model.create_from_payload(trace, sdk_info)
|
|
128
|
-
rescue StandardError => e
|
|
129
|
-
Rails.logger.error(
|
|
130
|
-
"[ActiveAgent::Dashboard] Failed to process trace #{trace['trace_id']}: " \
|
|
131
|
-
"#{e.class} - #{e.message}"
|
|
132
|
-
)
|
|
133
|
-
end
|
|
134
|
-
end
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
end
|
data/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/application_controller.rb
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module ActiveAgent
|
|
4
|
-
module Dashboard
|
|
5
|
-
# Base controller for the ActiveAgent Dashboard.
|
|
6
|
-
#
|
|
7
|
-
# Handles authentication and provides helper methods for multi-tenant mode.
|
|
8
|
-
class ApplicationController < ActionController::Base
|
|
9
|
-
protect_from_forgery with: :exception
|
|
10
|
-
|
|
11
|
-
before_action :authenticate_dashboard!
|
|
12
|
-
|
|
13
|
-
# Add the engine's view path
|
|
14
|
-
prepend_view_path ActiveAgent::Dashboard::Engine.dashboard_root.join("app", "views").to_s
|
|
15
|
-
|
|
16
|
-
# Use custom layout if configured, otherwise use engine layout
|
|
17
|
-
layout -> { ActiveAgent::Dashboard.layout || "active_agent/dashboard/application" }
|
|
18
|
-
|
|
19
|
-
helper_method :current_user, :current_owner
|
|
20
|
-
|
|
21
|
-
private
|
|
22
|
-
|
|
23
|
-
def authenticate_dashboard!
|
|
24
|
-
if ActiveAgent::Dashboard.authentication_method.nil?
|
|
25
|
-
# Traces contain prompts, outputs, and error backtraces. Refuse to
|
|
26
|
-
# serve them unauthenticated in production rather than exposing
|
|
27
|
-
# them to the internet by default.
|
|
28
|
-
if Rails.env.production?
|
|
29
|
-
render plain: "ActiveAgent Dashboard: set ActiveAgent::Dashboard.authentication_method " \
|
|
30
|
-
"(see docs/framework/dashboard.md) to enable access in production.",
|
|
31
|
-
status: :forbidden
|
|
32
|
-
end
|
|
33
|
-
return
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
result = ActiveAgent::Dashboard.authentication_method.call(self)
|
|
37
|
-
head :unauthorized unless result
|
|
38
|
-
rescue StandardError => e
|
|
39
|
-
Rails.logger.error("[ActiveAgent::Dashboard] Authentication error: #{e.message}")
|
|
40
|
-
head :unauthorized
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# Returns the current user from the host application.
|
|
44
|
-
def current_user
|
|
45
|
-
return nil unless ActiveAgent::Dashboard.current_user_method
|
|
46
|
-
|
|
47
|
-
send(ActiveAgent::Dashboard.current_user_method)
|
|
48
|
-
rescue NoMethodError
|
|
49
|
-
nil
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# Returns the current owner (account in multi-tenant, user otherwise).
|
|
53
|
-
def current_owner
|
|
54
|
-
if ActiveAgent::Dashboard.multi_tenant? && ActiveAgent::Dashboard.current_account_method
|
|
55
|
-
send(ActiveAgent::Dashboard.current_account_method)
|
|
56
|
-
else
|
|
57
|
-
current_user
|
|
58
|
-
end
|
|
59
|
-
rescue NoMethodError
|
|
60
|
-
nil
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
end
|