actionagent 1.2.1 → 1.2.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/app/models/action_agent/telemetry_trace.rb +1 -1
- data/app/services/action_agent/agent_execution_service.rb +11 -3
- data/app/services/action_agent/agent_toolbox.rb +24 -1
- data/lib/action_agent/engine.rb +53 -18
- data/lib/action_agent/version.rb +1 -1
- data/lib/action_agent.rb +19 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f6e31cd17a84e6621881630137c3478268d2e31c8bba138516a397c23964c4f9
|
|
4
|
+
data.tar.gz: 28e83e7b84704c5ff9de2b475998273ac3dd328929515472f31e3743981927de
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3a9e49dce9507b1243dd494b842c967bb5cb04921a7825ae2e16e37ed3cf25e845ae5b86de26b50cacdb01e7120859624ea7667d05c0d04048489bc7af5b534e
|
|
7
|
+
data.tar.gz: bebd359520c4ea07d69288ef8381afd5e5f663ea6df3264e17db81c2bc1a6d78836be216a8677666ede6223a1069f39e3e8e3a709025adcf8c9817a2be9750c9
|
|
@@ -135,7 +135,7 @@ module ActionAgent
|
|
|
135
135
|
# Add account if in multi-tenant mode
|
|
136
136
|
attrs[:account] = account if ActionAgent.multi_tenant? && account
|
|
137
137
|
|
|
138
|
-
create!(attrs)
|
|
138
|
+
create!(attrs).tap { |record| AgentRegistrar.call(record) }
|
|
139
139
|
end
|
|
140
140
|
|
|
141
141
|
# Sums a span's token counts (used to decide which spans carry the
|
|
@@ -354,8 +354,14 @@ module ActionAgent
|
|
|
354
354
|
define_singleton_method(:name) { klass_name }
|
|
355
355
|
|
|
356
356
|
# Persist the conversation (agent_contexts / agent_messages /
|
|
357
|
-
# agent_generations) via solid_agent.
|
|
358
|
-
# is loaded explicitly in the action below.
|
|
357
|
+
# agent_generations) via solid_agent. Auto-context is switched off —
|
|
358
|
+
# the context is loaded explicitly in the action below.
|
|
359
|
+
#
|
|
360
|
+
# The keyword that switches it off was renamed (contextable: ->
|
|
361
|
+
# contextual:) between solid_agent 0.1 and 0.2, and the gemspec floor
|
|
362
|
+
# admits both, so it is resolved from the installed method rather than
|
|
363
|
+
# hard-coded: passing the wrong one is an ArgumentError that only
|
|
364
|
+
# surfaces when a run executes.
|
|
359
365
|
#
|
|
360
366
|
# The model classes are named explicitly because solid_agent infers
|
|
361
367
|
# bare "AgentContext"/"AgentMessage"/"AgentGeneration" and resolves
|
|
@@ -363,10 +369,12 @@ module ActionAgent
|
|
|
363
369
|
# inferred names only resolve in a host app that happens to have
|
|
364
370
|
# top-level models of its own.
|
|
365
371
|
include SolidAgent::HasContext
|
|
366
|
-
has_context
|
|
372
|
+
has_context(
|
|
373
|
+
ActionAgent.solid_agent_auto_context_keyword => false,
|
|
367
374
|
class_name: "ActionAgent::AgentContext",
|
|
368
375
|
message_class: "ActionAgent::AgentMessage",
|
|
369
376
|
generation_class: "ActionAgent::AgentGeneration"
|
|
377
|
+
)
|
|
370
378
|
|
|
371
379
|
if effective_provider == :mock
|
|
372
380
|
# Test environment only (see #provider_available?).
|
|
@@ -399,7 +399,7 @@ module ActionAgent
|
|
|
399
399
|
if defined?(SolidAgent::ToolCache)
|
|
400
400
|
SolidAgent::ToolCache.fetch(tool: name.to_s, args: kwargs, ttl: CACHE_TTL, &block)
|
|
401
401
|
else
|
|
402
|
-
key =
|
|
402
|
+
key = fallback_cache_key(name, kwargs)
|
|
403
403
|
cached = Rails.cache.read(key)
|
|
404
404
|
return cached.merge(cached: true) unless cached.nil?
|
|
405
405
|
|
|
@@ -411,6 +411,29 @@ module ActionAgent
|
|
|
411
411
|
end
|
|
412
412
|
end
|
|
413
413
|
|
|
414
|
+
# Byte-for-byte the key SolidAgent::ToolCache would compute, so an app
|
|
415
|
+
# that upgrades solid_agent mid-TTL keeps reading what it already
|
|
416
|
+
# cached instead of silently starting over. Nested hashes and
|
|
417
|
+
# symbol/string keys have to normalize the same way, which a plain
|
|
418
|
+
# `kwargs.sort.to_h.to_json` does not do.
|
|
419
|
+
#
|
|
420
|
+
# test/integration/solid_agent/tool_cache_test.rb asserts the two
|
|
421
|
+
# schemes still agree.
|
|
422
|
+
def fallback_cache_key(name, kwargs)
|
|
423
|
+
"solid_agent:tool_cache:#{name}:#{Digest::SHA256.hexdigest(normalize_cache_args(kwargs).to_json)}"
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
def normalize_cache_args(args)
|
|
427
|
+
case args
|
|
428
|
+
when Hash
|
|
429
|
+
args.map { |key, value| [ key.to_s, normalize_cache_args(value) ] }.sort_by(&:first)
|
|
430
|
+
when Array
|
|
431
|
+
args.map { |value| normalize_cache_args(value) }
|
|
432
|
+
else
|
|
433
|
+
args
|
|
434
|
+
end
|
|
435
|
+
end
|
|
436
|
+
|
|
414
437
|
# SSRF guard for fetch_url: reject hosts that resolve to loopback,
|
|
415
438
|
# private, or link-local addresses.
|
|
416
439
|
def public_host?(host)
|
data/lib/action_agent/engine.rb
CHANGED
|
@@ -13,6 +13,13 @@ module ActionAgent
|
|
|
13
13
|
|
|
14
14
|
engine_name "action_agent"
|
|
15
15
|
|
|
16
|
+
# Basenames this engine spells differently from Zeitwerk's default
|
|
17
|
+
# camelization, consulted by the inflections initializer below. Empty
|
|
18
|
+
# today: every file here is named for the constant default camelization
|
|
19
|
+
# produces. An engine file that wants a genuine acronym in its constant
|
|
20
|
+
# adds its basename here rather than relying on the host to register one.
|
|
21
|
+
INFLECTION_OVERRIDES = {}.freeze
|
|
22
|
+
|
|
16
23
|
config.action_agent = ActiveSupport::OrderedOptions.new
|
|
17
24
|
|
|
18
25
|
# The dashboard's JS and CSS ship prebuilt in the gem. Adding the
|
|
@@ -26,26 +33,34 @@ module ActionAgent
|
|
|
26
33
|
app.config.filter_parameters += [ :credential, :api_key, :access_token ]
|
|
27
34
|
end
|
|
28
35
|
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
# ActionAgent::
|
|
35
|
-
#
|
|
36
|
+
# This engine's constants are spelled the way Zeitwerk's own inflector
|
|
37
|
+
# spells them — Api, McpCatalog, ApiKey — but an engine's files are
|
|
38
|
+
# autoloaded by the host's `rails.main` loader, under the *host's*
|
|
39
|
+
# inflections. A host that declares `inflect.acronym "API"` or "MCP" (both
|
|
40
|
+
# common, and documented by Rails) makes Zeitwerk expect
|
|
41
|
+
# ActionAgent::API::TracesController or ActionAgent::MCPCatalog from files
|
|
42
|
+
# that define ActionAgent::Api::TracesController and
|
|
43
|
+
# ActionAgent::McpCatalog. The constant never resolves and the request
|
|
44
|
+
# raises Zeitwerk::NameError.
|
|
45
|
+
#
|
|
46
|
+
# Every path under this engine therefore camelizes with Zeitwerk's default
|
|
47
|
+
# rules, ignoring whatever acronyms the host has registered. Applied by
|
|
48
|
+
# path rather than through `inflect`: the loader is shared with the host,
|
|
49
|
+
# so a blanket rule would re-spell the host's own constants. `camelize`
|
|
50
|
+
# receives the absolute path, which is the only hook that can tell this
|
|
51
|
+
# engine's files from the host's.
|
|
36
52
|
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
# constants. `camelize` receives the absolute path, which is the only hook
|
|
40
|
-
# that can tell this engine's api/ from the host's.
|
|
53
|
+
# Basenames whose spelling this engine cannot express through default
|
|
54
|
+
# camelization (a genuine acronym it wants uppercased) go in OVERRIDES.
|
|
41
55
|
initializer "action_agent.inflections", before: :set_autoload_paths do
|
|
42
|
-
engine_root = root.to_s
|
|
56
|
+
engine_root = File.join(root.to_s, "")
|
|
57
|
+
default = Zeitwerk::Inflector.new
|
|
43
58
|
|
|
44
59
|
Rails.autoloaders.main.inflector.singleton_class.prepend(Module.new do
|
|
45
60
|
define_method(:camelize) do |basename, abspath|
|
|
46
|
-
next
|
|
61
|
+
next super(basename, abspath) unless abspath.to_s.start_with?(engine_root)
|
|
47
62
|
|
|
48
|
-
|
|
63
|
+
ActionAgent::Engine::INFLECTION_OVERRIDES.fetch(basename) { default.camelize(basename, abspath) }
|
|
49
64
|
end
|
|
50
65
|
end)
|
|
51
66
|
end
|
|
@@ -59,13 +74,33 @@ module ActionAgent
|
|
|
59
74
|
# So the namespace answers to both. `const_missing` rather than an eager
|
|
60
75
|
# alias because the controllers are autoloaded on demand, and naming them at
|
|
61
76
|
# boot would load the whole dashboard.
|
|
62
|
-
|
|
77
|
+
# The router does not consult the autoloader's inflector, so an acronym
|
|
78
|
+
# host asks for ActionAgent::API::MCPServersController while the constants
|
|
79
|
+
# are Api::McpServersController. Rather than enumerate the pairs, an
|
|
80
|
+
# all-caps run in a missing constant is retried in the spelling default
|
|
81
|
+
# camelization produces: API -> Api, MCPServersController -> McpServers-
|
|
82
|
+
# Controller. Only the engine's own namespaces are touched, and only for a
|
|
83
|
+
# constant that is already missing.
|
|
84
|
+
inflection_shim = Module.new do
|
|
63
85
|
def const_missing(name)
|
|
64
|
-
|
|
86
|
+
relaxed = name.to_s.gsub(/([A-Z])([A-Z]+)(?=[A-Z][a-z]|\d|\z)/) { "#{$1}#{$2.downcase}" }
|
|
87
|
+
|
|
88
|
+
return super if relaxed == name.to_s || !const_defined?(relaxed, false)
|
|
89
|
+
|
|
90
|
+
const_get(relaxed, false)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
65
93
|
|
|
66
|
-
|
|
94
|
+
ActionAgent.singleton_class.prepend(inflection_shim)
|
|
95
|
+
|
|
96
|
+
# ActionAgent::Api is autoloaded, so it cannot be reopened at this point.
|
|
97
|
+
# Zeitwerk hands it over as soon as it is defined, which is before the
|
|
98
|
+
# router can ask it for a controller.
|
|
99
|
+
initializer "action_agent.api_inflection_shim" do
|
|
100
|
+
Rails.autoloaders.main.on_load("ActionAgent::Api") do |mod, _abspath|
|
|
101
|
+
mod.singleton_class.prepend(inflection_shim)
|
|
67
102
|
end
|
|
68
|
-
end
|
|
103
|
+
end
|
|
69
104
|
|
|
70
105
|
initializer "action_agent.assets", before: :append_assets_path do |app|
|
|
71
106
|
builds = root.join("app", "assets", "builds").to_s
|
data/lib/action_agent/version.rb
CHANGED
data/lib/action_agent.rb
CHANGED
|
@@ -20,6 +20,25 @@ module ActionAgent
|
|
|
20
20
|
global = defined?(::ActiveRecord::Base) ? ::ActiveRecord::Base.table_name_prefix : ""
|
|
21
21
|
"#{global}#{@table_name_prefix ||= "active_agent_"}"
|
|
22
22
|
end
|
|
23
|
+
|
|
24
|
+
# Which keyword the installed solid_agent uses to switch has_context's
|
|
25
|
+
# auto-context off: `contextable:` up to 0.1, `contextual:` from 0.2. The
|
|
26
|
+
# gemspec floor admits both, and passing the wrong one raises an
|
|
27
|
+
# ArgumentError deep inside a run rather than at boot — so
|
|
28
|
+
# AgentExecutionService asks rather than assumes.
|
|
29
|
+
#
|
|
30
|
+
# Covered by test/integration/solid_agent, which runs this engine against
|
|
31
|
+
# solid_agent's main branch as well as the released gem.
|
|
32
|
+
def solid_agent_auto_context_keyword
|
|
33
|
+
@solid_agent_auto_context_keyword ||= begin
|
|
34
|
+
keywords = ::SolidAgent::HasContext::ClassMethods
|
|
35
|
+
.instance_method(:has_context).parameters
|
|
36
|
+
.select { |type, _| [ :key, :keyreq ].include?(type) }
|
|
37
|
+
.map(&:last)
|
|
38
|
+
|
|
39
|
+
keywords.include?(:contextual) ? :contextual : :contextable
|
|
40
|
+
end
|
|
41
|
+
end
|
|
23
42
|
end
|
|
24
43
|
end
|
|
25
44
|
|