activeagent 1.5.2 → 1.6.2
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 +266 -0
- data/lib/active_agent/base.rb +9 -0
- data/lib/active_agent/concerns/authorization.rb +172 -0
- data/lib/active_agent/concerns/parameterized.rb +40 -3
- data/lib/active_agent/concerns/release.rb +171 -0
- data/lib/active_agent/concerns/tooling.rb +3 -1
- data/lib/active_agent/delegation/runner.rb +17 -0
- data/lib/active_agent/evals/diagnosis.rb +62 -6
- data/lib/active_agent/evals/judge.rb +7 -2
- data/lib/active_agent/evals/model_spec.rb +27 -11
- data/lib/active_agent/evals/runner.rb +1 -1
- data/lib/active_agent/evals/scorer.rb +4 -1
- data/lib/active_agent/generation_job.rb +7 -1
- data/lib/active_agent/schema_tools.rb +173 -6
- data/lib/active_agent/telemetry/configuration.rb +11 -0
- data/lib/active_agent/telemetry/instrumentation.rb +9 -0
- data/lib/active_agent/telemetry/tracer.rb +4 -1
- data/lib/active_agent/telemetry.rb +22 -0
- data/lib/active_agent/version.rb +1 -1
- data/lib/active_agent.rb +3 -0
- data/lib/generators/active_agent/schema_tools/USAGE +22 -0
- data/lib/generators/active_agent/schema_tools/schema_tools_generator.rb +84 -0
- data/lib/generators/active_agent/schema_tools/templates/schema_tools.rb.tt +48 -0
- metadata +7 -2
|
@@ -24,6 +24,17 @@ module ActiveAgent
|
|
|
24
24
|
# @return [Boolean] Whether to store traces in the app's own database
|
|
25
25
|
attr_reader :local_storage
|
|
26
26
|
|
|
27
|
+
# The deploy every trace is stamped with (`service.version`). Unset, it
|
|
28
|
+
# is whatever ActiveAgent::Release.revision resolves — a git SHA from
|
|
29
|
+
# the conventional deploy variables — so traces from two deploys of
|
|
30
|
+
# the same service can be told apart without any host configuration.
|
|
31
|
+
attr_writer :service_version
|
|
32
|
+
|
|
33
|
+
def service_version
|
|
34
|
+
value = @service_version.respond_to?(:call) ? @service_version.call : @service_version
|
|
35
|
+
(value.presence || ActiveAgent::Release.revision)&.to_s
|
|
36
|
+
end
|
|
37
|
+
|
|
27
38
|
def initialize
|
|
28
39
|
super
|
|
29
40
|
# The framework predates the shared gem and has always been opt-in;
|
|
@@ -58,6 +58,15 @@ module ActiveAgent
|
|
|
58
58
|
span.set_attribute("agent.action", action_name.to_s)
|
|
59
59
|
span.set_attribute("agent.provider", provider_name)
|
|
60
60
|
span.set_attribute("agent.model", model_name)
|
|
61
|
+
# Which release of the agent ran: the digest of what the model was
|
|
62
|
+
# given (ActiveAgent::Release), so a dashboard can pin this trace
|
|
63
|
+
# to the version it cut on deploy.
|
|
64
|
+
if self.class.respond_to?(:release_digest)
|
|
65
|
+
span.set_attribute("agent.version", self.class.release_digest)
|
|
66
|
+
if (revision = ActiveAgent::Release.revision).present?
|
|
67
|
+
span.set_attribute("agent.revision", revision)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
61
70
|
|
|
62
71
|
# Add prompt span, carrying the prompt contents (instructions +
|
|
63
72
|
# outbound messages) so dashboards can show what was sent.
|
|
@@ -169,12 +169,15 @@ module ActiveAgent
|
|
|
169
169
|
|
|
170
170
|
# Returns default attributes for all spans.
|
|
171
171
|
def default_attributes
|
|
172
|
-
{
|
|
172
|
+
attributes = {
|
|
173
173
|
"service.name" => configuration.resolved_service_name,
|
|
174
174
|
"service.environment" => configuration.resolved_environment,
|
|
175
175
|
"telemetry.sdk.name" => "activeagent",
|
|
176
176
|
"telemetry.sdk.version" => ActiveAgent::VERSION
|
|
177
177
|
}
|
|
178
|
+
version = configuration.respond_to?(:service_version) ? configuration.service_version : nil
|
|
179
|
+
attributes["service.version"] = version if version.present?
|
|
180
|
+
attributes
|
|
178
181
|
end
|
|
179
182
|
end
|
|
180
183
|
end
|
|
@@ -78,9 +78,31 @@ module ActiveAgent
|
|
|
78
78
|
# end
|
|
79
79
|
def configure
|
|
80
80
|
yield configuration if block_given?
|
|
81
|
+
install_instrumentation! if enabled?
|
|
81
82
|
configuration
|
|
82
83
|
end
|
|
83
84
|
|
|
85
|
+
# Installs generation instrumentation on ActiveAgent::Base.
|
|
86
|
+
#
|
|
87
|
+
# The railtie also does this at boot, but only for configuration already
|
|
88
|
+
# loaded by then (activeagent.yml, config.active_agent.telemetry). A host
|
|
89
|
+
# app that enables telemetry from its own initializer runs *after*
|
|
90
|
+
# railties, so that check has already seen `enabled? == false` and
|
|
91
|
+
# skipped the install — leaving telemetry enabled but nothing
|
|
92
|
+
# instrumented, and so no traces despite a valid local_store. Calling it
|
|
93
|
+
# from {.configure} as well makes the install order-independent;
|
|
94
|
+
# `instrument_telemetry!` is idempotent, so the two paths cannot
|
|
95
|
+
# double-prepend.
|
|
96
|
+
#
|
|
97
|
+
# @api private
|
|
98
|
+
# @return [void]
|
|
99
|
+
def install_instrumentation!
|
|
100
|
+
return unless defined?(ActiveAgent::Base)
|
|
101
|
+
|
|
102
|
+
ActiveAgent::Base.include(Instrumentation)
|
|
103
|
+
ActiveAgent::Base.instrument_telemetry!
|
|
104
|
+
end
|
|
105
|
+
|
|
84
106
|
# Resets the configuration to defaults.
|
|
85
107
|
#
|
|
86
108
|
# @return [Configuration] New default configuration
|
data/lib/active_agent/version.rb
CHANGED
data/lib/active_agent.rb
CHANGED
|
@@ -92,6 +92,9 @@ module ActiveAgent
|
|
|
92
92
|
#
|
|
93
93
|
# These components are loaded on-demand when first referenced.
|
|
94
94
|
autoload :Base
|
|
95
|
+
# The refusal an agent raises, and the default in Base.authorization_errors.
|
|
96
|
+
# Reachable before any agent class has loaded, so the dashboard can name it.
|
|
97
|
+
autoload :NotAuthorized, "active_agent/concerns/authorization"
|
|
95
98
|
autoload :Callbacks, "active_agent/concerns/callbacks"
|
|
96
99
|
autoload :Delegation, "active_agent/concerns/delegation"
|
|
97
100
|
autoload :Streaming, "active_agent/concerns/streaming"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Description:
|
|
2
|
+
Writes a starter ActiveAgent::SchemaTools class for a model under
|
|
3
|
+
app/agent_tools. The class generates find_<records>, count_<records> and
|
|
4
|
+
get_<record> tools, and exposes nothing beyond `id` until you move a
|
|
5
|
+
column into `filterable` or `returns` — every column the model has is
|
|
6
|
+
listed, commented out, so the allowlist is a review step rather than a
|
|
7
|
+
blank page. Columns that look like secrets are left out of the list.
|
|
8
|
+
|
|
9
|
+
When <Model>Policy::Scope exists the class scopes every read through it
|
|
10
|
+
(`scope_by_policy`); otherwise a `scope` block is suggested. Pass
|
|
11
|
+
--policy or --no-policy to decide explicitly.
|
|
12
|
+
|
|
13
|
+
Examples:
|
|
14
|
+
`bin/rails generate active_agent:schema_tools Reservation`
|
|
15
|
+
|
|
16
|
+
creates:
|
|
17
|
+
app/agent_tools/reservation_tools.rb
|
|
18
|
+
|
|
19
|
+
`bin/rails generate active_agent:schema_tools Reservation --policy`
|
|
20
|
+
|
|
21
|
+
scopes reads through ReservationPolicy::Scope even if it cannot be
|
|
22
|
+
found from here.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveAgent
|
|
4
|
+
module Generators
|
|
5
|
+
# Writes a starter ActiveAgent::SchemaTools class for one model:
|
|
6
|
+
#
|
|
7
|
+
# bin/rails generate active_agent:schema_tools Reservation
|
|
8
|
+
#
|
|
9
|
+
# The class it writes exposes nothing beyond +id+ until someone uncomments
|
|
10
|
+
# a column, because which columns an agent may filter on and read back is
|
|
11
|
+
# a judgement about exposure, not a fact about the table (#440). Every
|
|
12
|
+
# column the model has is listed, commented out, minus the ones that look
|
|
13
|
+
# like secrets, so the allowlist is a review step rather than a blank page.
|
|
14
|
+
class SchemaToolsGenerator < ::Rails::Generators::NamedBase
|
|
15
|
+
source_root File.expand_path("templates", __dir__)
|
|
16
|
+
|
|
17
|
+
# Columns never suggested, whatever the model: reading one back would
|
|
18
|
+
# hand a model a credential, and filtering on one leaks it a character
|
|
19
|
+
# at a time through the row counts.
|
|
20
|
+
SECRET_COLUMNS = /password|digest|token|secret|api_key|otp|encrypted|ssn/i
|
|
21
|
+
|
|
22
|
+
class_option :policy, type: :boolean, default: nil,
|
|
23
|
+
desc: "Scope every read through <Model>Policy::Scope (default: when that policy exists)"
|
|
24
|
+
|
|
25
|
+
check_class_collision suffix: "Tools"
|
|
26
|
+
|
|
27
|
+
def create_tools_file
|
|
28
|
+
template "schema_tools.rb.tt", File.join("app/agent_tools", class_path, "#{file_name}_tools.rb")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
# "Reservation" and "ReservationTools" both name the Reservation model.
|
|
34
|
+
def file_name # :doc:
|
|
35
|
+
@_file_name ||= super.sub(/_tools\z/i, "")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def model_class
|
|
39
|
+
@model_class ||= class_name.safe_constantize
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def policy_class_name
|
|
43
|
+
"#{class_name}Policy"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def policy?
|
|
47
|
+
return options[:policy] unless options[:policy].nil?
|
|
48
|
+
|
|
49
|
+
"#{policy_class_name}::Scope".safe_constantize.present?
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# [name, type] for every column the model has, or [] when the model or
|
|
53
|
+
# its table cannot be read from here (a model that does not exist yet,
|
|
54
|
+
# or a database that is not set up) — the file is still written.
|
|
55
|
+
def columns
|
|
56
|
+
@columns ||= begin
|
|
57
|
+
if model_class.respond_to?(:columns) && model_class.respond_to?(:table_exists?) && model_class.table_exists?
|
|
58
|
+
model_class.columns.map { |column| [ column.name, column.type ] }
|
|
59
|
+
else
|
|
60
|
+
[]
|
|
61
|
+
end
|
|
62
|
+
rescue StandardError
|
|
63
|
+
[]
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def suggested_columns
|
|
68
|
+
columns.reject { |name, _type| name == "id" || name.match?(SECRET_COLUMNS) }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def secret_columns
|
|
72
|
+
columns.select { |name, _type| name.match?(SECRET_COLUMNS) }.map(&:first)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def collection_name
|
|
76
|
+
class_name.demodulize.underscore.pluralize
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def resource_name
|
|
80
|
+
class_name.demodulize.underscore
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<% module_namespacing do -%>
|
|
2
|
+
# Bounded, read-only agent tools over <%= class_name %>:
|
|
3
|
+
# find_<%= collection_name %>, count_<%= collection_name %>, get_<%= resource_name %>.
|
|
4
|
+
#
|
|
5
|
+
# Nothing is exposed until you say so. Move a column from the commented lists
|
|
6
|
+
# into `filterable` (an agent may filter on it) or `returns` (an agent may
|
|
7
|
+
# read it back). Each one is a judgement about exposure, not a fact about the
|
|
8
|
+
# table: leave out anything a model should never see.
|
|
9
|
+
class <%= class_name %>Tools < ActiveAgent::SchemaTools
|
|
10
|
+
model <%= class_name %>
|
|
11
|
+
|
|
12
|
+
filterable :id
|
|
13
|
+
<% if suggested_columns.any? -%>
|
|
14
|
+
# filterable <%= suggested_columns.map { |name, _type| ":#{name}" }.join(", ") %>
|
|
15
|
+
<% else -%>
|
|
16
|
+
# filterable :status, :owner_id # columns an agent may filter on
|
|
17
|
+
<% end -%>
|
|
18
|
+
|
|
19
|
+
returns :id
|
|
20
|
+
<% if suggested_columns.any? -%>
|
|
21
|
+
# returns <%= suggested_columns.map { |name, _type| ":#{name}" }.join(", ") %>
|
|
22
|
+
<% else -%>
|
|
23
|
+
# returns :id, :title, :status # columns an agent may read back
|
|
24
|
+
<% end -%>
|
|
25
|
+
<% if suggested_columns.any? -%>
|
|
26
|
+
#
|
|
27
|
+
# Columns and their types:
|
|
28
|
+
<% suggested_columns.each do |name, type| -%>
|
|
29
|
+
# <%= name.ljust(24) %> <%= type %>
|
|
30
|
+
<% end -%>
|
|
31
|
+
<% end -%>
|
|
32
|
+
<% if secret_columns.any? -%>
|
|
33
|
+
#
|
|
34
|
+
# Not suggested, and not to be added: <%= secret_columns.join(", ") %>.
|
|
35
|
+
<% end -%>
|
|
36
|
+
|
|
37
|
+
<% if policy? -%>
|
|
38
|
+
# Every read runs through <%= policy_class_name %>::Scope for the acting
|
|
39
|
+
# user, so an agent sees exactly what that user could see.
|
|
40
|
+
scope_by_policy
|
|
41
|
+
<% else -%>
|
|
42
|
+
# Every read should run through a relation scoped to the acting user. Without
|
|
43
|
+
# a scope the tools read the whole table. Return <%= class_name %>.none for an
|
|
44
|
+
# actor with no access; never widen for a nil actor.
|
|
45
|
+
# scope { |actor| actor ? <%= class_name %>.where(owner: actor) : <%= class_name %>.none }
|
|
46
|
+
<% end -%>
|
|
47
|
+
end
|
|
48
|
+
<% end -%>
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activeagent
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.6.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Justin Bowen
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-17 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: actionpack
|
|
@@ -410,6 +410,7 @@ files:
|
|
|
410
410
|
- lib/active_agent.rb
|
|
411
411
|
- lib/active_agent/base.rb
|
|
412
412
|
- lib/active_agent/collector.rb
|
|
413
|
+
- lib/active_agent/concerns/authorization.rb
|
|
413
414
|
- lib/active_agent/concerns/callbacks.rb
|
|
414
415
|
- lib/active_agent/concerns/delegation.rb
|
|
415
416
|
- lib/active_agent/concerns/observers.rb
|
|
@@ -417,6 +418,7 @@ files:
|
|
|
417
418
|
- lib/active_agent/concerns/preview.rb
|
|
418
419
|
- lib/active_agent/concerns/provider.rb
|
|
419
420
|
- lib/active_agent/concerns/queueing.rb
|
|
421
|
+
- lib/active_agent/concerns/release.rb
|
|
420
422
|
- lib/active_agent/concerns/rescue.rb
|
|
421
423
|
- lib/active_agent/concerns/streaming.rb
|
|
422
424
|
- lib/active_agent/concerns/tooling.rb
|
|
@@ -578,6 +580,9 @@ files:
|
|
|
578
580
|
- lib/generators/active_agent/agent/agent_generator.rb
|
|
579
581
|
- lib/generators/active_agent/install/USAGE
|
|
580
582
|
- lib/generators/active_agent/install/install_generator.rb
|
|
583
|
+
- lib/generators/active_agent/schema_tools/USAGE
|
|
584
|
+
- lib/generators/active_agent/schema_tools/schema_tools_generator.rb
|
|
585
|
+
- lib/generators/active_agent/schema_tools/templates/schema_tools.rb.tt
|
|
581
586
|
- lib/generators/active_agent/templates/active_agent.yml
|
|
582
587
|
- lib/generators/active_agent/templates/agent.rb.tt
|
|
583
588
|
- lib/generators/active_agent/templates/application_agent.rb.tt
|