Class: RubyPi::Agent::Core

Inherits:
Object
  • Object
show all
Includes:
EventEmitter
Defined in:
lib/ruby_pi/agent/core.rb

Overview

The main agent class. Wraps State, Loop, and EventEmitter into a cohesive interface for running agentic LLM interactions with tool use, streaming, and lifecycle hooks.

Examples:

Full lifecycle

agent = RubyPi::Agent::Core.new(
  system_prompt: "You are Olli, an AI assistant.",
  model: RubyPi::LLM.model(:gemini, "gemini-2.0-flash"),
  tools: registry,
  max_iterations: 10,
  before_tool_call: ->(tc) { puts "Calling #{tc.name}" },
  after_tool_call: ->(tc, r) { puts "Done: #{r.success?}" }
)

agent.on(:text_delta) { |d| stream.write(d[:content]) }
agent.on(:agent_end) { |_| stream.close }

result = agent.run("Create a LinkedIn post")
result = agent.continue("Make it shorter")

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from EventEmitter

#emit, #off, #on

Constructor Details

#initialize(system_prompt:, model:, tools: nil, max_iterations: 10, transform_context: nil, before_tool_call: nil, after_tool_call: nil, compaction: nil, user_data: {}) ⇒ Core

Creates a new Agent instance.

Parameters:

  • system_prompt (String)

    the system-level instruction prompt

  • model (RubyPi::LLM::BaseProvider)

    the LLM provider instance

  • tools (RubyPi::Tools::Registry, nil) (defaults to: nil)

    tool registry

  • max_iterations (Integer) (defaults to: 10)

    max think-act-observe cycles (default: 10)

  • transform_context (Proc, nil) (defaults to: nil)

    context transform hook

  • before_tool_call (Proc, nil) (defaults to: nil)

    pre-tool-execution hook

  • after_tool_call (Proc, nil) (defaults to: nil)

    post-tool-execution hook

  • compaction (RubyPi::Context::Compaction, nil) (defaults to: nil)

    compaction strategy

  • user_data (Hash) (defaults to: {})

    arbitrary data bag for transforms/extensions



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/ruby_pi/agent/core.rb', line 59

def initialize(
  system_prompt:,
  model:,
  tools: nil,
  max_iterations: 10,
  transform_context: nil,
  before_tool_call: nil,
  after_tool_call: nil,
  compaction: nil,
  user_data: {}
)
  @state = State.new(
    system_prompt: system_prompt,
    model: model,
    tools: tools,
    max_iterations: max_iterations,
    transform_context: transform_context,
    before_tool_call: before_tool_call,
    after_tool_call: after_tool_call,
    user_data: user_data
  )
  @compaction = compaction
  @extensions = []
end

Instance Attribute Details

#stateRubyPi::Agent::State (readonly)

Returns the agent’s mutable state.

Returns:



46
47
48
# File 'lib/ruby_pi/agent/core.rb', line 46

def state
  @state
end

Instance Method Details

#continue(prompt) ⇒ RubyPi::Agent::Result

Continues the conversation with a follow-up user message. Preserves the existing conversation history and appends the new prompt before resuming the loop.

Parameters:

  • prompt (String)

    the follow-up user message

Returns:



101
102
103
104
105
106
# File 'lib/ruby_pi/agent/core.rb', line 101

def continue(prompt)
  # Reset the iteration counter for the new run while keeping history
  @state.instance_variable_set(:@iteration, 0)
  @state.add_message(role: :user, content: prompt)
  execute_loop
end

#run(prompt) ⇒ RubyPi::Agent::Result

Runs the agent with an initial user prompt. Adds the prompt to the conversation history, executes the think-act-observe loop, emits :agent_end when done, and returns the result.

Parameters:

  • prompt (String)

    the user’s initial message

Returns:



90
91
92
93
# File 'lib/ruby_pi/agent/core.rb', line 90

def run(prompt)
  @state.add_message(role: :user, content: prompt)
  execute_loop
end

#use(extension_class) ⇒ void

This method returns an undefined value.

Registers an extension with this agent. The extension’s hooks are automatically subscribed to the agent’s events.

Parameters:

  • extension_class (Class)

    a subclass of RubyPi::Extensions::Base

Raises:

  • (ArgumentError)

    if the argument is not a valid extension class



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/ruby_pi/agent/core.rb', line 114

def use(extension_class)
  unless extension_class.respond_to?(:hooks)
    raise ArgumentError,
          "Expected an extension class with a .hooks method, got #{extension_class.inspect}"
  end

  # Subscribe each hook to the corresponding event
  extension_class.hooks.each do |event, handlers|
    handlers.each do |handler|
      on(event) do |data|
        handler.call(data, self)
      end
    end
  end

  @extensions << extension_class
end