Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 26 additions & 11 deletions lib/active_agent/action_prompt/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
require "active_support/core_ext/string/inflections"
require "active_support/core_ext/hash/except"
require "active_support/core_ext/module/anonymous"
require "active_agent/action_prompt/message"
require "active_agent/action_prompt/action"

# require "active_agent/log_subscriber"
require "active_agent/rescuable"
Expand Down Expand Up @@ -218,7 +220,8 @@ def perform_generation
def handle_response(response)
return response unless response.message.requested_actions.present?

# Perform the requested actions
# The assistant message with tool_calls is already added by update_context in the provider
# Now perform the requested actions which will add tool response messages
perform_actions(requested_actions: response.message.requested_actions)

# Continue generation with updated context
Expand All @@ -242,24 +245,36 @@ def perform_actions(requested_actions:)
end

def perform_action(action)
current_context = context.clone
# Merge action params with original params to preserve context
original_params = current_context.params || {}
# Save the current messages to preserve conversation history
original_messages = context.messages.dup
original_params = context.params || {}

if action.params.is_a?(Hash)
self.params = original_params.merge(action.params)
else
self.params = original_params
end

# Save the current prompt_was_called state and reset it so the action can render
original_prompt_was_called = @_prompt_was_called
@_prompt_was_called = false

# Process the action, which will render the view and populate context
process(action.name)
context.message.role = :tool
context.message.action_id = action.id
context.message.action_name = action.name
context.message.generation_id = action.id
current_context.message = context.message
current_context.messages << context.message
self.context = current_context

# The action should have called prompt which populates context.message
# Create a tool message from the rendered response
tool_message = context.message.dup
tool_message.role = :tool
tool_message.action_id = action.id
tool_message.action_name = action.name
tool_message.generation_id = action.id

# Restore the messages with the new tool message
context.messages = original_messages + [ tool_message ]

# Restore the prompt_was_called state
@_prompt_was_called = original_prompt_was_called
end

def initialize # :nodoc:
Expand Down
22 changes: 16 additions & 6 deletions lib/active_agent/action_prompt/prompt.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,12 @@ def initialize(attributes = {})
@action_name = attributes.fetch(:action_name, nil)
@mcp_servers = attributes.fetch(:mcp_servers, [])
set_message if attributes[:message].is_a?(String) || @body.is_a?(String) && @message&.content
set_messages if @instructions.present?
# Ensure we have a system message with instructions at the start
if @messages.empty? || @messages[0].role != :system
@messages.unshift(instructions_message)
elsif @instructions.present?
@messages[0] = instructions_message
end
end

def multimodal?
Expand All @@ -39,17 +44,22 @@ def multimodal?

def messages=(messages)
@messages = messages
set_messages
# Only add system message if we have instructions and don't already have a system message
if @instructions.present? && (@messages.empty? || @messages.first&.role != :system)
set_messages
end
end

def instructions=(instructions)
return if instructions.blank?
# Store the instructions even if blank (will use empty string)
@instructions = instructions || ""

@instructions = instructions
# Update or add the system message
if @messages[0].present? && @messages[0].role == :system
@messages[0] = instructions_message
else
set_messages
elsif @messages.empty? || @messages[0].role != :system
# Only add system message if we don't have one at the start
@messages.unshift(instructions_message)
end
end

Expand Down
4 changes: 2 additions & 2 deletions lib/active_agent/generation_provider/message_formatting.rb
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,12 @@ def format_tool_calls(actions)
def format_single_tool_call(action)
# Default tool call format (OpenAI style)
{
id: action.id,
type: "function",
function: {
name: action.name,
arguments: action.params.is_a?(String) ? action.params : action.params.to_json
},
id: action.id
}
}
end
end
Expand Down
1 change: 0 additions & 1 deletion lib/generators/erb/install_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ class InstallGenerator < ::Rails::Generators::Base # :nodoc:
def create_agent_layouts
if behavior == :invoke
formats.each do |format|
puts format
layout_path = File.join("app/views/layouts", filename_with_extensions("agent", format))
template filename_with_extensions(:layout, format), layout_path unless File.exist?(layout_path)
end
Expand Down
Loading