activeagent 0.0.1.alpha → 0.1.1
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 +14 -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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 67bb51f0a98d5b2291b53e30b629adae069a3f24e19edfc74702a918bc3c1086
|
4
|
+
data.tar.gz: 04cb6ba122dac00b145c3baf1f54157d5e443470639d16f22d614bfaaba92b49
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9835738ac1bb5570c900dd9a9942591a28c8b17b6ce26afbd622f764d5bb4ee178d0fc1089824a84d924824cc0c9a291680b1a1b1df5e41f5bfff1e91700a382
|
7
|
+
data.tar.gz: 78e3fd95e02a950cd2c47bf52891156d861f926374333efaeee51578fc0c96f65a94a123ec104cd0a5c32c9ea130ac85075a9904478191904936ea80381df714
|
data/README.md
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
# ActiveAgent
|
2
|
+
|
3
|
+
ActiveAgent is a Rails framework for creating and managing AI agents. It provides a structured way to interact with AI services through agents that can generate text, images, speech-to-text, and text-to-speech. It includes modules for defining prompts, actions, and rendering generative UI, as well as scaling with asynchronous jobs and streaming.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'active_agent'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
```sh
|
16
|
+
bundle install
|
17
|
+
```
|
18
|
+
## Getting Started
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
### Generating an Agent
|
23
|
+
```
|
24
|
+
rails generate agent inventory search
|
25
|
+
```
|
26
|
+
|
27
|
+
This will generate the following files:
|
28
|
+
```
|
29
|
+
app/agents/application_agent.rb
|
30
|
+
app/agents/inventory_agent.rb
|
31
|
+
app/views/inventory_agent/search.text.erb
|
32
|
+
app/views/inventory_agent/search.json.jbuilder
|
33
|
+
```
|
34
|
+
|
35
|
+
### Define Agents
|
36
|
+
|
37
|
+
Agents are the core of ActiveAgent. An agent takes prompts and can perform actions to generate content. Agents are defined by a simple Ruby class that inherits from `ActiveAgent::Base` and are located in the `app/agents` directory.
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
#
|
41
|
+
|
42
|
+
inventory_agent.rb
|
43
|
+
|
44
|
+
|
45
|
+
class InventoryAgent < ActiveAgent::Base
|
46
|
+
generate_with :openai, model: 'gpt-4o-mini', temperature: 0.5, instructions: :inventory_operations
|
47
|
+
|
48
|
+
def search
|
49
|
+
@items = Item.search(params[:query])
|
50
|
+
end
|
51
|
+
|
52
|
+
def inventory_operations
|
53
|
+
@organization = Organization.find(params[:account_id])
|
54
|
+
prompt
|
55
|
+
end
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
### Interact with AI Services
|
60
|
+
|
61
|
+
ActiveAgent allows you to interact with various AI services to generate text, images, speech-to-text, and text-to-speech.
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
class SupportAgent < ActiveAgent::Base
|
65
|
+
generate_with :openai, model: 'gpt-4o-mini', instructions: :instructions
|
66
|
+
|
67
|
+
def perform(content, context)
|
68
|
+
@content = content
|
69
|
+
@context = context
|
70
|
+
end
|
71
|
+
|
72
|
+
def generate_message
|
73
|
+
provider_instance.generate(self)
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def after_generate
|
79
|
+
broadcast_message
|
80
|
+
end
|
81
|
+
|
82
|
+
def broadcast_message
|
83
|
+
broadcast_append_later_to(
|
84
|
+
broadcast_stream,
|
85
|
+
target: broadcast_target,
|
86
|
+
partial: 'support_agent/message',
|
87
|
+
locals: { message: @message }
|
88
|
+
)
|
89
|
+
end
|
90
|
+
|
91
|
+
def broadcast_stream
|
92
|
+
"#{dom_id(@chat)}_messages"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
```
|
96
|
+
|
97
|
+
### Render Generative UI
|
98
|
+
|
99
|
+
ActiveAgent uses Action Prompt both for rendering `instructions` prompt views as well as rendering action views. Prompts are Action Views that provide instructions for the agent to generate content.
|
100
|
+
|
101
|
+
```erb
|
102
|
+
<!--
|
103
|
+
|
104
|
+
instructions.text.erb
|
105
|
+
|
106
|
+
-->
|
107
|
+
INSTRUCTIONS: You are an inventory manager for <%= @organization.name %>. You can search for inventory or reconcile inventory using <%= assigned_actions %>
|
108
|
+
```
|
109
|
+
|
110
|
+
### Scale with Asynchronous Jobs and Streaming
|
111
|
+
|
112
|
+
ActiveAgent supports asynchronous job processing and streaming for scalable AI interactions.
|
113
|
+
|
114
|
+
#### Asynchronous Jobs
|
115
|
+
|
116
|
+
Use the `generate_later` method to enqueue a job for later processing.
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
InventoryAgent.with(query: query).search.generate_later
|
120
|
+
```
|
121
|
+
|
122
|
+
#### Streaming
|
123
|
+
|
124
|
+
Use the `stream_with` method to handle streaming responses.
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
class InventoryAgent < ActiveAgent::Base
|
128
|
+
generate_with :openai, model: 'gpt-4o-mini', stream: :broadcast_results
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
def broadcast_results
|
133
|
+
proc do |chunk, _bytesize|
|
134
|
+
@message.content = @message.content + chunk
|
135
|
+
broadcast_append_to(
|
136
|
+
"#{dom_id(chat)}_messages",
|
137
|
+
partial: "messages/message",
|
138
|
+
locals: { message: @message, scroll_to: true },
|
139
|
+
target: "#{dom_id(chat)}_messages"
|
140
|
+
)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
```
|
145
|
+
|
146
|
+
## Contributing
|
147
|
+
|
148
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/yourusername/active_agent.
|
149
|
+
|
150
|
+
## License
|
151
|
+
|
152
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
153
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# Active Agent
|
2
|
+
Active Agent is a Rails framework for creating and managing AI agents. It provides a structured way to interact with generation providers through agents with context including prompts, tools, and messages. It includes two core modules, Generation Provider and Action Prompt, along with several support classes to handle different aspects of agent creation and management.
|
3
|
+
|
4
|
+
## Core Modules
|
5
|
+
|
6
|
+
- Generation Provider - module for configuring and interacting with generation providers through Prompts and Responses.
|
7
|
+
- Action Prompt - module for defining prompts, tools, and messages. The Base class implements an AbstractController to render prompts and actions. Prompts are Action Views that provide instructions for the agent to generate content, formatted messages for the agent and users including **streaming generative UI**.
|
8
|
+
|
9
|
+
## Main Components
|
10
|
+
|
11
|
+
- Base class - for creating and configuring agents.
|
12
|
+
- Queued Generation - module for managing queued generation requests and responses. Using the Generation Job class to perform asynchronous generation requests, it provides a way to **stream generation** requests back to the Job, Agent, or User.
|
13
|
+
|
14
|
+
### ActiveAgent::Base
|
15
|
+
|
16
|
+
The Base class is used to create agents that interact with generation providers through prompts and messages. It includes methods for configuring and interacting with generation providers using Prompts and Responses. The Base class also provides a structured way to render prompts and actions.
|
17
|
+
|
18
|
+
#### Core Methods
|
19
|
+
|
20
|
+
- `generate_with(provider, options = {})` - Configures the agent to generate content using the specified generation provider and options.
|
21
|
+
- `streams_with(provider, options = {})` - Configures the agent to stream content using the specified generation provider's stream option.
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# Active Agent: Action Prompt
|
2
|
+
|
3
|
+
ActionPrompt provides a structured way to create and manage prompts and tools for AI interactions. It includes several support classes to handle different aspects of prompt creation and management. The Base class implements an AbstractController to perform actions that render prompts..
|
4
|
+
|
5
|
+
## Main Components
|
6
|
+
|
7
|
+
Module - for including in your agent classes to provide prompt functionality.
|
8
|
+
Base class - for creating and managing prompts in ActiveAgent.
|
9
|
+
Tool class - for representing the tool object sent to the Agent's generation provider.
|
10
|
+
Message - class for representing a single message within a prompt.
|
11
|
+
Prompt - class for representing a the context of a prompt, including messages, actions, and other attributes.
|
12
|
+
|
13
|
+
### ActionPrompt::Base
|
14
|
+
|
15
|
+
The base class is used to create and manage prompts in Active Agent. It provides the core functionality for creating and managing contexts woth prompts, tools, and messages.
|
16
|
+
|
17
|
+
#### Core Methods
|
18
|
+
`prompt` - Creates a new prompt object with the given attributes.
|
19
|
+
|
20
|
+
|
21
|
+
### ActionPrompt::Tool
|
22
|
+
|
23
|
+
### ActionPrompt::Message
|
24
|
+
|
25
|
+
Represents a single message within a prompt.
|
26
|
+
|
27
|
+
### ActionPrompt::Prompt
|
28
|
+
|
29
|
+
Manages the overall structure of a prompt, including multiple messages, actions, and other attributes.
|
30
|
+
|
31
|
+
### ActionPrompt::Action
|
32
|
+
|
33
|
+
Represents an action that represents the tool object sent to the Agent's generation provider can be associated with a prompt or message.
|
34
|
+
|
35
|
+
## Usage
|
36
|
+
|
37
|
+
To use ActionPrompt in your agent, include the module in your agent class:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
class MyAgent
|
41
|
+
include ActiveAgent::ActionPrompt
|
42
|
+
|
43
|
+
# Your agent code here
|
44
|
+
end
|
File without changes
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "abstract_controller/collector"
|
4
|
+
require "active_support/core_ext/hash/reverse_merge"
|
5
|
+
require "active_support/core_ext/array/extract_options"
|
6
|
+
|
7
|
+
module ActiveAgent
|
8
|
+
module ActionPrompt
|
9
|
+
class Collector
|
10
|
+
include AbstractController::Collector
|
11
|
+
attr_reader :responses
|
12
|
+
|
13
|
+
def initialize(context, &block)
|
14
|
+
@context = context
|
15
|
+
@responses = []
|
16
|
+
@default_render = block
|
17
|
+
end
|
18
|
+
|
19
|
+
def any(*args, &block)
|
20
|
+
options = args.extract_options!
|
21
|
+
raise ArgumentError, "You have to supply at least one format" if args.empty?
|
22
|
+
args.each { |type| send(type, options.dup, &block) }
|
23
|
+
end
|
24
|
+
alias_method :all, :any
|
25
|
+
|
26
|
+
def custom(mime, options = {})
|
27
|
+
options.reverse_merge!(content_type: mime.to_s)
|
28
|
+
@context.formats = [mime.to_sym]
|
29
|
+
options[:body] = block_given? ? yield : @default_render.call
|
30
|
+
@responses << options
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module ActiveAgent
|
2
|
+
module ActionPrompt
|
3
|
+
class Message
|
4
|
+
VALID_ROLES = %w[system assistant user tool function].freeze
|
5
|
+
|
6
|
+
attr_accessor :content, :role, :name, :action_requested, :requested_actions
|
7
|
+
|
8
|
+
def initialize(attributes = {})
|
9
|
+
@content = attributes[:content] || ""
|
10
|
+
@role = attributes[:role] || "user"
|
11
|
+
@name = attributes[:name]
|
12
|
+
@action_requested = attributes[:function_call]
|
13
|
+
@requested_actions = attributes[:tool_calls] || []
|
14
|
+
validate_role
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_h
|
18
|
+
hash = {role: role, content: content}
|
19
|
+
hash[:name] = name if name
|
20
|
+
hash[:action_requested] = action_requested if action_requested
|
21
|
+
hash[:requested_actions] = requested_actions if requested_actions.any?
|
22
|
+
hash
|
23
|
+
end
|
24
|
+
|
25
|
+
def perform_actions
|
26
|
+
requested_actions.each do |action|
|
27
|
+
action.call(self) if action.respond_to?(:call)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def action_requested?
|
32
|
+
action_requested.present? || requested_actions.any?
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def validate_role
|
38
|
+
unless VALID_ROLES.include?(role.to_s)
|
39
|
+
raise ArgumentError, "Invalid role: #{role}. Valid roles are: #{VALID_ROLES.join(", ")}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# lib/active_agent/action_prompt/prompt.rb
|
2
|
+
require_relative "message"
|
3
|
+
|
4
|
+
module ActiveAgent
|
5
|
+
module ActionPrompt
|
6
|
+
class Prompt
|
7
|
+
attr_accessor :actions, :body, :content_type, :instructions, :message, :messages, :options, :mime_version, :charset, :context
|
8
|
+
|
9
|
+
def initialize(attributes = {})
|
10
|
+
@options = attributes.fetch(:options, {})
|
11
|
+
@actions = attributes.fetch(:actions, [])
|
12
|
+
@action_choice = attributes.fetch(:action_choice, "")
|
13
|
+
@instructions = attributes.fetch(:instructions, "")
|
14
|
+
@body = attributes.fetch(:body, "")
|
15
|
+
@content_type = attributes.fetch(:content_type, "text/plain")
|
16
|
+
@message = attributes.fetch(:message, Message.new)
|
17
|
+
@messages = attributes.fetch(:messages, [])
|
18
|
+
@params = attributes.fetch(:params, {})
|
19
|
+
@mime_version = attributes.fetch(:mime_version, "1.0")
|
20
|
+
@charset = attributes.fetch(:charset, "UTF-8")
|
21
|
+
@context = attributes.fetch(:context, [])
|
22
|
+
@headers = attributes.fetch(:headers, {})
|
23
|
+
@parts = attributes.fetch(:parts, [])
|
24
|
+
|
25
|
+
set_message if attributes[:message].is_a?(String) || @body.is_a?(String) && @message.content
|
26
|
+
set_messages if @messages.any? || @instructions.present?
|
27
|
+
end
|
28
|
+
|
29
|
+
# Generate the prompt as a string (for debugging or sending to the provider)
|
30
|
+
def to_s
|
31
|
+
@message.to_s
|
32
|
+
end
|
33
|
+
|
34
|
+
def add_part(part)
|
35
|
+
message = Message.new(content: part[:body], role: :user)
|
36
|
+
prompt_part = self.class.new(message: message, content: message.content, content_type: part[:content_type], chartset: part[:charset])
|
37
|
+
|
38
|
+
set_message if @content_type == part[:content_type] && @message.content
|
39
|
+
|
40
|
+
@parts << prompt_part
|
41
|
+
end
|
42
|
+
|
43
|
+
def multipart?
|
44
|
+
@parts.any?
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_h
|
48
|
+
{
|
49
|
+
actions: @actions,
|
50
|
+
action: @action_choice,
|
51
|
+
instructions: @instructions,
|
52
|
+
message: @message.to_h,
|
53
|
+
messages: @messages.map(&:to_h),
|
54
|
+
headers: @headers,
|
55
|
+
context: @context
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
def headers(headers = {})
|
60
|
+
@headers.merge!(headers)
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def set_messages
|
66
|
+
@messages = [Message.new(content: @instructions, role: :system)] + @messages
|
67
|
+
end
|
68
|
+
|
69
|
+
def set_message
|
70
|
+
if @body.is_a?(String) && !@message.content
|
71
|
+
@message = Message.new(content: @body, role: :user)
|
72
|
+
elsif @message.is_a? String
|
73
|
+
@message = Message.new(content: @message, role: :user)
|
74
|
+
end
|
75
|
+
@messages = [@message]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require "abstract_controller"
|
2
|
+
require "active_support/core_ext/string/inflections"
|
3
|
+
|
4
|
+
module ActiveAgent
|
5
|
+
module ActionPrompt
|
6
|
+
extend ::ActiveSupport::Autoload
|
7
|
+
|
8
|
+
eager_autoload do
|
9
|
+
autoload :Collector
|
10
|
+
autoload :Message
|
11
|
+
autoload :Prompt
|
12
|
+
autoload :PromptHelper
|
13
|
+
end
|
14
|
+
|
15
|
+
autoload :Base
|
16
|
+
|
17
|
+
extend ActiveSupport::Concern
|
18
|
+
|
19
|
+
included do
|
20
|
+
include AbstractController::Rendering
|
21
|
+
include AbstractController::Layouts
|
22
|
+
include AbstractController::Helpers
|
23
|
+
include AbstractController::Translation
|
24
|
+
include AbstractController::AssetPaths
|
25
|
+
include AbstractController::Callbacks
|
26
|
+
include AbstractController::Caching
|
27
|
+
|
28
|
+
include ActionView::Layouts
|
29
|
+
|
30
|
+
helper ActiveAgent::PromptHelper
|
31
|
+
# class_attribute :default_params, default: {
|
32
|
+
# content_type: "text/plain",
|
33
|
+
# parts_order: ["text/plain", "text/html", "application/json"]
|
34
|
+
# }.freeze
|
35
|
+
end
|
36
|
+
|
37
|
+
# # def self.prompt(headers = {}, &)
|
38
|
+
# # new.prompt(headers, &)
|
39
|
+
# # end
|
40
|
+
|
41
|
+
# def prompt(headers = {}, &block)
|
42
|
+
# return @_message if @_prompt_was_called && headers.blank? && !block
|
43
|
+
|
44
|
+
# headers = apply_defaults(headers)
|
45
|
+
|
46
|
+
# @_message = ActiveAgent::ActionPrompt::Prompt.new
|
47
|
+
|
48
|
+
# assign_headers_to_message(@_message, headers)
|
49
|
+
|
50
|
+
# responses = collect_responses(headers, &block)
|
51
|
+
|
52
|
+
# @_prompt_was_called = true
|
53
|
+
|
54
|
+
# create_parts_from_responses(@_message, responses)
|
55
|
+
|
56
|
+
# @_message
|
57
|
+
# end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def apply_defaults(headers)
|
62
|
+
headers.reverse_merge(self.class.default_params)
|
63
|
+
end
|
64
|
+
|
65
|
+
def assign_headers_to_message(message, headers)
|
66
|
+
assignable = headers.except(:parts_order, :content_type, :body, :template_name, :template_path)
|
67
|
+
assignable.each { |k, v| message.send(:"#{k}=", v) }
|
68
|
+
end
|
69
|
+
|
70
|
+
def collect_responses(headers, &block)
|
71
|
+
if block
|
72
|
+
collect_responses_from_block(headers, &block)
|
73
|
+
elsif headers[:body]
|
74
|
+
collect_responses_from_text(headers)
|
75
|
+
else
|
76
|
+
collect_responses_from_templates(headers)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def collect_responses_from_block(headers, &block)
|
81
|
+
templates_name = headers[:template_name] || action_name
|
82
|
+
collector = ActiveAgent::ActionPrompt::Collector.new(lookup_context) { render(templates_name) }
|
83
|
+
yield(collector)
|
84
|
+
collector.responses
|
85
|
+
end
|
86
|
+
|
87
|
+
def collect_responses_from_text(headers)
|
88
|
+
[{
|
89
|
+
body: headers.delete(:body),
|
90
|
+
content_type: headers[:content_type] || "text/plain"
|
91
|
+
}]
|
92
|
+
end
|
93
|
+
|
94
|
+
def collect_responses_from_templates(headers)
|
95
|
+
templates_path = headers[:template_path] || self.class.name.sub(/Agent$/, "").underscore
|
96
|
+
templates_name = headers[:template_name] || action_name
|
97
|
+
each_template(Array(templates_path), templates_name).map do |template|
|
98
|
+
format = template.format || formats.first
|
99
|
+
{
|
100
|
+
body: render(template: template, formats: [format]),
|
101
|
+
content_type: Mime[format].to_s
|
102
|
+
}
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def each_template(paths, name, &)
|
107
|
+
templates = lookup_context.find_all(name, paths)
|
108
|
+
if templates.empty?
|
109
|
+
raise ActionView::MissingTemplate.new(paths, name, paths, false, "prompt")
|
110
|
+
else
|
111
|
+
templates.uniq(&:format).each(&)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def create_parts_from_responses(message, responses)
|
116
|
+
responses.each do |response|
|
117
|
+
message.add_part(response[:body], content_type: response[:content_type])
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
class TestAgent
|
122
|
+
class << self
|
123
|
+
attr_accessor :generations
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|