activeagent 0.0.1.alpha → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +153 -0
- data/Rakefile +3 -0
- data/lib/active_agent/README.md +21 -0
- data/lib/active_agent/action_prompt/README.md +44 -0
- data/lib/active_agent/action_prompt/base.rb +0 -0
- data/lib/active_agent/action_prompt/collector.rb +34 -0
- data/lib/active_agent/action_prompt/message.rb +44 -0
- data/lib/active_agent/action_prompt/prompt.rb +79 -0
- data/lib/active_agent/action_prompt.rb +127 -0
- data/lib/active_agent/base.rb +439 -0
- data/lib/active_agent/callbacks.rb +31 -0
- data/lib/active_agent/deprecator.rb +7 -0
- data/lib/active_agent/engine.rb +14 -0
- data/lib/active_agent/generation.rb +78 -0
- data/lib/active_agent/generation_job.rb +47 -0
- data/lib/active_agent/generation_methods.rb +60 -0
- data/lib/active_agent/generation_provider/README.md +17 -0
- data/lib/active_agent/generation_provider/base.rb +36 -0
- data/lib/active_agent/generation_provider/open_ai_provider.rb +68 -0
- data/lib/active_agent/generation_provider/response.rb +15 -0
- data/lib/active_agent/generation_provider.rb +63 -0
- data/lib/active_agent/inline_preview_interceptor.rb +60 -0
- data/lib/active_agent/log_subscriber.rb +44 -0
- data/lib/active_agent/operation.rb +13 -0
- data/lib/active_agent/parameterized.rb +66 -0
- data/lib/active_agent/preview.rb +133 -0
- data/lib/active_agent/prompt_helper.rb +19 -0
- data/lib/active_agent/queued_generation.rb +12 -0
- data/lib/active_agent/railtie.rb +89 -0
- data/lib/active_agent/rescuable.rb +34 -0
- data/lib/active_agent/service.rb +25 -0
- data/lib/active_agent/test_case.rb +125 -0
- data/lib/active_agent/version.rb +3 -0
- data/lib/active_agent.rb +27 -5
- data/lib/generators/active_agent/USAGE +18 -0
- data/lib/generators/active_agent/agent_generator.rb +63 -0
- data/lib/generators/active_agent/templates/action.html.erb.tt +0 -0
- data/lib/generators/active_agent/templates/action.json.jbuilder.tt +33 -0
- data/lib/generators/active_agent/templates/agent.rb.tt +14 -0
- data/lib/generators/active_agent/templates/agent_spec.rb.tt +0 -0
- data/lib/generators/active_agent/templates/agent_test.rb.tt +0 -0
- data/lib/generators/active_agent/templates/application_agent.rb.tt +4 -0
- metadata +128 -3
@@ -0,0 +1,36 @@
|
|
1
|
+
# lib/active_agent/generation_provider/base.rb
|
2
|
+
|
3
|
+
module ActiveAgent
|
4
|
+
module GenerationProvider
|
5
|
+
class Base
|
6
|
+
class GenerationProviderError < StandardError; end
|
7
|
+
attr_reader :client, :config, :prompt
|
8
|
+
|
9
|
+
def initialize(config)
|
10
|
+
@config = config
|
11
|
+
@prompt = nil
|
12
|
+
@response = nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def generate(prompt)
|
16
|
+
raise NotImplementedError, "Subclasses must implement the 'generate' method"
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def handle_response(response)
|
22
|
+
ActiveAgent::GenerationProvider::Response.new(message:, raw_response: response)
|
23
|
+
raise NotImplementedError, "Subclasses must implement the 'handle_response' method"
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def prompt_parameters
|
29
|
+
{
|
30
|
+
messages: @prompt.messages,
|
31
|
+
temperature: @config["temperature"] || 0.7
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# lib/active_agent/generation_provider/open_ai_provider.rb
|
2
|
+
|
3
|
+
require_relative "base"
|
4
|
+
require "openai"
|
5
|
+
require "active_agent/generation_provider/response"
|
6
|
+
|
7
|
+
module ActiveAgent
|
8
|
+
module GenerationProvider
|
9
|
+
class OpenAIProvider < Base
|
10
|
+
def initialize(config)
|
11
|
+
super
|
12
|
+
@api_key = config["api_key"]
|
13
|
+
@model_name = config["model"] || "gpt-4o-mini"
|
14
|
+
@client = OpenAI::Client.new(api_key: @api_key)
|
15
|
+
end
|
16
|
+
|
17
|
+
def generate(prompt)
|
18
|
+
@prompt = prompt
|
19
|
+
parameters = prompt_parameters.merge(model: @model_name)
|
20
|
+
|
21
|
+
# parameters[:instructions] = prompt.instructions.content if prompt.instructions.present?
|
22
|
+
|
23
|
+
parameters[:stream] = provider_stream if prompt.options[:stream] || config["stream"]
|
24
|
+
|
25
|
+
response = @client.chat(parameters: parameters)
|
26
|
+
handle_response(response)
|
27
|
+
rescue => e
|
28
|
+
raise GenerationProviderError, e.message
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def provider_stream
|
34
|
+
# prompt.config[:stream] will define a proc found in prompt at runtime
|
35
|
+
# config[:stream] will define a proc found in config stream would come from an Agent class's generate_with or stream_with method calls
|
36
|
+
agent_stream = prompt.config[:stream] || config["stream"]
|
37
|
+
proc do |chunk, bytesize|
|
38
|
+
# Provider parsing logic here
|
39
|
+
new_content = chunk.dig("choices", 0, "delta", "content")
|
40
|
+
message = @prompt.messages.find { |message| message.response_number == chunk.dig("choices", 0, "index") }
|
41
|
+
message.update(content: message.content + new_content) if new_content
|
42
|
+
|
43
|
+
# Call the custom stream_proc if provided
|
44
|
+
agent_stream.call(message) if agent_stream.respond_to?(:call)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def prompt_parameters
|
49
|
+
{
|
50
|
+
messages: @prompt.messages,
|
51
|
+
temperature: @config["temperature"] || 0.7,
|
52
|
+
tools: @prompt.actions
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
def handle_response(response)
|
57
|
+
message_json = response.dig("choices", 0, "message")
|
58
|
+
message = ActiveAgent::ActionPrompt::Message.new(
|
59
|
+
content: message_json["content"],
|
60
|
+
role: message_json["role"],
|
61
|
+
action_reqested: message_json["function_call"],
|
62
|
+
requested_actions: message_json["tool_calls"]
|
63
|
+
)
|
64
|
+
ActiveAgent::GenerationProvider::Response.new(prompt: prompt, message: message, raw_response: response)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveAgent
|
4
|
+
module GenerationProvider
|
5
|
+
class Response
|
6
|
+
attr_reader :message, :prompt, :raw_response
|
7
|
+
|
8
|
+
def initialize(prompt:, message:, raw_response: nil)
|
9
|
+
@message = message
|
10
|
+
@prompt = prompt
|
11
|
+
@raw_response = raw_response
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveAgent
|
4
|
+
module GenerationProvider
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
class_attribute :_generation_provider_name, instance_accessor: false, instance_predicate: false
|
9
|
+
class_attribute :_generation_provider, instance_accessor: false, instance_predicate: false
|
10
|
+
|
11
|
+
delegate :generation_provider, to: :class
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
def configuration(provider_name, **options)
|
16
|
+
config = ActiveAgent.config[provider_name.to_s] || ActiveAgent.config[ENV["RAILS_ENV"]][provider_name.to_s]
|
17
|
+
|
18
|
+
raise "Configuration not found for provider: #{provider_name}" unless config
|
19
|
+
|
20
|
+
config.merge!(options)
|
21
|
+
configure_provider(config)
|
22
|
+
end
|
23
|
+
|
24
|
+
def configure_provider(config)
|
25
|
+
require "active_agent/generation_provider/#{config["service"].underscore}_provider"
|
26
|
+
ActiveAgent::GenerationProvider.const_get("#{config["service"].camelize}Provider").new(config)
|
27
|
+
rescue LoadError
|
28
|
+
raise "Missing generation provider for #{config["service"].inspect}"
|
29
|
+
end
|
30
|
+
|
31
|
+
def generation_provider
|
32
|
+
self.generation_provider = :openai if _generation_provider.nil?
|
33
|
+
_generation_provider
|
34
|
+
end
|
35
|
+
|
36
|
+
def generation_provider_name
|
37
|
+
self.generation_provider = :openai if _generation_provider_name.nil?
|
38
|
+
_generation_provider_name
|
39
|
+
end
|
40
|
+
|
41
|
+
def generation_provider=(name_or_provider)
|
42
|
+
case name_or_provider
|
43
|
+
when Symbol, String
|
44
|
+
provider = configuration(name_or_provider)
|
45
|
+
assign_provider(name_or_provider.to_s, provider)
|
46
|
+
else
|
47
|
+
raise ArgumentError
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def assign_provider(provider_name, generation_provider)
|
54
|
+
self._generation_provider_name = provider_name
|
55
|
+
self._generation_provider = generation_provider
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def generation_provider
|
60
|
+
self.class.generation_provider
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "base64"
|
4
|
+
|
5
|
+
module ActiveAgent
|
6
|
+
# = Active Agent \InlinePreviewInterceptor
|
7
|
+
#
|
8
|
+
# Implements a agent preview interceptor that converts image tag src attributes
|
9
|
+
# that use inline cid: style URLs to data: style URLs so that they are visible
|
10
|
+
# when previewing an HTML prompt in a web browser.
|
11
|
+
#
|
12
|
+
# This interceptor is enabled by default. To disable it, delete it from the
|
13
|
+
# <tt>ActiveAgent::Base.preview_interceptors</tt> array:
|
14
|
+
#
|
15
|
+
# ActiveAgent::Base.preview_interceptors.delete(ActiveAgent::InlinePreviewInterceptor)
|
16
|
+
#
|
17
|
+
class InlinePreviewInterceptor
|
18
|
+
PATTERN = /src=(?:"cid:[^"]+"|'cid:[^']+')/i
|
19
|
+
|
20
|
+
include Base64
|
21
|
+
|
22
|
+
def self.previewing_prompt(context) # :nodoc:
|
23
|
+
new(context).transform!
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(context) # :nodoc:
|
27
|
+
@context = context
|
28
|
+
end
|
29
|
+
|
30
|
+
def transform! # :nodoc:
|
31
|
+
return context if html_part.blank?
|
32
|
+
|
33
|
+
html_part.body = html_part.decoded.gsub(PATTERN) do |match|
|
34
|
+
if part = find_part(match[9..-2])
|
35
|
+
%(src="#{data_url(part)}")
|
36
|
+
else
|
37
|
+
match
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
attr_reader :context
|
47
|
+
|
48
|
+
def html_part
|
49
|
+
@html_part ||= context.html_part
|
50
|
+
end
|
51
|
+
|
52
|
+
def data_url(part)
|
53
|
+
"data:#{part.mime_type};base64,#{strict_encode64(part.body.raw_source)}"
|
54
|
+
end
|
55
|
+
|
56
|
+
def find_part(cid)
|
57
|
+
context.all_parts.find { |p| p.attachment? && p.cid == cid }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# # frozen_string_literal: true
|
2
|
+
|
3
|
+
# require "active_support/log_subscriber"
|
4
|
+
|
5
|
+
# module ActiveAgent
|
6
|
+
# # = Active Agent \LogSubscriber
|
7
|
+
# #
|
8
|
+
# # Implements the ActiveSupport::LogSubscriber for logging notifications when
|
9
|
+
# # prompt is generated.
|
10
|
+
# class LogSubscriber < ActiveSupport::LogSubscriber
|
11
|
+
# # A prompt was generated.
|
12
|
+
# def deliver(event)
|
13
|
+
# info do
|
14
|
+
# if exception = event.payload[:exception_object]
|
15
|
+
# "Failed delivery of prompt #{event.payload[:message_id]} error_class=#{exception.class} error_message=#{exception.message.inspect}"
|
16
|
+
# elsif event.payload[:perform_deliveries]
|
17
|
+
# "Generated response for prompt #{event.payload[:message_id]} (#{event.duration.round(1)}ms)"
|
18
|
+
# else
|
19
|
+
# "Skipped generation of prompt #{event.payload[:message_id]} as `perform_generation` is false"
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
|
23
|
+
# debug { event.payload[:mail] }
|
24
|
+
# end
|
25
|
+
# subscribe_log_level :deliver, :debug
|
26
|
+
|
27
|
+
# # An email was generated.
|
28
|
+
# def process(event)
|
29
|
+
# debug do
|
30
|
+
# agent = event.payload[:agent]
|
31
|
+
# action = event.payload[:action]
|
32
|
+
# "#{agent}##{action}: processed outbound mail in #{event.duration.round(1)}ms"
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
# subscribe_log_level :process, :debug
|
36
|
+
|
37
|
+
# # Use the logger configured for ActionMailer::Base.
|
38
|
+
# def logger
|
39
|
+
# ActionMailer::Base.logger
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
|
44
|
+
# ActionMailer::LogSubscriber.attach_to :action_mailer
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module ActiveAgent
|
2
|
+
class Operation < AbstractController::Base
|
3
|
+
include AbstractController::Rendering
|
4
|
+
include ActionView::Rendering # Allows rendering of ERB templates without a view context tied to a request
|
5
|
+
append_view_path 'app/views' # Ensure the controller knows where to look for view templates
|
6
|
+
|
7
|
+
def process_tool(tool_name, params)
|
8
|
+
send(tool_name, params) # Dynamically calls the method corresponding to tool_name
|
9
|
+
rescue NoMethodError
|
10
|
+
"Tool not found: #{tool_name}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveAgent
|
4
|
+
module Parameterized
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
attr_writer :params
|
9
|
+
|
10
|
+
def params
|
11
|
+
@params ||= {}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
def with(params)
|
17
|
+
ActiveAgent::Parameterized::Agent.new(self, params)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Agent
|
22
|
+
def initialize(agent, params)
|
23
|
+
@agent = agent
|
24
|
+
@params = params
|
25
|
+
end
|
26
|
+
|
27
|
+
def method_missing(method_name, ...)
|
28
|
+
if @agent.public_instance_methods.include?(method_name)
|
29
|
+
ActiveAgent::Parameterized::Generation.new(@agent, method_name, @params, ...)
|
30
|
+
else
|
31
|
+
super
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def respond_to_missing?(method, include_all = false)
|
36
|
+
@agent.respond_to?(method, include_all)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class Generation < ActiveAgent::Generation
|
41
|
+
def initialize(agent_class, action, params, ...)
|
42
|
+
super(agent_class, action, ...)
|
43
|
+
@params = params
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def processed_agent
|
49
|
+
@processed_agent ||= @agent_class.new.tap do |agent|
|
50
|
+
agent.params = @params
|
51
|
+
agent.process @action, *@args
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def enqueue_generation(generation_method, options = {})
|
56
|
+
if processed?
|
57
|
+
super
|
58
|
+
else
|
59
|
+
@agent_class.generation_job.set(options).perform_later(
|
60
|
+
@agent_class.name, @action.to_s, params: @params, args: @args
|
61
|
+
)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/descendants_tracker"
|
4
|
+
|
5
|
+
module ActiveAgent
|
6
|
+
module Previews # :nodoc:
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
mattr_accessor :preview_paths, instance_writer: false, default: []
|
11
|
+
|
12
|
+
mattr_accessor :show_previews, instance_writer: false
|
13
|
+
|
14
|
+
mattr_accessor :preview_interceptors, instance_writer: false, default: [ActiveAgent::InlinePreviewInterceptor]
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
# Register one or more Interceptors which will be called before prompt is previewed.
|
19
|
+
def register_preview_interceptors(*interceptors)
|
20
|
+
interceptors.flatten.compact.each { |interceptor| register_preview_interceptor(interceptor) }
|
21
|
+
end
|
22
|
+
|
23
|
+
# Unregister one or more previously registered Interceptors.
|
24
|
+
def unregister_preview_interceptors(*interceptors)
|
25
|
+
interceptors.flatten.compact.each { |interceptor| unregister_preview_interceptor(interceptor) }
|
26
|
+
end
|
27
|
+
|
28
|
+
# Register an Interceptor which will be called before prompt is previewed.
|
29
|
+
# Either a class or a string can be passed in as the Interceptor. If a
|
30
|
+
# string is passed in it will be constantized.
|
31
|
+
def register_preview_interceptor(interceptor)
|
32
|
+
preview_interceptor = interceptor_class_for(interceptor)
|
33
|
+
|
34
|
+
unless preview_interceptors.include?(preview_interceptor)
|
35
|
+
preview_interceptors << preview_interceptor
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Unregister a previously registered Interceptor.
|
40
|
+
# Either a class or a string can be passed in as the Interceptor. If a
|
41
|
+
# string is passed in it will be constantized.
|
42
|
+
def unregister_preview_interceptor(interceptor)
|
43
|
+
preview_interceptors.delete(interceptor_class_for(interceptor))
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def interceptor_class_for(interceptor)
|
49
|
+
case interceptor
|
50
|
+
when String, Symbol
|
51
|
+
interceptor.to_s.camelize.constantize
|
52
|
+
else
|
53
|
+
interceptor
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class Preview
|
60
|
+
extend ActiveSupport::DescendantsTracker
|
61
|
+
|
62
|
+
attr_reader :params
|
63
|
+
|
64
|
+
def initialize(params = {})
|
65
|
+
@params = params
|
66
|
+
end
|
67
|
+
|
68
|
+
class << self
|
69
|
+
# Returns all agent preview classes.
|
70
|
+
def all
|
71
|
+
load_previews if descendants.empty?
|
72
|
+
descendants.sort_by { |agent| agent.name.titleize }
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns the prompt object for the given context. The registered preview
|
76
|
+
# interceptors will be informed so that they can transform the message
|
77
|
+
# as they would if the mail was actually being delivered.
|
78
|
+
def call(context, params = {})
|
79
|
+
preview = new(params)
|
80
|
+
prompt = preview.public_send(context)
|
81
|
+
inform_preview_interceptors(prompt)
|
82
|
+
prompt
|
83
|
+
end
|
84
|
+
|
85
|
+
# Returns all of the available prompt previews.
|
86
|
+
def prompts
|
87
|
+
public_instance_methods(false).map(&:to_s).sort
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns +true+ if the prompt exists.
|
91
|
+
def prompt_exists?(prompt)
|
92
|
+
prompts.include?(prompt)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Returns +true+ if the preview exists.
|
96
|
+
def exists?(preview)
|
97
|
+
all.any? { |p| p.preview_name == preview }
|
98
|
+
end
|
99
|
+
|
100
|
+
# Find a agent preview by its underscored class name.
|
101
|
+
def find(preview)
|
102
|
+
all.find { |p| p.preview_name == preview }
|
103
|
+
end
|
104
|
+
|
105
|
+
# Returns the underscored name of the agent preview without the suffix.
|
106
|
+
def preview_name
|
107
|
+
name.delete_suffix("Preview").underscore
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def load_previews
|
113
|
+
preview_paths.each do |preview_path|
|
114
|
+
Dir["#{preview_path}/**/*_preview.rb"].sort.each { |file| require file }
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def preview_paths
|
119
|
+
Base.preview_paths
|
120
|
+
end
|
121
|
+
|
122
|
+
def show_previews
|
123
|
+
Base.show_previews
|
124
|
+
end
|
125
|
+
|
126
|
+
def inform_preview_interceptors(context)
|
127
|
+
Base.preview_interceptors.each do |interceptor|
|
128
|
+
interceptor.previewing_prompt(context)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveAgent
|
4
|
+
# = Active Agent's Action Prompt \PromptHelper
|
5
|
+
#
|
6
|
+
# Provides helper methods for ActiveAgent::Base that can be used for easily
|
7
|
+
# formatting prompts, accessing agent or prompt instances.
|
8
|
+
module PromptHelper
|
9
|
+
# Access the agent instance.
|
10
|
+
def agent
|
11
|
+
@_controller
|
12
|
+
end
|
13
|
+
|
14
|
+
# Access the prompt instance.
|
15
|
+
def context
|
16
|
+
@_context
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveAgent
|
4
|
+
module QueuedGeneration
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
class_attribute :generation_job, default: ::ActiveAgent::GenerationJob
|
9
|
+
class_attribute :generate_later_queue_name, default: :agents
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_job/railtie"
|
4
|
+
require "active_agent"
|
5
|
+
require "active_agent/engine"
|
6
|
+
require "rails"
|
7
|
+
require "abstract_controller/railties/routes_helpers"
|
8
|
+
|
9
|
+
module ActiveAgent
|
10
|
+
class Railtie < Rails::Railtie # :nodoc:
|
11
|
+
config.active_agent = ActiveSupport::OrderedOptions.new
|
12
|
+
config.active_agent.preview_paths = []
|
13
|
+
config.eager_load_namespaces << ::ActiveAgent
|
14
|
+
|
15
|
+
initializer "active_agent.deprecator", before: :load_environment_config do |app|
|
16
|
+
app.deprecators[:active_agent] = ActiveAgent.deprecator
|
17
|
+
end
|
18
|
+
|
19
|
+
initializer "active_agent.logger" do
|
20
|
+
ActiveSupport.on_load(:active_agent) { self.logger ||= Rails.logger }
|
21
|
+
end
|
22
|
+
|
23
|
+
initializer "active_agent.set_configs" do |app|
|
24
|
+
paths = app.config.paths
|
25
|
+
options = app.config.active_agent
|
26
|
+
|
27
|
+
options.assets_dir ||= paths["public"].first
|
28
|
+
options.javascripts_dir ||= paths["public/javascripts"].first
|
29
|
+
options.stylesheets_dir ||= paths["public/stylesheets"].first
|
30
|
+
options.show_previews = Rails.env.development? if options.show_previews.nil?
|
31
|
+
options.cache_store ||= Rails.cache
|
32
|
+
options.preview_paths |= ["#{Rails.root}/test/agents/previews"]
|
33
|
+
|
34
|
+
# make sure readers methods get compiled
|
35
|
+
options.asset_host ||= app.config.asset_host
|
36
|
+
options.relative_url_root ||= app.config.relative_url_root
|
37
|
+
|
38
|
+
ActiveSupport.on_load(:active_agent) do
|
39
|
+
include AbstractController::UrlFor
|
40
|
+
extend ::AbstractController::Railties::RoutesHelpers.with(app.routes, false)
|
41
|
+
include app.routes.mounted_helpers
|
42
|
+
|
43
|
+
register_interceptors(options.delete(:interceptors))
|
44
|
+
register_preview_interceptors(options.delete(:preview_interceptors))
|
45
|
+
register_observers(options.delete(:observers))
|
46
|
+
self.view_paths = ["#{Rails.root}/app/views"]
|
47
|
+
self.preview_paths |= options[:preview_paths]
|
48
|
+
|
49
|
+
if delivery_job = options.delete(:delivery_job)
|
50
|
+
self.delivery_job = delivery_job.constantize
|
51
|
+
end
|
52
|
+
|
53
|
+
if options.smtp_settings
|
54
|
+
self.smtp_settings = options.smtp_settings
|
55
|
+
end
|
56
|
+
|
57
|
+
options.each { |k, v| send(:"#{k}=", v) }
|
58
|
+
end
|
59
|
+
|
60
|
+
ActiveSupport.on_load(:action_dispatch_integration_test) do
|
61
|
+
include ActiveAgent::TestHelper
|
62
|
+
include ActiveAgent::TestCase::ClearTestDeliveries
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
initializer "active_agent.set_autoload_paths", before: :set_autoload_paths do |app|
|
67
|
+
options = app.config.active_agent
|
68
|
+
# app.config.paths["test/agents/previews"].concat(options.preview_paths)
|
69
|
+
end
|
70
|
+
|
71
|
+
initializer "active_agent.compile_config_methods" do
|
72
|
+
ActiveSupport.on_load(:active_agent) do
|
73
|
+
config.compile_methods! if config.respond_to?(:compile_methods!)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
config.after_initialize do |app|
|
78
|
+
options = app.config.active_agent
|
79
|
+
|
80
|
+
if options.show_previews
|
81
|
+
app.routes.prepend do
|
82
|
+
get "/rails/agents" => "rails/agents#index", :internal => true
|
83
|
+
get "/rails/agents/download/*path" => "rails/agents#download", :internal => true
|
84
|
+
get "/rails/agents/*path" => "rails/agents#preview", :internal => true
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveAgent # :nodoc:
|
4
|
+
# = Active Agent \Rescuable
|
5
|
+
#
|
6
|
+
# Provides
|
7
|
+
# {rescue_from}[rdoc-ref:ActiveSupport::Rescuable::ClassMethods#rescue_from]
|
8
|
+
# for agents. Wraps agent action processing, generation job processing, and prompt
|
9
|
+
# generation to handle configured errors.
|
10
|
+
module Rescuable
|
11
|
+
extend ActiveSupport::Concern
|
12
|
+
include ActiveSupport::Rescuable
|
13
|
+
|
14
|
+
class_methods do
|
15
|
+
def handle_exception(exception) # :nodoc:
|
16
|
+
rescue_with_handler(exception) || raise(exception)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def handle_exceptions # :nodoc:
|
21
|
+
yield
|
22
|
+
rescue => exception
|
23
|
+
rescue_with_handler(exception) || raise
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def process(...)
|
29
|
+
handle_exceptions do
|
30
|
+
super
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveAgent
|
4
|
+
class Service
|
5
|
+
extend ActiveSupport::Autoload
|
6
|
+
autoload :Configurator
|
7
|
+
attr_accessor :name
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def configure(service_name, configurations)
|
11
|
+
Configurator.build(service_name, configurations)
|
12
|
+
end
|
13
|
+
|
14
|
+
def build(configurator:, name:, service: nil, **service_config) # :nodoc:
|
15
|
+
new(**service_config).tap do |service_instance|
|
16
|
+
service_instance.name = name
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def generate(...)
|
22
|
+
raise NotImplementedError
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|