Class: Riffer::Agent

Inherits:
Object
  • Object
show all
Extended by:
Helpers::ClassNameConverter, Helpers::Validations
Includes:
Messages::Converter
Defined in:
lib/riffer/agent.rb

Overview

Riffer::Agent is the base class for all agents in the Riffer framework.

Provides orchestration for LLM calls, tool use, and message management. Subclass this to create your own agents.

See Riffer::Messages and Riffer::Providers.

class MyAgent < Riffer::Agent
  model 'openai/gpt-4o'
  instructions 'You are a helpful assistant.'
end

agent = MyAgent.new
agent.generate('Hello!')

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Messages::Converter

#convert_to_message_object

Constructor Details

#initializeAgent

Initializes a new agent.

Raises Riffer::ArgumentError if the configured model string is invalid (must be “provider/model” format).



115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/riffer/agent.rb', line 115

def initialize
  @messages = []
  @message_callbacks = []
  @model_string = self.class.model
  @instructions_text = self.class.instructions

  provider_name, model_name = @model_string.split("/", 2)

  raise Riffer::ArgumentError, "Invalid model string: #{@model_string}" unless [provider_name, model_name].all? { |part| part.is_a?(String) && !part.strip.empty? }

  @provider_name = provider_name
  @model_name = model_name
end

Instance Attribute Details

#messagesObject (readonly)

The message history for the agent.

Returns Array of Riffer::Messages::Base.



109
110
111
# File 'lib/riffer/agent.rb', line 109

def messages
  @messages
end

Class Method Details

.allObject

Returns all agent subclasses.

Returns Array of Class - all agent subclasses.



101
102
103
# File 'lib/riffer/agent.rb', line 101

def all
  subclasses
end

.find(identifier) ⇒ Object

Finds an agent class by identifier.

identifier

String - the identifier to search for

Returns Class or nil - the agent class, or nil if not found.



94
95
96
# File 'lib/riffer/agent.rb', line 94

def find(identifier)
  subclasses.find { |agent_class| agent_class.identifier == identifier.to_s }
end

.identifier(value = nil) ⇒ Object

Gets or sets the agent identifier.

value

String or nil - the identifier to set, or nil to get

Returns String - the agent identifier.



32
33
34
35
# File 'lib/riffer/agent.rb', line 32

def identifier(value = nil)
  return @identifier || class_name_to_path(name) if value.nil?
  @identifier = value.to_s
end

.instructions(instructions_text = nil) ⇒ Object

Gets or sets the agent instructions.

instructions_text

String or nil - the instructions to set, or nil to get

Returns String - the agent instructions.



53
54
55
56
57
# File 'lib/riffer/agent.rb', line 53

def instructions(instructions_text = nil)
  return @instructions if instructions_text.nil?
  validate_is_string!(instructions_text, "instructions")
  @instructions = instructions_text
end

.model(model_string = nil) ⇒ Object

Gets or sets the model string (e.g., “openai/gpt-4o”).

model_string

String or nil - the model string to set, or nil to get

Returns String - the model string.



42
43
44
45
46
# File 'lib/riffer/agent.rb', line 42

def model(model_string = nil)
  return @model if model_string.nil?
  validate_is_string!(model_string, "model")
  @model = model_string
end

.model_options(options = nil) ⇒ Object

Gets or sets model options passed to generate_text/stream_text.

options

Hash or nil - the options to set, or nil to get

Returns Hash - the model options.



74
75
76
77
# File 'lib/riffer/agent.rb', line 74

def model_options(options = nil)
  return @model_options || {} if options.nil?
  @model_options = options
end

.provider_options(options = nil) ⇒ Object

Gets or sets provider options passed to the provider client.

options

Hash or nil - the options to set, or nil to get

Returns Hash - the provider options.



64
65
66
67
# File 'lib/riffer/agent.rb', line 64

def provider_options(options = nil)
  return @provider_options || {} if options.nil?
  @provider_options = options
end

.uses_tools(tools_or_lambda = nil) ⇒ Object

Gets or sets the tools used by this agent.

tools_or_lambda

Array of Tool classes, Proc, or nil - tools array or lambda returning tools

Returns Array, Proc, or nil - the tools configuration.



84
85
86
87
# File 'lib/riffer/agent.rb', line 84

def uses_tools(tools_or_lambda = nil)
  return @tools_config if tools_or_lambda.nil?
  @tools_config = tools_or_lambda
end

Instance Method Details

#generate(prompt_or_messages, tool_context: nil) ⇒ Object

Generates a response from the agent.

prompt_or_messages

String or Array - a string prompt or array of message hashes/objects

tool_context

Object or nil - optional context object passed to all tool calls

Returns String - the final response content.



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/riffer/agent.rb', line 135

def generate(prompt_or_messages, tool_context: nil)
  @tool_context = tool_context
  @resolved_tools = nil
  initialize_messages(prompt_or_messages)

  loop do
    response = call_llm
    add_message(response)

    break unless has_tool_calls?(response)

    execute_tool_calls(response)
  end

  extract_final_response
end

#on_message(&block) ⇒ Object

Registers a callback to be invoked when messages are added during generation.

block

Block - callback receiving a Riffer::Messages::Base subclass

Raises Riffer::ArgumentError if no block is given.

Returns self for method chaining.



209
210
211
212
213
# File 'lib/riffer/agent.rb', line 209

def on_message(&block)
  raise Riffer::ArgumentError, "on_message requires a block" unless block_given?
  @message_callbacks << block
  self
end

#stream(prompt_or_messages, tool_context: nil) ⇒ Object

Streams a response from the agent.

prompt_or_messages

String or Array - a string prompt or array of message hashes/objects

tool_context

Object or nil - optional context object passed to all tool calls

Returns Enumerator - an enumerator yielding stream events.



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/riffer/agent.rb', line 158

def stream(prompt_or_messages, tool_context: nil)
  @tool_context = tool_context
  @resolved_tools = nil
  initialize_messages(prompt_or_messages)

  Enumerator.new do |yielder|
    loop do
      accumulated_content = ""
      accumulated_tool_calls = []
      current_tool_call = nil

      call_llm_stream.each do |event|
        yielder << event

        case event
        when Riffer::StreamEvents::TextDelta
          accumulated_content += event.content
        when Riffer::StreamEvents::TextDone
          accumulated_content = event.content
        when Riffer::StreamEvents::ToolCallDelta
          current_tool_call ||= {item_id: event.item_id, name: event.name, arguments: ""}
          current_tool_call[:arguments] += event.arguments_delta
          current_tool_call[:name] ||= event.name
        when Riffer::StreamEvents::ToolCallDone
          accumulated_tool_calls << {
            id: event.item_id,
            call_id: event.call_id,
            name: event.name,
            arguments: event.arguments
          }
          current_tool_call = nil
        end
      end

      response = Riffer::Messages::Assistant.new(accumulated_content, tool_calls: accumulated_tool_calls)
      add_message(response)

      break unless has_tool_calls?(response)

      execute_tool_calls(response)
    end
  end
end