activeagent 0.2.6.8 → 0.3

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: 44f424a63296642766c739571e7833995ef70b05f1d16ed91fab3b4f1657136a
4
- data.tar.gz: cf242b606153176e0e7a11efb77306df9bf0f526b601301dc6f8213f1a86ff26
3
+ metadata.gz: bf6889f20afa4e61e46ab6edbe801186aee0d3fe85284f3ce6379509a6a76bc4
4
+ data.tar.gz: 3340dcc2d65740cea5a98f4c0640ac806e1686bb48bee2a3260cf3e65b5478b2
5
5
  SHA512:
6
- metadata.gz: 75265e0675be1e59bfa5721ddf3c87cb9487ce45cbefdcd6457d43b064d70f0ab2c5256a913dc8630cbd05efada6e41af17d05f85347414e2f0568659c9cbca6
7
- data.tar.gz: 6c0a82f30aed52a0bb12f8f1d89d1b6310c1400f7456e0351b3d5765d27613546d1fdd87dd9add64944a054cdecdf797ef09a4767eee239a7f405c9b74c439ca
6
+ metadata.gz: 8c63eaaad2c5049fac65902d5d5676f976580fffd40477287b149d29695c15b75143e1bbf8e60128c04adfc23531770c9ab1c7893a6cf73bd4f258315a93e251
7
+ data.tar.gz: 0be4d79a4755d23d1c7e2c659871d8e0480c05583e6fe1d6eae48571fd1d2540cc88ea53a827192109bf76476e237e8c09f1bbc64f97bca429416b3c7503154c
@@ -3,14 +3,13 @@ module ActiveAgent
3
3
  class Message
4
4
  VALID_ROLES = %w[system assistant user tool function].freeze
5
5
 
6
- attr_accessor :action_id, :content, :role, :name, :action_requested, :requested_actions, :content_type, :charset
6
+ attr_accessor :action_id, :content, :role, :action_requested, :requested_actions, :content_type, :charset
7
7
 
8
8
  def initialize(attributes = {})
9
9
  @action_id = attributes[:action_id]
10
10
  @charset = attributes[:charset] || "UTF-8"
11
11
  @content = attributes[:content] || ""
12
12
  @content_type = attributes[:content_type] || "text/plain"
13
- @name = attributes[:name]
14
13
  @role = attributes[:role] || :user
15
14
  @requested_actions = attributes[:requested_actions] || []
16
15
  @action_requested = @requested_actions.any?
@@ -26,7 +25,6 @@ module ActiveAgent
26
25
  charset: charset
27
26
  }
28
27
 
29
- hash[:name] = name if name
30
28
  hash[:action_requested] = requested_actions.any?
31
29
  hash[:requested_actions] = requested_actions if requested_actions.any?
32
30
  hash
@@ -3,10 +3,11 @@ require_relative "message"
3
3
  module ActiveAgent
4
4
  module ActionPrompt
5
5
  class Prompt
6
- attr_accessor :actions, :body, :content_type, :instructions, :message, :messages, :options, :mime_version, :charset, :context, :parts
6
+ attr_accessor :actions, :body, :content_type, :context_id, :instructions, :message, :messages, :options, :mime_version, :charset, :context, :parts
7
7
 
8
8
  def initialize(attributes = {})
9
9
  @options = attributes.fetch(:options, {})
10
+ @agent_class = attributes.fetch(:agent_class, ApplicationAgent)
10
11
  @actions = attributes.fetch(:actions, [])
11
12
  @action_choice = attributes.fetch(:action_choice, "")
12
13
  @instructions = attributes.fetch(:instructions, "")
@@ -18,6 +19,7 @@ module ActiveAgent
18
19
  @mime_version = attributes.fetch(:mime_version, "1.0")
19
20
  @charset = attributes.fetch(:charset, "UTF-8")
20
21
  @context = attributes.fetch(:context, [])
22
+ @context_id = attributes.fetch(:context_id, nil)
21
23
  @headers = attributes.fetch(:headers, {})
22
24
  @parts = attributes.fetch(:parts, [])
23
25
 
@@ -129,6 +129,7 @@ module ActiveAgent
129
129
  def generate_with(provider, **options)
130
130
  self.generation_provider = provider
131
131
  self.options = (options || {}).merge(options)
132
+ self.options[:stream] = new.agent_stream if self.options[:stream]
132
133
  generation_provider.config.merge!(self.options)
133
134
  end
134
135
 
@@ -176,13 +177,8 @@ module ActiveAgent
176
177
 
177
178
  def set_payload_for_prompt(payload, prompt)
178
179
  payload[:prompt] = prompt.encoded
179
- payload[:agent] = name
180
+ payload[:agent] = agent_name
180
181
  payload[:message_id] = prompt.message_id
181
- payload[:subject] = prompt.subject
182
- payload[:to] = prompt.to
183
- payload[:from] = prompt.from
184
- payload[:bcc] = prompt.bcc if prompt.bcc.present?
185
- payload[:cc] = prompt.cc if prompt.cc.present?
186
182
  payload[:date] = prompt.date
187
183
  payload[:perform_generations] = prompt.perform_generations
188
184
  end
@@ -200,47 +196,56 @@ module ActiveAgent
200
196
  end
201
197
  end
202
198
 
203
- attr_internal :context
199
+ attr_internal :prompt_context
200
+
201
+ def agent_stream
202
+ proc do |message, delta, stop|
203
+ run_stream_callbacks(message, delta, stop) do |message, delta, stop|
204
+ yield message, delta, stop if block_given?
205
+ end
206
+ end
207
+ end
204
208
 
205
209
  def embed
206
- context.options.merge(options)
207
- generation_provider.embed(context) if context && generation_provider
210
+ prompt_context.options.merge(options)
211
+ generation_provider.embed(prompt_context) if prompt_context && generation_provider
208
212
  handle_response(generation_provider.response)
209
213
  end
210
214
 
211
215
  def perform_generation
212
- context.options.merge(options)
213
- generation_provider.generate(context) if context && generation_provider
216
+ prompt_context.options.merge(options)
217
+ generation_provider.generate(prompt_context) if prompt_context && generation_provider
214
218
  handle_response(generation_provider.response)
215
219
  end
216
220
 
217
221
  def handle_response(response)
218
222
  perform_actions(requested_actions: response.message.requested_actions) if response.message.requested_actions.present?
219
223
 
220
- update_context(response)
224
+ update_prompt_context(response)
221
225
  end
222
226
 
223
- def update_context(response)
224
- response.prompt
227
+ def update_prompt_context(response)
228
+ # response.prompt = prompt_context
229
+ # response.message = response.messages.last
225
230
  response
226
231
  end
227
232
 
228
233
  def perform_actions(requested_actions:)
229
234
  requested_actions.each do |action|
230
235
  perform_action(action)
231
- prompt.messages.last.role = :tool
232
- prompt.messages.last.action_id = action.id
233
236
  end
234
237
  end
235
238
 
236
239
  def perform_action(action)
237
240
  process(action.name, *action.params)
241
+ prompt_context.messages.last.role = :tool
242
+ prompt_context.messages.last.action_id = action.id
238
243
  end
239
244
 
240
245
  def initialize
241
246
  super
242
247
  @_prompt_was_called = false
243
- @_context = ActiveAgent::ActionPrompt::Prompt.new(instructions: options[:instructions], options: options)
248
+ @_prompt_context = ActiveAgent::ActionPrompt::Prompt.new(instructions: options[:instructions], options: options)
244
249
  end
245
250
 
246
251
  def process(method_name, *args) # :nodoc:
@@ -252,7 +257,7 @@ module ActiveAgent
252
257
 
253
258
  ActiveSupport::Notifications.instrument("process.active_agent", payload) do
254
259
  super
255
- @_context = ActiveAgent::ActionPrompt::Prompt.new unless @_prompt_was_called
260
+ @_prompt_context = ActiveAgent::ActionPrompt::Prompt.new unless @_prompt_was_called
256
261
  end
257
262
  end
258
263
  ruby2_keywords(:process)
@@ -282,60 +287,39 @@ module ActiveAgent
282
287
 
283
288
  def headers(args = nil)
284
289
  if args
285
- @_context.headers(args)
290
+ @_prompt_context.headers(args)
286
291
  else
287
- @_context
288
- end
289
- end
290
-
291
- # def attachments
292
- # if @_prompt_was_called
293
- # LateAttachmentsProxy.new(@_context.attachments)
294
- # else
295
- # @_context.attachments
296
- # end
297
- # end
298
-
299
- class LateAttachmentsProxy < SimpleDelegator
300
- def inline
301
- self
302
- end
303
-
304
- def []=(_name, _content)
305
- _raise_error
306
- end
307
-
308
- private
309
-
310
- def _raise_error
311
- raise "Can't add attachments after `prompt` was called.\n" \
312
- "Make sure to use `attachments[]=` before calling `prompt`."
292
+ @_prompt_context
313
293
  end
314
294
  end
315
295
 
316
296
  def prompt_with(*)
317
- context.update_context(*)
297
+ prompt_context.update_prompt_context(*)
318
298
  end
319
299
 
320
300
  def prompt(headers = {}, &block)
321
- return context if @_prompt_was_called && headers.blank? && !block
301
+ return prompt_context if @_prompt_was_called && headers.blank? && !block
322
302
 
323
303
  content_type = headers[:content_type]
324
304
 
325
305
  headers = apply_defaults(headers)
326
306
 
327
- context.charset = charset = headers[:charset]
307
+ prompt_context.context_id = headers[:context_id]
308
+
309
+ prompt_context.options = options.merge(headers[:options] || {})
310
+
311
+ prompt_context.charset = charset = headers[:charset]
328
312
 
329
313
  responses = collect_responses(headers, &block)
330
314
 
331
315
  @_prompt_was_called = true
332
316
 
333
- create_parts_from_responses(context, responses)
317
+ create_parts_from_responses(prompt_context, responses)
334
318
 
335
- context.content_type = set_content_type(context, content_type, headers[:content_type])
336
- context.charset = charset
337
- context.actions = headers[:actions] || action_schemas
338
- context
319
+ prompt_context.content_type = set_content_type(prompt_context, content_type, headers[:content_type])
320
+ prompt_context.charset = charset
321
+ prompt_context.actions = headers[:actions] || action_schemas
322
+ prompt_context
339
323
  end
340
324
 
341
325
  def action_schemas
@@ -352,7 +336,7 @@ module ActiveAgent
352
336
  if user_content_type.present?
353
337
  user_content_type
354
338
  else
355
- context.content_type || class_default
339
+ prompt_context.content_type || class_default
356
340
  end
357
341
  end
358
342
 
@@ -383,10 +367,10 @@ module ActiveAgent
383
367
  end
384
368
  end
385
369
 
386
- def assign_headers_to_context(context, headers)
370
+ def assign_headers_to_prompt_context(prompt_context, headers)
387
371
  assignable = headers.except(:parts_order, :content_type, :body, :template_name,
388
372
  :template_path, :delivery_method, :delivery_method_options)
389
- assignable.each { |k, v| context[k] = v }
373
+ assignable.each { |k, v| prompt_context[k] = v }
390
374
  end
391
375
 
392
376
  def collect_responses(headers, &)
@@ -437,24 +421,24 @@ module ActiveAgent
437
421
  end
438
422
  end
439
423
 
440
- def create_parts_from_responses(context, responses)
424
+ def create_parts_from_responses(prompt_context, responses)
441
425
  if responses.size > 1
442
426
  # prompt_container = ActiveAgent::ActionPrompt::Prompt.new
443
427
  # prompt_container.content_type = "multipart/alternative"
444
- responses.each { |r| insert_part(context, r, context.charset) }
445
- # context.add_part(prompt_container)
428
+ responses.each { |r| insert_part(prompt_context, r, prompt_context.charset) }
429
+ # prompt_context.add_part(prompt_container)
446
430
  else
447
- responses.each { |r| insert_part(context, r, context.charset) }
431
+ responses.each { |r| insert_part(prompt_context, r, prompt_context.charset) }
448
432
  end
449
433
  end
450
434
 
451
- def insert_part(context, response, charset)
435
+ def insert_part(prompt_context, response, charset)
452
436
  message = ActiveAgent::ActionPrompt::Message.new(
453
437
  content: response[:body],
454
438
  content_type: response[:content_type],
455
439
  charset: charset
456
440
  )
457
- context.add_part(message)
441
+ prompt_context.add_part(message)
458
442
  end
459
443
 
460
444
  # This and #instrument_name is for caching instrument
@@ -11,14 +11,14 @@ module ActiveAgent
11
11
  ruby2_keywords(:initialize)
12
12
 
13
13
  def __getobj__
14
- @prompt_context ||= processed_agent.context
14
+ @prompt_context ||= processed_agent.prompt_context
15
15
  end
16
16
 
17
17
  def __setobj__(prompt_context)
18
18
  @prompt_context = prompt_context
19
19
  end
20
20
 
21
- def context
21
+ def prompt_context
22
22
  __getobj__
23
23
  end
24
24
 
@@ -0,0 +1,142 @@
1
+ # lib/active_agent/generation_provider/anthropic_provider.rb
2
+
3
+ require "anthropic"
4
+ require "active_agent/action_prompt/action"
5
+ require_relative "base"
6
+ require_relative "response"
7
+
8
+ module ActiveAgent
9
+ module GenerationProvider
10
+ class AnthropicProvider < Base
11
+ def initialize(config)
12
+ super
13
+ @api_key = config["api_key"]
14
+ @model_name = config["model"] || "claude-3-5-sonnet-20240620"
15
+ @client = Anthropic::Client.new(access_token: @api_key)
16
+ end
17
+
18
+ def generate(prompt)
19
+ @prompt = prompt
20
+
21
+ chat_prompt(parameters: prompt_parameters)
22
+ rescue => e
23
+ raise GenerationProviderError, e.message
24
+ end
25
+
26
+ def chat_prompt(parameters: prompt_parameters)
27
+ parameters[:stream] = provider_stream if prompt.options[:stream] || config["stream"]
28
+
29
+ chat_response(@client.messages(parameters))
30
+ end
31
+
32
+ private
33
+
34
+ def provider_stream
35
+ agent_stream = prompt.options[:stream]
36
+ message = ActiveAgent::ActionPrompt::Message.new(content: "", role: :assistant)
37
+ @response = ActiveAgent::GenerationProvider::Response.new(prompt: prompt, message:)
38
+
39
+ proc do |chunk|
40
+ if new_content = chunk.dig(:delta, :text)
41
+ message.content += new_content
42
+ agent_stream.call(message) if agent_stream.respond_to?(:call)
43
+ end
44
+ end
45
+ end
46
+
47
+ def prompt_parameters(model: @prompt.options[:model] || @model_name, messages: @prompt.messages, temperature: @config["temperature"] || 0.7, tools: @prompt.actions)
48
+ params = {
49
+ model: model,
50
+ messages: provider_messages(messages),
51
+ temperature: temperature,
52
+ max_tokens: 4096
53
+ }
54
+
55
+ if tools&.present?
56
+ params[:tools] = format_tools(tools)
57
+ end
58
+
59
+ params
60
+ end
61
+
62
+ def format_tools(tools)
63
+ tools.map do |tool|
64
+ {
65
+ name: tool[:name] || tool[:function][:name],
66
+ description: tool[:description],
67
+ input_schema: tool[:parameters]
68
+ }
69
+ end
70
+ end
71
+
72
+ def provider_messages(messages)
73
+ messages.map do |message|
74
+ provider_message = {
75
+ role: convert_role(message.role),
76
+ content: []
77
+ }
78
+
79
+ provider_message[:content] << if message.content_type == "image_url"
80
+ {
81
+ type: "image",
82
+ source: {
83
+ type: "url",
84
+ url: message.content
85
+ }
86
+ }
87
+ else
88
+ {
89
+ type: "text",
90
+ text: message.content
91
+ }
92
+ end
93
+
94
+ provider_message
95
+ end
96
+ end
97
+
98
+ def convert_role(role)
99
+ case role.to_s
100
+ when "system" then "system"
101
+ when "user" then "user"
102
+ when "assistant" then "assistant"
103
+ when "tool", "function" then "assistant"
104
+ else "user"
105
+ end
106
+ end
107
+
108
+ def chat_response(response)
109
+ return @response if prompt.options[:stream]
110
+
111
+ content = response.content.first[:text]
112
+
113
+ message = ActiveAgent::ActionPrompt::Message.new(
114
+ content: content,
115
+ role: "assistant",
116
+ action_requested: response.stop_reason == "tool_use",
117
+ requested_actions: handle_actions(response.tool_use)
118
+ )
119
+
120
+ update_context(prompt: prompt, message: message, response: response)
121
+
122
+ @response = ActiveAgent::GenerationProvider::Response.new(
123
+ prompt: prompt,
124
+ message: message,
125
+ raw_response: response
126
+ )
127
+ end
128
+
129
+ def handle_actions(tool_uses)
130
+ return unless tool_uses&.present?
131
+
132
+ tool_uses.map do |tool_use|
133
+ ActiveAgent::ActionPrompt::Action.new(
134
+ id: tool_use[:id],
135
+ name: tool_use[:name],
136
+ params: tool_use[:input]
137
+ )
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
@@ -64,9 +64,16 @@ module ActiveAgent
64
64
  @response = ActiveAgent::GenerationProvider::Response.new(prompt: prompt, message:)
65
65
 
66
66
  proc do |chunk, bytesize|
67
- if new_content = chunk.dig("choices", 0, "delta", "content")
67
+ if (new_content = chunk.dig("choices", 0, "delta", "content"))
68
68
  message.content += new_content
69
- agent_stream.call(message) if agent_stream.respond_to?(:call)
69
+
70
+ agent_stream.call(message, new_content, false) do |message, new_content|
71
+ yield message, new_content if block_given?
72
+ end
73
+ end
74
+
75
+ agent_stream.call(message, nil, true) do |message|
76
+ yield message, nil if block_given?
70
77
  end
71
78
  end
72
79
  end
@@ -98,6 +105,7 @@ module ActiveAgent
98
105
  end
99
106
 
100
107
  def chat_response(response)
108
+ binding.irb
101
109
  return @response if prompt.options[:stream]
102
110
 
103
111
  message_json = response.dig("choices", 0, "message")
@@ -1,3 +1,3 @@
1
1
  module ActiveAgent
2
- VERSION = "0.2.6.8"
2
+ VERSION = "0.3"
3
3
  end
@@ -30,13 +30,25 @@ module ActiveAgent
30
30
  def create_view_files
31
31
  actions.each do |action|
32
32
  @action = action
33
- @schema_path = File.join("app/views", class_path, file_name, "#{action}.json.jbuilder")
34
- @view_path = File.join("app/views", class_path, file_name, "#{action}.html.#{template_engine}")
35
- template "action.json.jbuilder", @schema_path
36
- template "action.html.#{template_engine}", @view_path
33
+
34
+ # Use configured template engines or fall back to defaults
35
+ json_template_engine = json_template_engine_for_views || "jbuilder"
36
+ html_template_engine = html_template_engine_for_views || "erb"
37
+
38
+ @schema_path = File.join("app/views", class_path, file_name, "#{action}.json.#{json_template_engine}")
39
+ @view_path = File.join("app/views", class_path, file_name, "#{action}.html.#{html_template_engine}")
40
+
41
+ template "action.json.#{json_template_engine}", @schema_path
42
+ template "action.html.#{html_template_engine}", @view_path
37
43
  end
38
44
  end
39
45
 
46
+ def create_layout_files
47
+ # Create the application agent layouts
48
+ template "layout.text.erb", "app/views/layouts/agent.text.erb"
49
+ template "layout.html.erb", "app/views/layouts/agent.html.erb"
50
+ end
51
+
40
52
  private
41
53
 
42
54
  def test_framework
@@ -47,6 +59,17 @@ module ActiveAgent
47
59
  ::Rails.application.config.generators.options[:rails][:template_engine]
48
60
  end
49
61
 
62
+ def json_template_engine_for_views
63
+ # Check if there's a specific JSON template engine configured
64
+ json_engine = ::Rails.application.config.generators.options[:rails][:json_template_engine]
65
+ json_engine || "jbuilder" # Default to jbuilder if not specified
66
+ end
67
+
68
+ def html_template_engine_for_views
69
+ # Use the configured template engine or default to erb
70
+ template_engine || "erb"
71
+ end
72
+
50
73
  def file_name # :doc:
51
74
  @_file_name ||= super + "_agent"
52
75
  end
@@ -12,6 +12,10 @@ module ActiveAgent
12
12
  def create_application_agent
13
13
  template "application_agent.rb", "app/agents/application_agent.rb"
14
14
  end
15
+
16
+ def create_agent_layout_template
17
+ template "agent.text.erb", "app/views/layouts/agent.text.erb"
18
+ end
15
19
  end
16
20
  end
17
21
  end
@@ -0,0 +1 @@
1
+ <%= yield %>
@@ -0,0 +1 @@
1
+ <%= yield %>
@@ -0,0 +1 @@
1
+ <%= yield %>
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :activeagent do
3
+ # # Task goes here
4
+ # 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.6.8
4
+ version: '0.3'
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-03-17 00:00:00.000000000 Z
11
+ date: 2025-03-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -156,6 +156,7 @@ files:
156
156
  - lib/active_agent/generation_methods.rb
157
157
  - lib/active_agent/generation_provider.rb
158
158
  - lib/active_agent/generation_provider/README.md
159
+ - lib/active_agent/generation_provider/anthropic_provider.rb
159
160
  - lib/active_agent/generation_provider/base.rb
160
161
  - lib/active_agent/generation_provider/open_ai_provider.rb
161
162
  - lib/active_agent/generation_provider/response.rb
@@ -179,9 +180,13 @@ files:
179
180
  - lib/generators/active_agent/templates/action.json.jbuilder.tt
180
181
  - lib/generators/active_agent/templates/active_agent.yml
181
182
  - lib/generators/active_agent/templates/agent.rb.tt
183
+ - lib/generators/active_agent/templates/agent.text.erb
182
184
  - lib/generators/active_agent/templates/agent_spec.rb.tt
183
185
  - lib/generators/active_agent/templates/agent_test.rb.tt
184
186
  - lib/generators/active_agent/templates/application_agent.rb.tt
187
+ - lib/generators/active_agent/templates/layout.html.erb
188
+ - lib/generators/active_agent/templates/layout.text.erb
189
+ - lib/tasks/activeagent_tasks.rake
185
190
  homepage: https://activeagents.ai
186
191
  licenses:
187
192
  - MIT