class Riffer::Agent
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!’)
Constants
- DEFAULT_MAX_STEPS
- INTERRUPT_MAX_STEPS
Attributes
The message history for the agent.
Cumulative token usage across all LLM calls.
Public Class Methods
Source
# File lib/riffer/agent.rb, line 155 def self.all subclasses end
Returns all agent subclasses.
: () -> Array
Source
# File lib/riffer/agent.rb, line 148 def self.find(identifier) subclasses.find { |agent_class| agent_class.identifier == identifier.to_s } end
Finds an agent class by identifier.
: (String) -> singleton(Riffer::Agent)?
Source
# File lib/riffer/agent.rb, line 164 def self.generate(...) new.generate(...) end
Generates a response using a new agent instance.
See generate for parameters and return value.
: (untyped, *untyped) -> Riffer::Agent::Response
Source
# File lib/riffer/agent.rb, line 185 def self.guardrail(phase, with:, **options) valid_phases = [*Riffer::Guardrails::PHASES, :around] raise Riffer::ArgumentError, "Invalid guardrail phase: #{phase}" unless valid_phases.include?(phase) raise Riffer::ArgumentError, "Guardrail must be a Riffer::Guardrail subclass" unless with.is_a?(Class) && with <= Riffer::Guardrail @guardrails ||= {before: [], after: []} config = {class: with, options: options} case phase when :before @guardrails[:before] << config when :after @guardrails[:after] << config when :around @guardrails[:before] << config @guardrails[:after] << config end end
Registers a guardrail for input, output, or both phases.
+phase+ - :before, :after, or :around. +with+ - the guardrail class (must be subclass of Riffer::Guardrail). +options+ - additional options passed to the guardrail.
Raises Riffer::ArgumentError if phase is invalid or guardrail is not a Guardrail class. : (Symbol, with: singleton(Riffer::Guardrail), **untyped) -> void
Source
# File lib/riffer/agent.rb, line 209 def self.guardrails_for(phase) @guardrails ||= {before: [], after: []} @guardrails[phase] || [] end
Returns the registered guardrail configs for a given phase.
+phase+ - :before or :after.
: (Symbol) -> Array[Hash[Symbol, untyped]]
Source
# File lib/riffer/agent.rb, line 32 def self.identifier(value = nil) return @identifier || class_name_to_path(name) if value.nil? @identifier = value.to_s end
Gets or sets the agent identifier.
: (?String?) -> String
Source
# File lib/riffer/agent.rb, line 64 def self.instructions(instructions_or_proc = nil) return @instructions if instructions_or_proc.nil? if instructions_or_proc.is_a?(Proc) @instructions = instructions_or_proc else validate_is_string!(instructions_or_proc, "instructions") @instructions = instructions_or_proc end end
Gets or sets the agent instructions.
Accepts a static string or a Proc for dynamic instructions. When a Proc is given, it is called at generate time and receives the +context+ hash (which may be +nil+).
instructions “You are a helpful assistant.”
instructions -> (context) { “You are assisting #{context}” }
: (?(String | Proc)?) -> (String | Proc)?
Source
# File lib/riffer/agent.rb, line 114 def self.max_steps(value = nil) return @max_steps || DEFAULT_MAX_STEPS if value.nil? @max_steps = value end
Gets or sets the maximum number of LLM call steps in the tool-use loop.
Defaults to DEFAULT_MAX_STEPS (16). Set to +Float::INFINITY+ for unlimited steps.
: (?Numeric?) -> Numeric
Source
# File lib/riffer/agent.rb, line 40 def self.model(model_string_or_proc = nil) return @model if model_string_or_proc.nil? if model_string_or_proc.is_a?(Proc) @model = model_string_or_proc else validate_is_string!(model_string_or_proc, "model") @model = model_string_or_proc end end
Gets or sets the model string (e.g., “openai/gpt-4o”) or Proc.
: (?(String | Proc)?) -> (String | Proc)?
Source
# File lib/riffer/agent.rb, line 86 def self.model_options(options = nil) return @model_options || {} if options.nil? @model_options = options end
Gets or sets model options passed to generate_text/stream_text.
: (?Hash[Symbol, untyped]?) -> Hash[Symbol, untyped]
Source
# File lib/riffer/agent.rb, line 226 def initialize @messages = [] @message_callbacks = [] @token_usage = nil @interrupted = false @model_config = self.class.model @instructions_config = self.class.instructions if @model_config.is_a?(Proc) @provider_name = nil @model_name = nil else parse_model_string!(@model_config) end end
Initializes a new agent.
Raises Riffer::ArgumentError if the configured model string is invalid (must be “provider/model” format).
: () -> void
Source
# File lib/riffer/agent.rb, line 78 def self.provider_options(options = nil) return @provider_options || {} if options.nil? @provider_options = options end
Gets or sets provider options passed to the provider client.
: (?Hash[Symbol, untyped]?) -> Hash[Symbol, untyped]
Source
# File lib/riffer/agent.rb, line 173 def self.stream(...) new.stream(...) end
Streams a response using a new agent instance.
See stream for parameters and return value.
: (untyped, *untyped) -> Enumerator[Riffer::StreamEvents::Base, void]
Source
# File lib/riffer/agent.rb, line 96 def self.structured_output(params = nil, &block) if block @structured_output = Riffer::Params.new @structured_output.instance_eval(&block) elsif params.nil? @structured_output else raise Riffer::ArgumentError, "structured_output must be a Riffer::Params" unless params.is_a?(Riffer::Params) @structured_output = params end end
Gets or sets the structured output schema for this agent.
Accepts a Riffer::Params instance or a block evaluated against a new Params.
: (?Riffer::Params?) ?{ () -> void } -> Riffer::Params?
Source
# File lib/riffer/agent.rb, line 136 def self.tool_runtime(value = nil) if value.nil? return @tool_runtime if instance_variable_defined?(:@tool_runtime) superclass.respond_to?(:tool_runtime) ? superclass.tool_runtime : nil else @tool_runtime = value end end
Gets or sets the tool runtime for this agent.
Accepts a Riffer::ToolRuntime subclass, a Riffer::ToolRuntime instance, or a Proc.
Inherited by subclasses. When unset, walks the ancestor chain and falls back to the global +Riffer.config.tool_runtime+.
: (?(singleton(Riffer::ToolRuntime) | Riffer::ToolRuntime | Proc)?) -> (singleton(Riffer::ToolRuntime) | Riffer::ToolRuntime | Proc)?
Source
# File lib/riffer/agent.rb, line 122 def self.uses_tools(tools_or_lambda = nil) return @tools_config if tools_or_lambda.nil? @tools_config = tools_or_lambda end
Public Instance Methods
Source
# File lib/riffer/agent.rb, line 245 def generate(prompt_or_messages, files: nil, context: nil) @context = context prepare_run @structured_output = resolve_structured_output initialize_messages(prompt_or_messages, files: files) all_modifications = [] #: Array[Riffer::Guardrails::Modification] tripwire, modifications = run_before_guardrails all_modifications.concat(modifications) return build_response("", tripwire: tripwire, modifications: all_modifications) if tripwire run_generate_loop(all_modifications) end
Generates a response from the agent.
: ((String | Array[Hash[Symbol, untyped] | Riffer::Messages::Base]), ?files: Array[Hash[Symbol, untyped] | Riffer::FilePart]?, ?context: Hash[Symbol, untyped]?) -> Riffer::Agent::Response
Source
# File lib/riffer/agent.rb, line 334 def interrupt!(reason = nil) throw :riffer_interrupt, reason end
Interrupts the agent loop.
Call from an +on_message+ callback to cleanly interrupt the loop. Equivalent to +throw :riffer_interrupt, reason+.
: (?(String | Symbol)?) -> void
Source
# File lib/riffer/agent.rb, line 322 def on_message(&block) raise Riffer::ArgumentError, "on_message requires a block" unless block_given? @message_callbacks << block self end
Registers a callback to be invoked when messages are added during generation.
Raises Riffer::ArgumentError if no block is given.
: () { (Riffer::Messages::Base) -> void } -> self
Source
# File lib/riffer/agent.rb, line 296 def resume(messages: nil, context: nil) restore_state(messages: messages, context: context) @structured_output = resolve_structured_output run_generate_loop(resume: true) end
Resumes an agent loop.
When called without +messages+, continues using the existing in-memory message history. When called with +messages+, reconstructs the agent state from persisted data (useful for cross-process resume).
Skips message initialization and before guardrails in both cases. The step offset is derived automatically from the number of assistant messages so +max_steps+ is enforced across the full session.
: (?messages: Array[Hash[Symbol, untyped] | Riffer::Messages::Base]?, ?context: Hash[Symbol, untyped]?) -> Riffer::Agent::Response
Source
# File lib/riffer/agent.rb, line 307 def resume_stream(messages: nil, context: nil) raise Riffer::ArgumentError, "Structured output is not supported with streaming. Use #resume instead." if self.class.structured_output restore_state(messages: messages, context: context) Enumerator.new do |yielder| run_stream_loop(yielder, resume: true) end end
Resumes an agent loop in streaming mode.
Same as +resume+ but returns an Enumerator yielding stream events.
: (?messages: Array[Hash[Symbol, untyped] | Riffer::Messages::Base]?, ?context: Hash[Symbol, untyped]?) -> Enumerator[Riffer::StreamEvents::Base, void]
Source
# File lib/riffer/agent.rb, line 265 def stream(prompt_or_messages, files: nil, context: nil) raise Riffer::ArgumentError, "Structured output is not supported with streaming. Use #generate instead." if self.class.structured_output @context = context prepare_run initialize_messages(prompt_or_messages, files: files) Enumerator.new do |yielder| tripwire, modifications = run_before_guardrails modifications.each { |m| yielder << Riffer::StreamEvents::GuardrailModification.new(m) } if tripwire yielder << Riffer::StreamEvents::GuardrailTripwire.new(tripwire) next end run_stream_loop(yielder) end end
Streams a response from the agent.
Raises Riffer::ArgumentError if structured output is configured.
: ((String | Array[Hash[Symbol, untyped] | Riffer::Messages::Base]), ?files: Array[Hash[Symbol, untyped] | Riffer::FilePart]?, ?context: Hash[Symbol, untyped]?) -> Enumerator[Riffer::StreamEvents::Base, void]