Skip to content

Callbacks

Callbacks can be registered to execute on specific events during the prompt and response cycle. This allows you to perform actions such as logging, modifying prompts, or triggering additional processes based on the agent's activity.

Action Callbacks

Action callbacks are triggered when an action is invoked within an agent. This allows you to customize the behavior of actions, such as modifying the action's parameters or logging the action's execution. Great for retrieval augmented generation (RAG) workflows.

ruby
before_action :set_context

private
def set_context
  # Logic to set the context for the action
  @context_set = true
  prompt_context.instructions = "Context has been set"
end

Generation Callbacks

Generation callbacks are executed during the generation process of an agent. This allows you to modify the prompt, handle responses, or perform additional processing based on the generated content.

ruby
after_generation :process_response

private
def process_response
  # Access the generation provider response
  @response_data = generation_provider.response
end

Around Generation Callbacks

Around generation callbacks wrap the entire generation process, allowing you to perform setup and teardown operations. This is useful for timing, caching, logging, or any operation that needs to wrap the generation process.

Basic Around Generation

The around_generation callback wraps the generation with custom logic:

ruby
class AgentWithAroundGeneration < ActiveAgent::Base
  layout "agent"

  generate_with :openai, model: "gpt-4o-mini", instructions: "You are a helpful assistant."

  around_generation :track_generation_calls

  def test_action
    prompt_context(message: "Generate a test response")
  end

  def another_action
    prompt_context(message: "Generate another response")
  end

  private

  def track_generation_calls
    # Track how many times generation is called
    self.class.instance_variable_set(:@generation_count, (self.class.instance_variable_get(:@generation_count) || 0) + 1)

    # Store agent instance in context for tracking
    context.agent_instance.instance_variable_set(:@called_at, Time.current)

    # Execute the actual generation
    yield
  end
end

Testing Around Generation

The callback tracks generation calls and timing:

ruby
test "around_generation wraps the generation process" do
  VCR.use_cassette("openai_prompt_context_response") do
    # First call - should execute the generation
    generation_result = @agent_class.test_action.generate_now

    assert generation_result.message.content.present?
    assert_equal 1, @agent_class.instance_variable_get(:@generation_count)

    # Check that the agent instance was tracked
    assert generation_result.prompt.agent_instance.instance_variable_get(:@called_at).present?
  end
end

Around Generation with Conditions

You can apply around_generation callbacks conditionally using :only and :except options:

ruby
class AgentWithAroundGenerationAndConditions < ActiveAgent::Base
  layout "agent"

  generate_with :openai, model: "gpt-4o-mini", instructions: "You are a helpful assistant."

  around_generation :log_timing, only: :timed_action
  around_generation :cache_llm_request, except: :uncached_action

  def timed_action
    prompt_context(message: "Timed response")
  end

  def cached_action
    prompt_context(message: "Cached response")
  end

  def uncached_action
    prompt_context(message: "Uncached response")
  end

  private

  def log_timing
    start_time = Time.current
    result = yield
    @generation_time = Time.current - start_time
    # Store timing in context for test access through agent_instance
    context.agent_instance.instance_variable_set(:@generation_time, @generation_time)
    result
  end

  def cache_llm_request
    # Simple tracking for the test
    @cached_actions ||= []
    @cached_actions << action_name

    # Store in context for test access
    context.agent_instance.instance_variable_set(:@cached_actions, @cached_actions)

    yield
  end
end

This pattern is useful for:

  • Performance monitoring: Track generation times for specific actions
  • Caching: Cache LLM responses for expensive operations
  • Rate limiting: Implement custom rate limiting logic
  • Debugging: Log detailed information about specific generations

On Stream Callbacks

On stream callbacks are triggered during the streaming of responses from an agent. This allows you to handle real-time updates, such as displaying partial responses in a user interface or logging the progress of the response generation.

Streaming Implementation

The streaming agent demonstrates real-time response handling:

ruby
class StreamingAgent < ApplicationAgent
  layout "agent"
  generate_with :openai,
    model: "gpt-4.1-nano",
    instructions: "You're a chat agent. Your job is to help users with their questions.",
    stream: true

  on_stream :broadcast_message

  private

  def broadcast_message
    response = generation_provider.response

    # Broadcast the message to the specified channel
    ActionCable.server.broadcast(
      "#{response.message.generation_id}_messages",
      partial: "streaming_agent/message",
      locals: { message: response.message.content, scroll_to: true }
    )
  end
end

Testing Streaming

The streaming functionality broadcasts each chunk as it arrives:

ruby
StreamingAgent.with(message: "Stream this message").prompt_context.generate_now

In this test, the agent broadcasts 30 chunks during the streaming response.