Class: Riffer::Agent
- Inherits:
-
Object
- Object
- Riffer::Agent
- Extended by:
- Helpers::ClassNameConverter
- 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
Modules: Run Classes: Config, Context, Response, Session, StructuredOutput
Constant Summary collapse
- INTERRUPT_MAX_STEPS =
: Symbol
:max_steps
Constants included from Helpers::ClassNameConverter
Helpers::ClassNameConverter::DEFAULT_SEPARATOR
Instance Attribute Summary collapse
-
#config ⇒ Object
readonly
The per-instance Riffer::Agent::Config.
-
#context ⇒ Object
readonly
The mutable runtime context, a
Riffer::Agent::Contextvalue object threaded into every Proc-based DSL setting, guardrail, tool runtime, and skills resolution, and shared with everyRiffer::Agent::Runthis agent executes. -
#instruction_message ⇒ Object
readonly
The system message built from the configured
instructions, ornilwhen no instructions are configured. -
#model_name ⇒ Object
readonly
The resolved model name (the part after “provider/”), used as the model argument on every LLM call.
-
#provider ⇒ Object
readonly
The provider client.
-
#session ⇒ Object
readonly
The conversation handle.
-
#skills_message ⇒ Object
readonly
The system message describing the configured skills catalog, or
nilwhen skills are unconfigured or the catalog is empty. -
#structured_output ⇒ Object
readonly
The
Riffer::Agent::StructuredOutputwrapping the configured schema, ornilwhen structured output is not configured. -
#tool_runtime ⇒ Object
readonly
The tool runtime instance used to execute tool calls.
-
#tools ⇒ Object
readonly
The tool classes the LLM sees on every call this agent makes.
Class Method Summary collapse
-
.all ⇒ Object
Returns all agent subclasses.
-
.config ⇒ Object
Returns the per-class Riffer::Agent::Config value object holding every DSL setting.
-
.find(identifier) ⇒ Object
Finds an agent class by identifier.
-
.generate(prompt = nil, files: nil, context: nil) ⇒ 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(value = 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.
-
.mcp_configs ⇒ Object
Returns the accumulated
use_mcpconfigurations for this agent class. -
.model(value = 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.
-
.skills(&block) ⇒ Object
Configures skills for this agent via a block DSL.
-
.stream(prompt = nil, files: nil, context: nil) ⇒ 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.
-
.use_mcp(tag) ⇒ Object
Opts this agent into tools from all MCP registrations that share any of the given tag(s).
-
.uses_tools(value = nil) ⇒ Object
Gets or sets the tools used by this agent.
Instance Method Summary collapse
-
#generate(prompt = nil, files: nil) ⇒ Object
Generates a response from the agent.
-
#initialize(session: nil, context: nil, config: nil) ⇒ Agent
constructor
Initializes a new agent.
-
#interrupt!(reason = nil) ⇒ Object
Interrupts the agent loop.
-
#stream(prompt = nil, files: nil) ⇒ Object
Streams a response from the agent.
Methods included from Helpers::ClassNameConverter
Methods included from Messages::Converter
#convert_to_file_part, #convert_to_message_object
Constructor Details
#initialize(session: nil, context: nil, config: nil) ⇒ Agent
Initializes a new agent.
When session: is omitted, a fresh Riffer::Agent::Session is built and seeded with the system instruction message and skills catalog (when configured), using context:. When session: is provided, the agent uses it as-is —the caller is responsible for the session’s contents (typical use case: cross-process resume from persisted history). With Riffer.config.experimental_history_healing on, a provided session is healed at construction time so the tool_use ↔ tool_result invariant holds before the next inference call.
context: flows through Proc-based instructions, model, skills resolution, tool resolution, guardrails, and tool runtime. It is fixed for the lifetime of the agent.
Raises Riffer::ArgumentError if the configured model string is invalid (must be “provider/model” format).
– : (?session: Riffer::Agent::Session?, ?context: Hash[Symbol, untyped]?, ?config: Riffer::Agent::Config?) -> void
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
# File 'lib/riffer/agent.rb', line 307 def initialize(session: nil, context: nil, config: nil) @config = config || self.class.config @context = Riffer::Agent::Context.new(context || {}) provider_class, @model_name = resolve_provider_and_model @provider = provider_class.new(**@config.) @context.skills = resolve_skills(provider_class) @structured_output = resolve_structured_output @tools = resolve_tools @tool_runtime = resolve_tool_runtime @instruction_message = @skills_message = @session = session || Riffer::Agent::Session.new(messages: [@instruction_message, @skills_message].compact) @session.set(Riffer::Agent::Session::Repair.prune_orphans(@session.)) end |
Instance Attribute Details
#config ⇒ Object (readonly)
The per-instance Riffer::Agent::Config. Either the class-level default or an explicit Config passed to Agent.new(config:).
237 238 239 |
# File 'lib/riffer/agent.rb', line 237 def config @config end |
#context ⇒ Object (readonly)
The mutable runtime context, a Riffer::Agent::Context value object threaded into every Proc-based DSL setting, guardrail, tool runtime, and skills resolution, and shared with every Riffer::Agent::Run this agent executes. Exposes:
-
context.skills— the resolvedRiffer::Skills::Context(when skills are configured), set atAgent.newtime. -
context.token_usage— the cumulativeRiffer::Providers::TokenUsage, updated by each Run as the loop progresses. -
context[:key]/context.dig(:key)— Hash-style reads for caller-provided keys (e.g.context[:agent],context[:tenant]).:skillsand:token_usageare reserved and cannot be passed by the caller.
262 263 264 |
# File 'lib/riffer/agent.rb', line 262 def context @context end |
#instruction_message ⇒ Object (readonly)
The system message built from the configured instructions, or nil when no instructions are configured. Built once at Agent.new using the constructor context: and cached. Useful for persistence flows.
242 243 244 |
# File 'lib/riffer/agent.rb', line 242 def @instruction_message end |
#model_name ⇒ Object (readonly)
The resolved model name (the part after “provider/”), used as the model argument on every LLM call. Resolved eagerly at Agent.new.
266 267 268 |
# File 'lib/riffer/agent.rb', line 266 def model_name @model_name end |
#provider ⇒ Object (readonly)
The provider client. Built eagerly at Agent.new from the configured provider class and Config#provider_options, then handed to every Riffer::Agent::Run this agent executes. Public so tests can pre-queue responses on Riffer::Providers::Mock before calling #generate.
272 273 274 |
# File 'lib/riffer/agent.rb', line 272 def provider @provider end |
#session ⇒ Object (readonly)
The conversation handle. See Riffer::Agent::Session.
233 234 235 |
# File 'lib/riffer/agent.rb', line 233 def session @session end |
#skills_message ⇒ Object (readonly)
The system message describing the configured skills catalog, or nil when skills are unconfigured or the catalog is empty. Built once at Agent.new and cached.
247 248 249 |
# File 'lib/riffer/agent.rb', line 247 def @skills_message end |
#structured_output ⇒ Object (readonly)
The Riffer::Agent::StructuredOutput wrapping the configured schema, or nil when structured output is not configured. Resolved eagerly at Agent.new.
276 277 278 |
# File 'lib/riffer/agent.rb', line 276 def structured_output @structured_output end |
#tool_runtime ⇒ Object (readonly)
The tool runtime instance used to execute tool calls. Resolved eagerly at Agent.new (Proc-form tool_runtime is called against context once).
285 286 287 |
# File 'lib/riffer/agent.rb', line 285 def tool_runtime @tool_runtime end |
#tools ⇒ Object (readonly)
The tool classes the LLM sees on every call this agent makes. Resolved eagerly at Agent.new (Proc-form uses_tools is called against context once; MCP tools and the skill_activate tool are merged in).
281 282 283 |
# File 'lib/riffer/agent.rb', line 281 def tools @tools end |
Class Method Details
.all ⇒ Object
Returns all agent subclasses.
– : () -> Array
183 184 185 |
# File 'lib/riffer/agent.rb', line 183 def self.all subclasses #: Array[singleton(Riffer::Agent)] end |
.config ⇒ Object
Returns the per-class Riffer::Agent::Config value object holding every DSL setting. Lazily initialized on first read; each subclass has its own.
– : () -> Riffer::Agent::Config
34 35 36 |
# File 'lib/riffer/agent.rb', line 34 def self.config @config ||= Riffer::Agent::Config.new end |
.find(identifier) ⇒ Object
Finds an agent class by identifier.
– : (String) -> singleton(Riffer::Agent)?
175 176 177 |
# File 'lib/riffer/agent.rb', line 175 def self.find(identifier) all.find { |agent_class| agent_class.identifier == identifier.to_s } end |
.generate(prompt = nil, files: nil, context: nil) ⇒ Object
Generates a response using a new agent instance.
context: is threaded into new; prompt and files: are forwarded to #generate.
– : (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?context: Hash[Symbol, untyped]?) -> Riffer::Agent::Response
194 195 196 |
# File 'lib/riffer/agent.rb', line 194 def self.generate(prompt = nil, files: nil, context: nil) new(context: context).generate(prompt, files: files) 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
218 219 220 |
# File 'lib/riffer/agent.rb', line 218 def self.guardrail(phase, with:, **) config.add_guardrail(phase, klass: with, options: ) end |
.guardrails_for(phase) ⇒ Object
Returns the registered guardrail configs for a given phase.
- phase
-
:before or :after.
– : (Symbol) -> Array[Hash[Symbol, untyped]]
228 229 230 |
# File 'lib/riffer/agent.rb', line 228 def self.guardrails_for(phase) config.guardrails_for(phase) end |
.identifier(value = nil) ⇒ Object
Gets or sets the agent identifier.
– : (?String?) -> String
42 43 44 |
# File 'lib/riffer/agent.rb', line 42 def self.identifier(value = nil) value.nil? ? (config.identifier || class_name_to_path(name)) : (config.identifier = value) end |
.instructions(value = 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)?
68 69 70 |
# File 'lib/riffer/agent.rb', line 68 def self.instructions(value = nil) value.nil? ? config.instructions : (config.instructions = value) end |
.max_steps(value = nil) ⇒ Object
Gets or sets the maximum number of LLM call steps in the tool-use loop.
Defaults to Riffer::Agent::Config::DEFAULT_MAX_STEPS (16). Set to Float::INFINITY for unlimited steps.
– : (?Numeric?) -> Numeric
110 111 112 |
# File 'lib/riffer/agent.rb', line 110 def self.max_steps(value = nil) value.nil? ? config.max_steps : (config.max_steps = value) end |
.mcp_configs ⇒ Object
Returns the accumulated use_mcp configurations for this agent class.
: () -> Array[Hash[Symbol, untyped]]
135 136 137 |
# File 'lib/riffer/agent.rb', line 135 def self.mcp_configs config.mcp_configs end |
.model(value = nil) ⇒ Object
Gets or sets the model string (e.g., “openai/gpt-4o”) or Proc.
– : (?(String | Proc)?) -> (String | Proc)?
50 51 52 |
# File 'lib/riffer/agent.rb', line 50 def self.model(value = nil) value.nil? ? config.model : (config.model = value) end |
.model_options(options = nil) ⇒ Object
Gets or sets model options passed to generate_text/stream_text.
– : (?Hash[Symbol, untyped]?) -> Hash[Symbol, untyped]
84 85 86 |
# File 'lib/riffer/agent.rb', line 84 def self.( = nil) .nil? ? config. : (config. = ) end |
.provider_options(options = nil) ⇒ Object
Gets or sets provider options passed to the provider client.
– : (?Hash[Symbol, untyped]?) -> Hash[Symbol, untyped]
76 77 78 |
# File 'lib/riffer/agent.rb', line 76 def self.( = nil) .nil? ? config. : (config. = ) 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
– : () ?{ (Riffer::Skills::Config) [self: Riffer::Skills::Config] -> void } -> Riffer::Skills::Config?
162 163 164 165 166 167 168 169 |
# File 'lib/riffer/agent.rb', line 162 def self.skills(&block) if block skills_config = Riffer::Skills::Config.new skills_config.instance_eval(&block) config.skills_config = skills_config end config.skills_config end |
.stream(prompt = nil, files: nil, context: nil) ⇒ Object
Streams a response using a new agent instance.
context: is threaded into new; prompt and files: are forwarded to #stream.
– : (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?context: Hash[Symbol, untyped]?) -> Enumerator[Riffer::StreamEvents::Base, void]
205 206 207 |
# File 'lib/riffer/agent.rb', line 205 def self.stream(prompt = nil, files: nil, context: nil) new(context: context).stream(prompt, files: files) 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?) ?{ (Riffer::Params) [self: Riffer::Params] -> void } -> Riffer::Params?
94 95 96 97 98 99 100 101 |
# File 'lib/riffer/agent.rb', line 94 def self.structured_output(params = nil, &block) if block params = Riffer::Params.new params.instance_eval(&block) end config.structured_output = params if params config.structured_output end |
.tool_runtime(value = nil) ⇒ Object
Gets or sets the tool runtime for this agent.
Accepts a Riffer::Tools::Runtime subclass, a Riffer::Tools::Runtime instance, or a Proc. Defaults to Riffer.config.tool_runtime when unset.
– : (?(singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)
146 147 148 |
# File 'lib/riffer/agent.rb', line 146 def self.tool_runtime(value = nil) value.nil? ? config.tool_runtime : (config.tool_runtime = value) end |
.use_mcp(tag) ⇒ Object
Opts this agent into tools from all MCP registrations that share any of the given tag(s).
tag - a String or Symbol; matched against registration manifest tags.
: (String | Symbol) -> void
128 129 130 |
# File 'lib/riffer/agent.rb', line 128 def self.use_mcp(tag) config.add_mcp(tag) end |
Instance Method Details
#generate(prompt = nil, files: nil) ⇒ Object
Generates a response from the agent.
Runs the inference loop via Riffer::Agent::Run.generate. When prompt is given, a new Riffer::Messages::User is appended to the session (silently — on_message does not fire for user inputs) and then the loop runs. When prompt is omitted, the loop runs against the current session — useful for resuming a persisted conversation whose last turn is already a user message, or for picking up pending tool calls after an interrupt.
files: requires prompt. Pass files to attach to the new user message.
– : (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?) -> Riffer::Agent::Response
341 342 343 |
# File 'lib/riffer/agent.rb', line 341 def generate(prompt = nil, files: nil) Riffer::Agent::Run.generate(agent: self, prompt: prompt, files: files) 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.
When Riffer.config.experimental_history_healing is enabled, riffer fills any orphaned tool_use on the way out with a placeholder Riffer::Messages::Tool carrying error_type: :interrupted. The filled call_ids are exposed on Riffer::Agent::Response#healed_tool_call_ids (and the streaming Riffer::StreamEvents::Interrupt event).
– : (?(String | Symbol)?) -> void
375 376 377 |
# File 'lib/riffer/agent.rb', line 375 def interrupt!(reason = nil) throw :riffer_interrupt, reason end |
#stream(prompt = nil, files: nil) ⇒ Object
Streams a response from the agent.
Runs the inference loop via Riffer::Agent::Run.stream, returning an Enumerator of Riffer::StreamEvents.
Raises Riffer::ArgumentError if structured output is configured.
See #generate for prompt/files semantics.
– : (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?) -> Enumerator[Riffer::StreamEvents::Base, void]
356 357 358 359 |
# File 'lib/riffer/agent.rb', line 356 def stream(prompt = nil, files: nil) raise Riffer::ArgumentError, "Structured output is not supported with streaming. Use #generate instead." if @structured_output Riffer::Agent::Run.stream(agent: self, prompt: prompt, files: files) end |