Class: Riffer::Agent
- Inherits:
-
Object
- Object
- Riffer::Agent
- 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!')
Defined Under Namespace
Classes: Response
Constant Summary collapse
- DEFAULT_MAX_STEPS =
: Integer
16- INTERRUPT_MAX_STEPS =
: Symbol
:max_steps
Constants included from Helpers::ClassNameConverter
Helpers::ClassNameConverter::DEFAULT_SEPARATOR
Instance Attribute Summary collapse
-
#messages ⇒ Object
readonly
The message history for the agent.
-
#token_usage ⇒ Object
readonly
Cumulative token usage across all LLM calls.
Class Method Summary collapse
-
.all ⇒ Object
Returns all agent subclasses.
-
.find(identifier) ⇒ Object
Finds an agent class by identifier.
-
.generate ⇒ Object
Generates a response using a new agent instance.
-
.guardrail(phase, with:, **options) ⇒ Object
Registers a guardrail for input, output, or both phases.
-
.guardrails_for(phase) ⇒ Object
Returns the registered guardrail configs for a given phase.
-
.identifier(value = nil) ⇒ Object
Gets or sets the agent identifier.
-
.instructions(instructions_or_proc = nil) ⇒ Object
Gets or sets the agent instructions.
-
.max_steps(value = nil) ⇒ Object
Gets or sets the maximum number of LLM call steps in the tool-use loop.
-
.model(model_string_or_proc = nil) ⇒ Object
Gets or sets the model string (e.g., “openai/gpt-4o”) or Proc.
-
.model_options(options = nil) ⇒ Object
Gets or sets model options passed to generate_text/stream_text.
-
.provider_options(options = nil) ⇒ Object
Gets or sets provider options passed to the provider client.
-
.resolved_tool_classes(context: nil) ⇒ Object
Returns the tool classes the LLM should see for this agent.
-
.skills(&block) ⇒ Object
Configures skills for this agent via a block DSL.
-
.stream ⇒ Object
Streams a response using a new agent instance.
-
.structured_output(params = nil, &block) ⇒ Object
Gets or sets the structured output schema for this agent.
-
.tool_runtime(value = nil) ⇒ Object
Gets or sets the tool runtime for this agent.
-
.uses_tools(tools_or_lambda = nil) ⇒ Object
Gets or sets the tools used by this agent.
Instance Method Summary collapse
-
#generate(prompt_or_messages, files: nil, context: nil) ⇒ Object
Generates a response from the agent.
-
#generate_instruction_message(context: nil) ⇒ Object
Generates the instruction system message for this agent.
-
#generate_skills_message(context: nil) ⇒ Object
Generates the skills catalog system message for this agent.
-
#initialize ⇒ Agent
constructor
Initializes a new agent.
-
#interrupt!(reason = nil) ⇒ Object
Interrupts the agent loop.
-
#on_message(&block) ⇒ Object
Registers a callback to be invoked when messages are added during generation.
-
#stream(prompt_or_messages, files: nil, context: nil) ⇒ Object
Streams a response from the agent.
Methods included from Helpers::ClassNameConverter
Methods included from Helpers::Validations
Methods included from Messages::Converter
#convert_to_file_part, #convert_to_message_object
Constructor Details
#initialize ⇒ Agent
Initializes a new agent.
Raises Riffer::ArgumentError if the configured model string is invalid (must be “provider/model” format).
– : () -> void
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 |
# File 'lib/riffer/agent.rb', line 302 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 |
Instance Attribute Details
#messages ⇒ Object (readonly)
The message history for the agent.
290 291 292 |
# File 'lib/riffer/agent.rb', line 290 def @messages end |
#token_usage ⇒ Object (readonly)
Cumulative token usage across all LLM calls.
293 294 295 |
# File 'lib/riffer/agent.rb', line 293 def token_usage @token_usage end |
Class Method Details
.all ⇒ Object
Returns all agent subclasses.
– : () -> Array
226 227 228 |
# File 'lib/riffer/agent.rb', line 226 def self.all subclasses #: Array[singleton(Riffer::Agent)] end |
.find(identifier) ⇒ Object
Finds an agent class by identifier.
– : (String) -> singleton(Riffer::Agent)?
218 219 220 |
# File 'lib/riffer/agent.rb', line 218 def self.find(identifier) all.find { |agent_class| agent_class.identifier == identifier.to_s } end |
.generate ⇒ Object
Generates a response using a new agent instance.
See #generate for parameters and return value.
– : (*untyped, **untyped) -> Riffer::Agent::Response
236 237 238 |
# File 'lib/riffer/agent.rb', line 236 def self.generate(...) new.generate(...) end |
.guardrail(phase, with:, **options) ⇒ Object
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
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 |
# File 'lib/riffer/agent.rb', line 259 def self.guardrail(phase, with:, **) 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: } case phase when :before @guardrails[:before] << config when :after @guardrails[:after] << config when :around @guardrails[:before] << config @guardrails[:after] << config end end |
.guardrails_for(phase) ⇒ Object
Returns the registered guardrail configs for a given phase.
- phase
-
:before or :after.
– : (Symbol) -> Array[Hash[Symbol, untyped]]
284 285 286 287 |
# File 'lib/riffer/agent.rb', line 284 def self.guardrails_for(phase) @guardrails ||= {before: [], after: []} @guardrails[phase] || [] end |
.identifier(value = nil) ⇒ Object
Gets or sets the agent identifier.
– : (?String?) -> String
33 34 35 36 |
# File 'lib/riffer/agent.rb', line 33 def self.identifier(value = nil) return @identifier || class_name_to_path(name) if value.nil? @identifier = value.to_s end |
.instructions(instructions_or_proc = nil) ⇒ Object
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[:name]}"
}
– : (?(String | Proc)?) -> (String | Proc)?
67 68 69 70 71 72 73 74 75 76 |
# File 'lib/riffer/agent.rb', line 67 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 |
.max_steps(value = nil) ⇒ Object
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
121 122 123 124 |
# File 'lib/riffer/agent.rb', line 121 def self.max_steps(value = nil) return @max_steps || DEFAULT_MAX_STEPS if value.nil? @max_steps = value end |
.model(model_string_or_proc = nil) ⇒ Object
Gets or sets the model string (e.g., “openai/gpt-4o”) or Proc.
– : (?(String | Proc)?) -> (String | Proc)?
42 43 44 45 46 47 48 49 50 51 |
# File 'lib/riffer/agent.rb', line 42 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 |
.model_options(options = nil) ⇒ Object
Gets or sets model options passed to generate_text/stream_text.
– : (?Hash[Symbol, untyped]?) -> Hash[Symbol, untyped]
91 92 93 94 |
# File 'lib/riffer/agent.rb', line 91 def self.( = nil) return @model_options || {} if .nil? @model_options = end |
.provider_options(options = nil) ⇒ Object
Gets or sets provider options passed to the provider client.
– : (?Hash[Symbol, untyped]?) -> Hash[Symbol, untyped]
82 83 84 85 |
# File 'lib/riffer/agent.rb', line 82 def self.( = nil) return @provider_options || {} if .nil? @provider_options = end |
.resolved_tool_classes(context: nil) ⇒ Object
Returns the tool classes the LLM should see for this agent.
Class-level companion to the instance #resolved_tools. Resolves the Proc form of uses_tools and appends the skill activation tool when a skills block is configured. Does not read the skills backend —the LLM-facing tool schema reflects class-level intent, not the runtime state of any backend.
When uses_tools is a Proc, context is forwarded to it.
The activation tool class is resolved from the agent’s skills do; activate_tool ...; end override when set, otherwise from Riffer.config.skills.default_activate_tool.
Raises Riffer::ArgumentError on tool name conflicts with the skill activation tool.
– : (?context: Hash[Symbol, untyped]?) -> Array
154 155 156 157 158 159 160 161 162 163 |
# File 'lib/riffer/agent.rb', line 154 def self.resolved_tool_classes(context: nil) base = resolve_uses_tools_config(context) return base unless skills skill_activate_tool_class = skills.activate_tool || Riffer.config.skills.default_activate_tool if base.any? { |t| t.name == skill_activate_tool_class.name } raise Riffer::ArgumentError, "Tool name conflict with skill tools: #{skill_activate_tool_class.name}" end base + [skill_activate_tool_class] end |
.skills(&block) ⇒ Object
Configures skills for this agent via a block DSL.
Returns the current Riffer::Skills::Config when called without a block.
skills do
backend Riffer::Skills::FilesystemBackend.new(".skills")
adapter Riffer::Skills::XmlAdapter
activate ["code-review"]
end
– : () ?{ () -> void } -> Riffer::Skills::Config?
206 207 208 209 210 211 212 |
# File 'lib/riffer/agent.rb', line 206 def self.skills(&block) if block @skills_config = Riffer::Skills::Config.new @skills_config.instance_eval(&block) end @skills_config end |
.stream ⇒ Object
Streams a response using a new agent instance.
See #stream for parameters and return value.
– : (*untyped, **untyped) -> Enumerator[Riffer::StreamEvents::Base, void]
246 247 248 |
# File 'lib/riffer/agent.rb', line 246 def self.stream(...) new.stream(...) end |
.structured_output(params = nil, &block) ⇒ Object
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?
102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/riffer/agent.rb', line 102 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 |
.tool_runtime(value = nil) ⇒ Object
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)?
185 186 187 188 189 190 191 192 |
# File 'lib/riffer/agent.rb', line 185 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 |
.uses_tools(tools_or_lambda = nil) ⇒ Object
130 131 132 133 |
# File 'lib/riffer/agent.rb', line 130 def self.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, files: nil, context: nil) ⇒ Object
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
322 323 324 325 326 327 328 329 330 331 332 333 334 335 |
# File 'lib/riffer/agent.rb', line 322 def generate(, files: nil, context: nil) @context = context prepare_run @structured_output = resolve_structured_output (, 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 |
#generate_instruction_message(context: nil) ⇒ Object
Generates the instruction system message for this agent.
Useful for database persistence workflows where the system messages need to be stored independently.
Returns nil when no instructions are configured.
– : (?context: Hash[Symbol, untyped]?) -> Riffer::Messages::System?
384 385 386 |
# File 'lib/riffer/agent.rb', line 384 def (context: nil) (context) end |
#generate_skills_message(context: nil) ⇒ Object
Generates the skills catalog system message for this agent.
Useful for database persistence workflows where the system messages need to be stored independently.
Returns nil when no skills are configured or the catalog is empty.
– : (?context: Hash[Symbol, untyped]?) -> Riffer::Messages::System?
397 398 399 |
# File 'lib/riffer/agent.rb', line 397 def (context: nil) (resolve_skills(context)) end |
#interrupt!(reason = nil) ⇒ Object
Interrupts the agent loop.
Call from an on_message callback to cleanly interrupt the loop. Equivalent to throw :riffer_interrupt, reason.
– : (?(String | Symbol)?) -> void
408 409 410 |
# File 'lib/riffer/agent.rb', line 408 def interrupt!(reason = nil) throw :riffer_interrupt, reason end |
#on_message(&block) ⇒ Object
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
369 370 371 372 373 |
# File 'lib/riffer/agent.rb', line 369 def (&block) raise Riffer::ArgumentError, "on_message requires a block" unless block_given? @message_callbacks << block self end |
#stream(prompt_or_messages, files: nil, context: nil) ⇒ Object
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]
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 |
# File 'lib/riffer/agent.rb', line 343 def stream(, 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 (, 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 |