activeagent 0.2.5 → 0.2.6.rc1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cbfba3a78246c1008e29842f64cfb2bd880e2a33800639590324844ce6045bcd
4
- data.tar.gz: a52d61ba1dac3d01ae6b84db9d70d81cb230dc1a975897218b48e372392710c3
3
+ metadata.gz: b5be38e4a523b45e6456791eaa43e9a04ea377d0d6921c8c0e427b8bb06418f3
4
+ data.tar.gz: 191f478a1d79f5bfe65e2016f9ec4063dce945eb09912da2cd70d393f0fda3c5
5
5
  SHA512:
6
- metadata.gz: a85609f0e5d816b9fabe84fdbf589d2da716c740aa2406386c3c6cf0898436ac1d57ee04adca0d99418d2b38fd4c88c700ff468d1a6040869c3b22b790c2c780
7
- data.tar.gz: 040fb0a37075cb2fc691be6b42e0395b7b3ab664e869a984f6581ee3c7a7357864676699af852e6b3e76e2ae765ab403e6a6e04cff46aa544f83b2e156fa86aa
6
+ metadata.gz: 8f4749307fec6f7b9a7339486c09b9f20d61b05388a67f12f0c2e00267fc14979f6d193f649c64117f13b822baac75511b0269700a6362d0e3cb548c9d68c1df
7
+ data.tar.gz: 7b3adbd0b9372d037b6fd303f55103d06e54ae7693b3920c7746ee1b7efe95f14134c7998a0bf5034e044fd3548ab0e44bf69df7a64109a2a0e319201273cd84
@@ -9,7 +9,7 @@ module ActiveAgent
9
9
  end
10
10
 
11
11
  def perform_action
12
- agent_name.constantize
12
+ agent_name.constantize.new.process(name, params)
13
13
  end
14
14
  end
15
15
  end
@@ -12,7 +12,7 @@ module ActiveAgent
12
12
  @instructions = attributes.fetch(:instructions, "")
13
13
  @body = attributes.fetch(:body, "")
14
14
  @content_type = attributes.fetch(:content_type, "text/plain")
15
- @message = attributes.fetch(:message, Message.new)
15
+ @message = attributes.fetch(:message, nil)
16
16
  @messages = attributes.fetch(:messages, [])
17
17
  @params = attributes.fetch(:params, {})
18
18
  @mime_version = attributes.fetch(:mime_version, "1.0")
@@ -21,8 +21,8 @@ module ActiveAgent
21
21
  @headers = attributes.fetch(:headers, {})
22
22
  @parts = attributes.fetch(:parts, [])
23
23
 
24
- set_message if attributes[:message].is_a?(String) || @body.is_a?(String) && @message.content
25
- set_messages if @messages.any? || @instructions.present?
24
+ set_message if attributes[:message].is_a?(String) || @body.is_a?(String) && @message&.content
25
+ set_messages
26
26
  end
27
27
 
28
28
  # Generate the prompt as a string (for debugging or sending to the provider)
@@ -30,12 +30,11 @@ module ActiveAgent
30
30
  @message.to_s
31
31
  end
32
32
 
33
- def add_part(prompt_part)
34
- @message = prompt_part.message
33
+ def add_part(message)
34
+ @message = message
35
+ set_message if @content_type == message.content_type && @message.content.present?
35
36
 
36
- set_message if @content_type == prompt_part.content_type && @message.content.present?
37
-
38
- @parts << prompt_part
37
+ @parts << context
39
38
  end
40
39
 
41
40
  def multipart?
@@ -128,8 +128,8 @@ module ActiveAgent
128
128
  # Define how the agent should generate content
129
129
  def generate_with(provider, **options)
130
130
  self.generation_provider = provider
131
- self.options = (options || {}).merge(options)
132
- generation_provider.config.merge!(options)
131
+ self.options = (options || {}).merge(options)
132
+ generation_provider.config.merge!(self.options)
133
133
  end
134
134
 
135
135
  def stream_with(&stream)
@@ -203,20 +203,20 @@ module ActiveAgent
203
203
  attr_internal :context
204
204
 
205
205
  def perform_generation
206
- context.options.merge(options)
206
+ context.options.merge(options)
207
207
  generation_provider.generate(context) if context && generation_provider
208
- handle_response(generation_provider.response)
209
- generation_provider.response
208
+ handle_response(generation_provider.response)
210
209
  end
211
210
 
212
211
  def handle_response(response)
213
212
  perform_actions(requested_actions: response.message.requested_actions) if response.message.requested_actions.present?
214
- update_context(response)
213
+
214
+ update_context(response)
215
215
  end
216
216
 
217
217
  def update_context(response)
218
218
  context.message = response.message
219
- context.messages << response.message
219
+ response
220
220
  end
221
221
 
222
222
  def perform_actions(requested_actions:)
@@ -232,7 +232,7 @@ module ActiveAgent
232
232
  def initialize
233
233
  super
234
234
  @_prompt_was_called = false
235
- @_context = ActiveAgent::ActionPrompt::Prompt.new(instructions: options[:instructions])
235
+ @_context = ActiveAgent::ActionPrompt::Prompt.new(instructions: options[:instructions], options: options)
236
236
  end
237
237
 
238
238
  def process(method_name, *args) # :nodoc:
@@ -241,7 +241,7 @@ module ActiveAgent
241
241
  action: method_name,
242
242
  args: args
243
243
  }
244
-
244
+
245
245
  ActiveSupport::Notifications.instrument("process.active_agent", payload) do
246
246
  super
247
247
  @_context = ActiveAgent::ActionPrompt::Prompt.new unless @_prompt_was_called
@@ -332,7 +332,7 @@ module ActiveAgent
332
332
 
333
333
  def action_schemas
334
334
  action_methods.map do |action|
335
- if action != "prompt"
335
+ if action != "text_prompt"
336
336
  JSON.parse render_to_string(locals: {action_name: action}, action: action, formats: :json)
337
337
  end
338
338
  end.compact
@@ -431,24 +431,22 @@ module ActiveAgent
431
431
 
432
432
  def create_parts_from_responses(context, responses)
433
433
  if responses.size > 1
434
- prompt_container = ActiveAgent::ActionPrompt::Prompt.new
435
- prompt_container.content_type = "multipart/alternative"
434
+ # prompt_container = ActiveAgent::ActionPrompt::Prompt.new
435
+ # prompt_container.content_type = "multipart/alternative"
436
436
  responses.each { |r| insert_part(context, r, context.charset) }
437
- context.add_part(prompt_container)
437
+ # context.add_part(prompt_container)
438
438
  else
439
439
  responses.each { |r| insert_part(context, r, context.charset) }
440
440
  end
441
441
  end
442
442
 
443
- def insert_part(container, response, charset)
444
- prompt = ActiveAgent::ActionPrompt::Prompt.new
443
+ def insert_part(context, response, charset)
445
444
  message = ActiveAgent::ActionPrompt::Message.new(
446
445
  content: response[:body],
447
446
  content_type: response[:content_type],
448
447
  charset: charset
449
448
  )
450
- prompt.message = message
451
- container.add_part(prompt)
449
+ context.add_part(message)
452
450
  end
453
451
  # This and #instrument_name is for caching instrument
454
452
  def instrument_payload(key)
@@ -7,6 +7,7 @@ module ActiveAgent
7
7
  included do
8
8
  include ActiveSupport::Callbacks
9
9
  define_callbacks :generate, skip_after_callbacks_if_terminated: true
10
+ define_callbacks :stream, skip_after_callbacks_if_terminated: true
10
11
  end
11
12
 
12
13
  module ClassMethods
@@ -26,6 +27,18 @@ module ActiveAgent
26
27
  def around_generate(*filters, &)
27
28
  set_callback(:generate, :around, *filters, &)
28
29
  end
30
+
31
+ # Defines a callback for handling streaming responses during generation
32
+ def on_stream(*filters, &)
33
+ set_callback(:stream, :before, *filters, &)
34
+ end
35
+ end
36
+
37
+ # Helper method to run stream callbacks
38
+ def run_stream_callbacks(message, delta = nil, stop = false)
39
+ run_callbacks(:stream) do
40
+ yield(message, delta, stop) if block_given?
41
+ end
29
42
  end
30
43
  end
31
- end
44
+ end
@@ -23,7 +23,7 @@ module ActiveAgent
23
23
  agent = agent_class.new
24
24
  agent.params = params if params
25
25
  agent.process(action_name, *args)
26
- agent.generate
26
+ agent.perform_generation
27
27
  end
28
28
 
29
29
  private
@@ -17,7 +17,8 @@ module ActiveAgent
17
17
 
18
18
  def generate(prompt)
19
19
  @prompt = prompt
20
-
20
+
21
+ # prompt_parameters(model: @model_name, messages: prompt.messages, tools: prompt.actions)
21
22
  # parameters[:instructions] = prompt.instructions.content if prompt.instructions.present?
22
23
 
23
24
  chat_prompt(parameters: prompt_parameters)
@@ -60,19 +61,19 @@ module ActiveAgent
60
61
  def provider_stream
61
62
  # prompt.options[:stream] will define a proc found in prompt at runtime
62
63
  # config[:stream] will define a proc found in config. stream would come from an Agent class's generate_with or stream_with method calls
63
- agent_stream = prompt.options[:stream] || config["stream"]
64
+ agent_stream = prompt.options[:stream]
65
+ message = ActiveAgent::ActionPrompt::Message.new(content: "", role: :assistant)
66
+ @response = ActiveAgent::GenerationProvider::Response.new(prompt: prompt, message: )
67
+
64
68
  proc do |chunk, bytesize|
65
- # Provider parsing logic here
66
- new_content = chunk.dig("choices", 0, "delta", "content")
67
- message = @prompt.messages.find { |message| message.response_number == chunk.dig("choices", 0, "index") }
68
- message.update(content: message.content + new_content) if new_content
69
-
70
- # Call the custom stream_proc if provided
71
- agent_stream.call(message) if agent_stream.respond_to?(:call)
69
+ if new_content = chunk.dig("choices", 0, "delta", "content")
70
+ message.content += new_content
71
+ agent_stream.call(message) if agent_stream.respond_to?(:call)
72
+ end
72
73
  end
73
74
  end
74
75
 
75
- def prompt_parameters(model: @model_name, messages: @prompt.messages, temperature: @config["temperature"] || 0.7, tools: @prompt.actions)
76
+ def prompt_parameters(model: @prompt.options[:model] || @model_name, messages: @prompt.messages, temperature: @config["temperature"] || 0.7, tools: @prompt.actions)
76
77
  {
77
78
  model: model,
78
79
  messages: messages,
@@ -82,14 +83,16 @@ module ActiveAgent
82
83
  end
83
84
 
84
85
  def chat_response(response)
86
+ return @response if prompt.options[:stream]
87
+
85
88
  message_json = response.dig("choices", 0, "message")
89
+
86
90
  message = ActiveAgent::ActionPrompt::Message.new(
87
91
  content: message_json["content"],
88
92
  role: message_json["role"],
89
93
  action_requested: message_json["finish_reason"] == "tool_calls",
90
94
  requested_actions: handle_actions(message_json["tool_calls"])
91
95
  )
92
-
93
96
  update_context(prompt: prompt, message: message, response: response)
94
97
 
95
98
  @response = ActiveAgent::GenerationProvider::Response.new(prompt: prompt, message: message, raw_response: response)
@@ -2,7 +2,7 @@
2
2
 
3
3
  require "active_job/railtie"
4
4
  require "active_agent"
5
- require "active_agent/engine"
5
+ # require "active_agent/engine"
6
6
  require "rails"
7
7
  require "abstract_controller/railties/routes_helpers"
8
8
 
@@ -10,7 +10,7 @@ module ActiveAgent
10
10
  class Railtie < Rails::Railtie # :nodoc:
11
11
  config.active_agent = ActiveSupport::OrderedOptions.new
12
12
  config.active_agent.preview_paths = []
13
- config.eager_load_namespaces << ::ActiveAgent
13
+ config.eager_load_namespaces << ActiveAgent
14
14
 
15
15
  initializer "active_agent.deprecator", before: :load_environment_config do |app|
16
16
  app.deprecators[:active_agent] = ActiveAgent.deprecator
@@ -34,6 +34,8 @@ module ActiveAgent
34
34
  # make sure readers methods get compiled
35
35
  options.asset_host ||= app.config.asset_host
36
36
  options.relative_url_root ||= app.config.relative_url_root
37
+
38
+ ActiveAgent.load_configuration(Rails.root.join('config', 'active_agent.yml'))
37
39
 
38
40
  ActiveSupport.on_load(:active_agent) do
39
41
  include AbstractController::UrlFor
@@ -46,20 +48,16 @@ module ActiveAgent
46
48
  self.view_paths = ["#{Rails.root}/app/views"]
47
49
  self.preview_paths |= options[:preview_paths]
48
50
 
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
51
+ if generation_job = options.delete(:generation_job)
52
+ self.generation_job = generation_job.constantize
55
53
  end
56
54
 
57
55
  options.each { |k, v| send(:"#{k}=", v) }
58
56
  end
59
57
 
60
58
  ActiveSupport.on_load(:action_dispatch_integration_test) do
61
- include ActiveAgent::TestHelper
62
- include ActiveAgent::TestCase::ClearTestDeliveries
59
+ # include ActiveAgent::TestHelper
60
+ # include ActiveAgent::TestCase::ClearTestDeliveries
63
61
  end
64
62
  end
65
63
 
@@ -1,3 +1,3 @@
1
1
  module ActiveAgent
2
- VERSION = "0.2.5"
2
+ VERSION = "0.2.6.rc1"
3
3
  end
@@ -0,0 +1 @@
1
+ require "active_agent"
@@ -4,8 +4,8 @@ class ApplicationAgent < ActiveAgent::Base
4
4
 
5
5
  generate_with :openai, model: "gpt-4o-mini", instructions: "You are a helpful assistant."
6
6
 
7
- def prompt
8
- super { |format| format.text { render plain: params[:message] } }
7
+ def text_prompt
8
+ prompt { |format| format.text { render plain: params[:message] } }
9
9
  end
10
10
  end
11
11
  <% 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: 0.2.5
4
+ version: 0.2.6.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Bowen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-02-28 00:00:00.000000000 Z
11
+ date: 2025-03-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -137,8 +137,6 @@ executables: []
137
137
  extensions: []
138
138
  extra_rdoc_files: []
139
139
  files:
140
- - README.md
141
- - Rakefile
142
140
  - lib/active_agent.rb
143
141
  - lib/active_agent/README.md
144
142
  - lib/active_agent/action_prompt.rb
@@ -172,6 +170,7 @@ files:
172
170
  - lib/active_agent/service.rb
173
171
  - lib/active_agent/test_case.rb
174
172
  - lib/active_agent/version.rb
173
+ - lib/activeagent.rb
175
174
  - lib/generators/active_agent/USAGE
176
175
  - lib/generators/active_agent/agent_generator.rb
177
176
  - lib/generators/active_agent/install_generator.rb
@@ -182,11 +181,14 @@ files:
182
181
  - lib/generators/active_agent/templates/agent_spec.rb.tt
183
182
  - lib/generators/active_agent/templates/agent_test.rb.tt
184
183
  - lib/generators/active_agent/templates/application_agent.rb.tt
185
- - lib/generators/active_agent/templates/initializer.rb
186
- homepage: https://rubygems.org/gems/activeagent
184
+ homepage: https://activeagents.ai
187
185
  licenses:
188
186
  - MIT
189
- metadata: {}
187
+ metadata:
188
+ bug_tracker_uri: https://github.com/activeagents/activeagent/issues
189
+ documentation_uri: https://github.com/activeagents/activeagent
190
+ source_code_uri: https://github.com/activeagents/activeagent
191
+ rubygems_mfa_required: 'true'
190
192
  post_install_message:
191
193
  rdoc_options: []
192
194
  require_paths:
data/README.md DELETED
@@ -1,188 +0,0 @@
1
- # Active Agent
2
-
3
- ## Install
4
-
5
- ### Gemfile
6
- `gem 'activeagent', require: 'active_agent'`
7
-
8
- ### CLI
9
- `gem install activeagent`
10
-
11
- ### Rails Generator
12
- After installing the gem, run the Rails installation generator:
13
-
14
- ```bash
15
- $ rails generate active_agent:install
16
- ```
17
-
18
- This will create:
19
- ```
20
- create config/initializers/active_agent.rb
21
- create config/active_agent.yml
22
- create app/agents/application_agent.rb
23
- create app/agents
24
- ```
25
-
26
- - An initializer that uses default configurations
27
- ```ruby
28
- # config/initializers/active_agent.rb
29
- ActiveAgent.load_configuration(Rails.root.join('config', 'active_agent.yml'))
30
- ```
31
- - A YAML configuration file for provider settings, such as OpenAI and might include environment-specific configurations:
32
- ```yaml
33
- # config/active_agent.yml
34
- development:
35
- openai:
36
- service: "OpenAI"
37
- api_key: <%= Rails.application.credentials.dig(:openai, :api_key) %>
38
- model: "gpt-3.5-turbo"
39
- temperature: 0.7
40
-
41
- production:
42
- openai:
43
- service: "OpenAI"
44
- api_key: <%= Rails.application.credentials.dig(:openai, :api_key) %>
45
- model: "gpt-3.5-turbo"
46
- temperature: 0.7
47
-
48
- ```
49
- - A base application agent class
50
- ```ruby
51
- # app/agents/application_agent.rb
52
- class ApplicationAgent < ActiveAgent::Base
53
- layout 'agent'
54
-
55
- def prompt
56
- super { |format| format.text { render plain: params[:message] } }
57
- end
58
- ```
59
- - The agents directory structure
60
-
61
- ## Agent
62
- Create agents that take instructions, prompts, and perform actions
63
-
64
- ### Rails Generator
65
- To use the Rails Active Agent generator to create a new agent and the associated views for the requested action prompts:
66
-
67
- ```bash
68
- $ rails generate active_agent:agent travel search book plans
69
- ```
70
- This will create:
71
- ```
72
- create app/agents/travel_agent.rb
73
- create app/views/agents/travel/search.text.erb
74
- create app/views/agents/travel/book.text.erb
75
- create app/views/agents/travel/plans.text.erb
76
- ```
77
-
78
- The generator creates:
79
- - An agent class inheriting from ApplicationAgent
80
- - Text template views for each action
81
- - Action methods in the agent class for processing prompts
82
-
83
- ### Agent Actions
84
- ```ruby
85
- class TravelAgent < ApplicationAgent
86
- def search
87
-
88
- prompt { |format| format.text { render plain: "Searching for travel options" } }
89
- end
90
-
91
- def book
92
- prompt { |format| format.text { render plain: "Booking travel plans" } }
93
- end
94
-
95
- def plans
96
- prompt { |format| format.text { render plain: "Making travel plans" } }
97
- end
98
- end
99
- ```
100
-
101
- ## Action Prompt
102
-
103
- Action Prompt provides the structured interface for composing AI interactions through messages, actions/tools, and conversation context. [Read more about Action Prompt](lib/active_agent/action_prompt/README.md)
104
-
105
- ```ruby
106
- agent.prompt(message: "Find hotels in Paris",
107
- actions: [{name: "search", params: {query: "hotels paris"}}])
108
- ```
109
-
110
- The prompt interface manages:
111
- - Message content and roles (system/user/assistant)
112
- - Action/tool definitions and requests
113
- - Headers and context tracking
114
- - Content types and multipart handling
115
-
116
- ### Generation Provider
117
-
118
- Generation Provider defines how prompts are sent to AI services for completion and embedding generation. [Read more about Generation Providers](lib/active_agent/generation_provider/README.md)
119
-
120
- ```ruby
121
- class VacationAgent < ActiveAgent::Base
122
- # Try not to get too model-rous with the parameters!
123
- generate_with :openai,
124
- model: "gpt-4",
125
- temperature: 0.7
126
-
127
- # Embed yourself in the joy of vector search
128
- embed_with :openai,
129
- model: "text-embedding-ada-002"
130
- end
131
- ```
132
-
133
- Providers handle:
134
- - API client configuration
135
- - Prompt/completion generation
136
- - Stream processing
137
- - Embedding generation
138
- - Context management
139
- - Error handling
140
-
141
- ### Queue Generation
142
-
143
- Active Agent also supports queued generation with Active Job using a common Generation Job interface.
144
-
145
- ### Perform actions
146
-
147
- Active Agents can define methods that are autoloaded as callable tools. These actions’ default schema will be provided to the agent’s context as part of the prompt request to the Generation Provider.
148
-
149
- ## Actions
150
-
151
- ```ruby
152
- def get_cat_image_base64
153
- uri = URI("https://cataas.com/cat")
154
- response = Net::HTTP.get_response(uri)
155
-
156
- if response.is_a?(Net::HTTPSuccess)
157
- image_data = response.body
158
- Base64.strict_encode64(image_data)
159
- else
160
- raise "Failed to fetch cat image. Status code: #{response.code}"
161
- end
162
- end
163
-
164
- class SupportAgent < ActiveAgent
165
- generate_with :openai,
166
- model: "gpt-4o",
167
- instructions: "Help people with their problems",
168
- temperature: 0.7
169
-
170
- def get_cat_image
171
- prompt { |format| format.text { render plain: get_cat_image_base64 } }
172
- end
173
- end
174
- ```
175
-
176
- ## Prompts
177
-
178
- ### Basic
179
-
180
- #### Plain text prompt and response templates
181
-
182
- ### HTML
183
-
184
- ### Action Schema JSON
185
-
186
- response = SupportAgent.prompt(‘show me a picture of a cat’).generate_now
187
-
188
- response.message
data/Rakefile DELETED
@@ -1,2 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
@@ -1,3 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- ActiveAgent.load_configuration(Rails.root.join('config', 'active_agent.yml'))