Providers · 08
Mock Provider
The Mock provider is a mock provider for testing agents without making real API calls.
Usage
No additional gems required. Use the mock provider identifier:
class TestableAgent < Riffer::Agent
model 'mock/any' # The model name doesn't matter for mock provider
instructions 'You are helpful.'
uses_tools [MyTool]
end
Stubbing Responses
Use stub_response to queue responses:
# Get the provider instance from the agent
agent = TestableAgent.new
provider = agent.provider
# Stub a simple text response
provider.stub_response("Hello, I'm here to help!")
# Now generate will return the stubbed response
response = agent.generate("Hi")
response.content
# => "Hello, I'm here to help!"
Stubbing Tool Calls
Stub responses that trigger tool execution:
provider.stub_response("", tool_calls: [
{name: "my_tool", arguments: '{"query":"test"}'}
])
# Queue the response after tool execution
provider.stub_response("Based on the tool result, here's my answer.")
response = agent.generate("Use the tool")
Stubbing Reasoning
Stub reasoning parts to exercise your application’s persistence of them. Hashes are normalized into Riffer::Messages::Assistant::ReasoningParts:
provider.stub_response("42", reasoning: [
{type: :text, text: "The user wants the answer.", format: "mock-v1"},
{type: :encrypted, data: "b3BhcXVl", signature: "sig", format: "mock-v1"}
])
response = agent.generate("What is the answer?")
response.reasoning.map(&:type) # => [:text, :encrypted]
agent.session.messages.last.reasoning_text # => "The user wants the answer."
When streaming, each part is emitted as a ReasoningDelta (only when it carries text) followed by a ReasoningDone carrying the part, ahead of the text events.
Queueing Multiple Responses
Responses are consumed in order:
provider.stub_response("First response")
provider.stub_response("Second response")
provider.stub_response("Third response")
agent.generate("Message 1").content # => "First response"
agent.generate("Message 2").content # => "Second response"
agent.generate("Message 3").content # => "Third response"
agent.generate("Message 4").content # => "Mock response" (default)
Inspecting Calls
Access recorded calls for assertions:
provider.calls
# => [
# {messages: [...], model: "any", tools: [...], ...},
# {messages: [...], model: "any", tools: [...], ...}
# ]
# Check what was sent
expect(provider.calls.last[:messages].last[:content]).to eq("Hi")
Clearing State
Reset stubbed responses:
provider.clear_stubs
Example Test
require 'minitest/autorun'
class MyAgentTest < Minitest::Test
def setup
@agent = TestableAgent.new
@provider = @agent.provider
end
def test_generates_response
@provider.stub_response("Hello!")
response = @agent.generate("Hi")
assert_equal "Hello!", response.content
end
def test_executes_tool
@provider.stub_response("", tool_calls: [
{name: "weather_tool", arguments: '{"city":"Tokyo"}'}
])
@provider.stub_response("The weather is sunny.")
response = @agent.generate("What's the weather?")
assert_equal "The weather is sunny.", response.content
assert_equal 2, @provider.calls.length
end
def test_passes_context_to_tools
@provider.stub_response("", tool_calls: [
{name: "user_tool", arguments: '{}'}
])
@provider.stub_response("Done.")
agent = TestableAgent.new(context: {user_id: 123})
agent.generate("Do something")
# Tool receives the context
end
end
Streaming
The mock provider also supports streaming:
provider.stub_response("Hello world.")
events = []
agent.stream("Hi").each { |e| events << e }
# Events include TextDelta and TextDone
text_deltas = events.select { |e| e.is_a?(Riffer::StreamEvents::TextDelta) }
text_done = events.find { |e| e.is_a?(Riffer::StreamEvents::TextDone) }
Web Search
The mock provider emits web search events when web_search: true is set in the agent’s model_options:
class SearchingAgent < Riffer::Agent
model 'mock/any'
model_options web_search: true
end
agent = SearchingAgent.new
agent.provider.stub_response("Here are the latest results.")
events = []
agent.stream("What's new in Ruby?").each { |e| events << e }
# Events include WebSearchStatus and WebSearchDone before text events
search_statuses = events.select { |e| e.is_a?(Riffer::StreamEvents::WebSearchStatus) }
search_done = events.find { |e| e.is_a?(Riffer::StreamEvents::WebSearchDone) }
search_done.query # => "mock search query"
search_done.sources # => [{title: "Example", url: "https://example.com"}]
Initial Responses
Pass responses during initialization:
provider = Riffer::Providers::Mock.new(responses: [
{content: "First"},
{content: "Second"}
])
Default Response
When no stubs are queued and initial responses are exhausted, the provider returns:
{role: "assistant", content: "Mock response"}