action_ai 0.3.0 → 0.4.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 +27 -0
- data/README.rdoc +45 -19
- data/lib/action_ai/agent.rb +53 -58
- data/lib/action_ai/model_schema/active_record.rb +14 -0
- data/lib/action_ai/model_schema.rb +1 -1
- data/lib/action_ai/version.rb +1 -1
- data/lib/rails/generators/ai/templates/agent.rb.tt +4 -0
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3fa6d979d2013779ecfe3932a2c1bb04158dd99072ef5eb4b802f2ece910b34d
|
|
4
|
+
data.tar.gz: 46cb4d1ad9cca9f63c7af8d575abe3ad23fef1359d2f5d445617662e071b1ef6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0de59f1a428c9be0cd4e1926a8f1d59f1cca46c8fbdc77248fe34ab21c755412c39db3bfabcaa86855be878e707cfdb61e2d74a983c7ae2625de50eed3b96184
|
|
7
|
+
data.tar.gz: 4e77dc58ce51dc7a8d9027cabdab9465b82b3e9abccce7bbfb3591f8fd12cd36dbd4f28953118ec41ec3bc9f64b0569045d3e191c362ba8dbdcb9decaa9312e6
|
data/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,33 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
5
|
|
|
6
|
+
## [0.4.0] - 2026-10-20
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
|
|
10
|
+
- Moved agent-level configuration to RubyLLM agents.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
#### Configuration
|
|
15
|
+
|
|
16
|
+
- RubyLLM agent configuration DSL support on `ActionAI::Agent`: class methods `model`, `instructions`, `tools`, `tool_options`, `server_tools`, `thinking`, `citations`, `caching`, `compaction`, `provider_options`, `headers`, `schema`, `fallbacks`, `context`, `chat_model`, `inputs`, `temperature`, `max_output_tokens`, and `end_user` configure a private, internally managed `RubyLLM::Agent` subclass.
|
|
17
|
+
`chat` is built via that agent’s `.chat` instead of plain `RubyLLM.chat`.
|
|
18
|
+
- Parameters set through `ActionAI::Parameterized` are forwarded as the agent’s runtime `inputs`.
|
|
19
|
+
|
|
20
|
+
#### Schemas
|
|
21
|
+
|
|
22
|
+
- Consider Active Record _stored attributes_ for schema inference.
|
|
23
|
+
The type is `string` because `ActiveRecord::Store` doesn’t provide type casting. The same is true for the attributes a model will get from a user-filled form, so the behavior is consistent. One should override attribute setters for type casting if needed.
|
|
24
|
+
|
|
25
|
+
### Removed
|
|
26
|
+
|
|
27
|
+
#### Configuration
|
|
28
|
+
|
|
29
|
+
- `default`/`default_options=` class method and `default_params` class attribute.
|
|
30
|
+
Use the RubyLLM agent DSL (`model`, `instructions`, and so on) instead.
|
|
31
|
+
|
|
32
|
+
|
|
6
33
|
## [0.3.0] - 2026-10-13
|
|
7
34
|
|
|
8
35
|
### Changed
|
data/README.rdoc
CHANGED
|
@@ -22,7 +22,7 @@ automatically at the end of the action unless you have already called it.
|
|
|
22
22
|
This can be as simple as:
|
|
23
23
|
|
|
24
24
|
class Generator < ApplicationAI
|
|
25
|
-
|
|
25
|
+
model "gpt-6.1-sol"
|
|
26
26
|
|
|
27
27
|
def code(task, language)
|
|
28
28
|
@task = task
|
|
@@ -88,34 +88,60 @@ You can also chain multiple agent actions to compose a small workflow before rea
|
|
|
88
88
|
WorkflowAgent.collect("release notes").refine(style: "bullet list").content
|
|
89
89
|
# => collected release notes refined as bullet list
|
|
90
90
|
|
|
91
|
-
==
|
|
91
|
+
== Configuration
|
|
92
|
+
|
|
93
|
+
Your agent class uses the full +RubyLLM::Agent+ configuration DSL
|
|
94
|
+
directly. You never need to define or reference a +RubyLLM::Agent+
|
|
95
|
+
subclass yourself; just call +model+, +instructions+, +tools+, +schema+, and
|
|
96
|
+
the other +RubyLLM::Agent+ class macros right on your Action AI agent:
|
|
97
|
+
|
|
98
|
+
class Generator < ApplicationAI
|
|
99
|
+
model "gpt-6.1-sol"
|
|
100
|
+
instructions "You are a concise support assistant."
|
|
101
|
+
tools SearchDocs, LookupAccount
|
|
102
|
+
|
|
103
|
+
def code(task, language)
|
|
104
|
+
@task = task
|
|
105
|
+
@language = language
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
If you declare +inputs+, use ActionAI::Parameterized to supply them at call
|
|
110
|
+
time; the parameters given to +with+ are forwarded as the agent's runtime
|
|
111
|
+
inputs:
|
|
92
112
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
Finally, it is also possible to pass in a
|
|
99
|
-
Proc that will get evaluated when it is needed.
|
|
113
|
+
class Generator < ApplicationAI
|
|
114
|
+
inputs :workspace
|
|
115
|
+
|
|
116
|
+
instructions { "You are helping #{workspace.name}" }
|
|
117
|
+
end
|
|
100
118
|
|
|
101
|
-
|
|
102
|
-
same key in your agent method.
|
|
119
|
+
Generator.with(workspace:).code("Parse CSV", :ruby)
|
|
103
120
|
|
|
104
|
-
|
|
121
|
+
Subclasses inherit their parent's RubyLLM agent configuration and can extend
|
|
122
|
+
or override it, the same way +RubyLLM::Agent+ subclasses do:
|
|
123
|
+
|
|
124
|
+
class ApplicationAI < ActionAI::Agent
|
|
125
|
+
model "gpt-6-luna", provider: :openai
|
|
126
|
+
end
|
|
105
127
|
|
|
106
128
|
class Generator < ApplicationAI
|
|
107
|
-
|
|
129
|
+
model { Current.user.preferred_model }
|
|
108
130
|
end
|
|
109
131
|
|
|
110
|
-
|
|
132
|
+
The Agent class has a few settings unrelated to the RubyLLM agent DSL that are specified on the class level.
|
|
133
|
+
Here's an example:
|
|
134
|
+
|
|
135
|
+
ActionAI::Agent.execute_later_queue_name = :ai_agents
|
|
111
136
|
|
|
112
|
-
|
|
137
|
+
* +logger+ — the logger is used for generating information on prompt execution if available.
|
|
138
|
+
Can be set to +nil+ for no logging. Compatible with both Ruby's own +Logger+ and Log4r loggers.
|
|
113
139
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
model: "gpt-4o-mini"
|
|
117
|
-
}
|
|
140
|
+
* +execution_job+ — The job class used with +later+. Agents can set this to use a
|
|
141
|
+
custom execution job. Defaults to +ActionAI::ExecutionJob+.
|
|
118
142
|
|
|
143
|
+
* +execute_later_queue_name+ — The queue name used by +later+ with the default
|
|
144
|
+
+execution_job+. Agents can set this to use a custom queue name.
|
|
119
145
|
|
|
120
146
|
== Structured output (data schemas)
|
|
121
147
|
|
data/lib/action_ai/agent.rb
CHANGED
|
@@ -4,6 +4,9 @@ require "ruby_llm"
|
|
|
4
4
|
require "active_support/core_ext/string/inflections"
|
|
5
5
|
require "active_support/core_ext/hash/except"
|
|
6
6
|
require "active_support/core_ext/module/anonymous"
|
|
7
|
+
require "active_support/core_ext/module/delegation"
|
|
8
|
+
require "active_support/core_ext/object/try"
|
|
9
|
+
require "active_support/core_ext/object/with_options"
|
|
7
10
|
require "memery"
|
|
8
11
|
|
|
9
12
|
require "action_ai/log_subscriber"
|
|
@@ -26,12 +29,11 @@ module ActionAI
|
|
|
26
29
|
# the prompt views, options on the AI model used such as the <tt>:model</tt> ID, and attachments.
|
|
27
30
|
#
|
|
28
31
|
# class ApplicationAI < ActionAI::Agent
|
|
29
|
-
#
|
|
30
|
-
# provider: :openai
|
|
32
|
+
# model "gpt-6-luna", provider: :openai
|
|
31
33
|
# end
|
|
32
34
|
#
|
|
33
35
|
# class Generator < ApplicationAI
|
|
34
|
-
#
|
|
36
|
+
# model "gpt-6.1-sol"
|
|
35
37
|
#
|
|
36
38
|
# def code(task, language)
|
|
37
39
|
# @task = task
|
|
@@ -155,33 +157,40 @@ module ActionAI
|
|
|
155
157
|
# end
|
|
156
158
|
# end
|
|
157
159
|
#
|
|
158
|
-
# ==
|
|
160
|
+
# == Configuration
|
|
159
161
|
#
|
|
160
|
-
# Action AI
|
|
161
|
-
#
|
|
162
|
+
# Action AI agents configure their model, instructions, tools, and other
|
|
163
|
+
# chat options using the +RubyLLM::Agent+ class DSL directly. You never
|
|
164
|
+
# need to define or reference a +RubyLLM::Agent+ subclass yourself; just
|
|
165
|
+
# call +model+, +instructions+, +tools+, +schema+, and the other
|
|
166
|
+
# +RubyLLM::Agent+ class macros right on your +ActionAI::Agent+ subclass:
|
|
162
167
|
#
|
|
163
168
|
# class Generator < ApplicationAI
|
|
164
|
-
#
|
|
169
|
+
# model "gpt-6.1-sol"
|
|
170
|
+
# instructions "You are a concise support assistant."
|
|
171
|
+
# tools SearchDocs, LookupAccount
|
|
165
172
|
# end
|
|
166
173
|
#
|
|
167
|
-
#
|
|
174
|
+
# Under the hood, Action AI manages a private +RubyLLM::Agent+ subclass for you
|
|
175
|
+
# and builds +chat+ from it, so instructions, tools, schema, fallbacks, caching,
|
|
176
|
+
# and every other declaration are honored exactly as documented on
|
|
177
|
+
# <tt>RubyLLM::Agent</tt>. Any params set through ActionAI::Parameterized
|
|
178
|
+
# (<tt>Generator.with(...)</tt>) are forwarded as its runtime +inputs+.
|
|
168
179
|
#
|
|
169
|
-
#
|
|
170
|
-
# so
|
|
180
|
+
# Configuration is inherited the same way +RubyLLM::Agent+ subclasses inherit
|
|
181
|
+
# it, so a shared +ApplicationAI+ base class can declare a default +model+ and
|
|
182
|
+
# +instructions+ that every agent subclass then extends or overrides:
|
|
171
183
|
#
|
|
172
|
-
# class
|
|
173
|
-
#
|
|
174
|
-
# api_key: proc { Current.user.ai_api_key }
|
|
184
|
+
# class ApplicationAI < ActionAI::Agent
|
|
185
|
+
# model "gpt-6-luna", provider: :openai
|
|
175
186
|
# end
|
|
176
187
|
#
|
|
177
|
-
#
|
|
178
|
-
#
|
|
179
|
-
#
|
|
180
|
-
#
|
|
181
|
-
# It is also possible to set these default options that will be used in all agents through
|
|
182
|
-
# the <tt>default_options=</tt> configuration in <tt>config/application.rb</tt>:
|
|
188
|
+
# class Generator < ApplicationAI
|
|
189
|
+
# model { Current.user.preferred_model }
|
|
190
|
+
# end
|
|
183
191
|
#
|
|
184
|
-
#
|
|
192
|
+
# ::rescue_from continues to handle agent-action level exceptions; use it as
|
|
193
|
+
# usual, there's no separate chat-level rescue mechanism to reach for.
|
|
185
194
|
#
|
|
186
195
|
# == \Callbacks
|
|
187
196
|
#
|
|
@@ -265,9 +274,6 @@ module ActionAI
|
|
|
265
274
|
# These options are specified on the class level, like
|
|
266
275
|
# <tt>ActionAI::Agent.raise_execution_errors = true</tt>
|
|
267
276
|
#
|
|
268
|
-
# * <tt>default_options</tt> - You can pass this in at a class level as well as within the class itself as
|
|
269
|
-
# per the above section.
|
|
270
|
-
#
|
|
271
277
|
# * <tt>logger</tt> - the logger is used for generating information on prompt execution if available.
|
|
272
278
|
# Can be set to +nil+ for no logging. Compatible with both Ruby's own +Logger+ and Log4r loggers.
|
|
273
279
|
#
|
|
@@ -304,11 +310,9 @@ module ActionAI
|
|
|
304
310
|
|
|
305
311
|
helper ActionAI::PromptHelper
|
|
306
312
|
|
|
307
|
-
class_attribute :default_params, default: {
|
|
308
|
-
# none
|
|
309
|
-
}.freeze
|
|
310
|
-
|
|
311
313
|
class << self
|
|
314
|
+
include Memery
|
|
315
|
+
|
|
312
316
|
# Returns the name of the current agent. This method is also being used as a path for a view lookup.
|
|
313
317
|
# If this is an anonymous agent, this method will return +anonymous+ instead.
|
|
314
318
|
def agent_name
|
|
@@ -318,18 +322,18 @@ module ActionAI
|
|
|
318
322
|
attr_writer :agent_name
|
|
319
323
|
alias :controller_path :agent_name
|
|
320
324
|
|
|
321
|
-
#
|
|
322
|
-
#
|
|
323
|
-
#
|
|
324
|
-
def
|
|
325
|
-
|
|
326
|
-
default_params
|
|
325
|
+
# The private, per-agent-class +RubyLLM::Agent+ subclass,
|
|
326
|
+
# lazily created and configured via its own class macros
|
|
327
|
+
# (+model+, +instructions+, +tools+, +schema+, and so on).
|
|
328
|
+
memoize def agent_class
|
|
329
|
+
Class.new superclass.try(:agent_class) || RubyLLM::Agent
|
|
327
330
|
end
|
|
328
|
-
alias :default_options= :default
|
|
329
331
|
|
|
330
332
|
private
|
|
331
333
|
def method_missing(method_name, ...)
|
|
332
|
-
if
|
|
334
|
+
if agent_class.respond_to? method_name # configuration
|
|
335
|
+
agent_class.public_send(method_name, ...)
|
|
336
|
+
elsif action_methods.include? method_name.name # actions
|
|
333
337
|
Interaction.new(self, method_name, ...)
|
|
334
338
|
else
|
|
335
339
|
super
|
|
@@ -337,13 +341,22 @@ module ActionAI
|
|
|
337
341
|
end
|
|
338
342
|
|
|
339
343
|
def respond_to_missing?(method, include_all = false)
|
|
340
|
-
|
|
344
|
+
agent_class.respond_to? method or
|
|
345
|
+
action_methods.include? method.name or
|
|
346
|
+
super
|
|
341
347
|
end
|
|
342
348
|
end
|
|
343
349
|
|
|
344
350
|
attr_internal :message
|
|
345
351
|
|
|
346
|
-
memoize def
|
|
352
|
+
memoize def agent = agent_class.new(**params
|
|
353
|
+
.slice(*agent_class.inputs)
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
with_options private: true do
|
|
357
|
+
delegate :agent_class, to: :class
|
|
358
|
+
delegate :chat, to: :agent
|
|
359
|
+
end
|
|
347
360
|
|
|
348
361
|
def process(method_name, *args) # :nodoc:
|
|
349
362
|
payload = {
|
|
@@ -379,10 +392,10 @@ module ActionAI
|
|
|
379
392
|
#
|
|
380
393
|
# * +:with+ - Array of file paths or URLs to attach to the prompt.
|
|
381
394
|
#
|
|
382
|
-
# You can set default model options using the ::
|
|
395
|
+
# You can set default model options using the ::model class method:
|
|
383
396
|
#
|
|
384
397
|
# class Generator < ActionAI::Agent
|
|
385
|
-
#
|
|
398
|
+
# model "gpt-6.1-sol", provider: :openai
|
|
386
399
|
# end
|
|
387
400
|
#
|
|
388
401
|
# It will find a template in the view paths using by default the agent name and the
|
|
@@ -392,7 +405,7 @@ module ActionAI
|
|
|
392
405
|
# For example:
|
|
393
406
|
#
|
|
394
407
|
# class Generator < ActionAI::Agent
|
|
395
|
-
#
|
|
408
|
+
# model "gpt-6.1-sol"
|
|
396
409
|
#
|
|
397
410
|
# def code(task, language)
|
|
398
411
|
# @task = task
|
|
@@ -423,24 +436,6 @@ module ActionAI
|
|
|
423
436
|
false
|
|
424
437
|
end
|
|
425
438
|
|
|
426
|
-
def apply_defaults(config = {})
|
|
427
|
-
default_values = self.class.default.except(*config.keys).transform_values do |value|
|
|
428
|
-
compute_default(value)
|
|
429
|
-
end
|
|
430
|
-
|
|
431
|
-
config.reverse_merge(default_values)
|
|
432
|
-
end
|
|
433
|
-
|
|
434
|
-
def compute_default(value)
|
|
435
|
-
return value unless value.is_a?(Proc)
|
|
436
|
-
|
|
437
|
-
if value.arity == 1
|
|
438
|
-
instance_exec(self, &value)
|
|
439
|
-
else
|
|
440
|
-
instance_exec(&value)
|
|
441
|
-
end
|
|
442
|
-
end
|
|
443
|
-
|
|
444
439
|
def use_attachments
|
|
445
440
|
attachments.presence
|
|
446
441
|
.tap { @attachments = nil } # reset
|
|
@@ -35,6 +35,20 @@ module ActionAI
|
|
|
35
35
|
locking_column,
|
|
36
36
|
*cache_columns,
|
|
37
37
|
].map(&:to_sym))
|
|
38
|
+
.merge(stored_attributes.values.reduce(:|).to_h do |name|
|
|
39
|
+
[ name, {
|
|
40
|
+
type: :string,
|
|
41
|
+
description: try(:human_attribute_name, name),
|
|
42
|
+
required: attribute_required?(name),
|
|
43
|
+
default: attribute_default(name),
|
|
44
|
+
enum: attribute_enum(name),
|
|
45
|
+
const: attribute_const(name),
|
|
46
|
+
pattern: attribute_format(name),
|
|
47
|
+
|
|
48
|
+
**attribute_length(name),
|
|
49
|
+
**attribute_boundaries(name),
|
|
50
|
+
}.compact ]
|
|
51
|
+
end)
|
|
38
52
|
.merge(schema_reflections)
|
|
39
53
|
|
|
40
54
|
memoize def schema_reflections = nested_attributes_options
|
data/lib/action_ai/version.rb
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
<% module_namespacing do -%>
|
|
2
2
|
class <%= class_name %> < ApplicationAI
|
|
3
|
+
# Uncomment to configure model/instructions/tools using the RubyLLM agent DSL:
|
|
4
|
+
# model "gpt-6.1-sol"
|
|
5
|
+
# instructions "You are a helpful assistant."
|
|
6
|
+
|
|
3
7
|
<% actions.each_with_index do |action, index| -%>
|
|
4
8
|
<% if index != 0 -%>
|
|
5
9
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: action_ai
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alexander Senko
|
|
@@ -182,8 +182,8 @@ licenses:
|
|
|
182
182
|
- MIT
|
|
183
183
|
metadata:
|
|
184
184
|
bug_tracker_uri: "https://github.com/Alexander-Senko/action_ai/issues"
|
|
185
|
-
changelog_uri: "https://github.com/Alexander-Senko/action_ai/blob/v0.
|
|
186
|
-
source_code_uri: "https://github.com/Alexander-Senko/action_ai/tree/v0.
|
|
185
|
+
changelog_uri: "https://github.com/Alexander-Senko/action_ai/blob/v0.4.0/CHANGELOG.md"
|
|
186
|
+
source_code_uri: "https://github.com/Alexander-Senko/action_ai/tree/v0.4.0"
|
|
187
187
|
rubygems_mfa_required: "true"
|
|
188
188
|
rdoc_options: []
|
|
189
189
|
require_paths:
|