Class: Riffer::Providers::Mock
- Defined in:
- lib/riffer/providers/mock.rb,
sig/generated/riffer/providers/mock.rbs
Overview
Mock provider for mocking LLM responses in tests; no external gems required.
Constant Summary
Constants inherited from Base
Base::REQUEST_PARAM_ATTRIBUTES, Base::WIRE_SEPARATOR
Instance Attribute Summary collapse
-
#calls ⇒ Array[Hash[Symbol, untyped]]
readonly
Array of recorded method calls for assertions.
Class Method Summary collapse
-
.semconv_provider_name ⇒ String
The GenAI semconv well-known provider name.
-
.skills_adapter(model = nil) ⇒ singleton(Riffer::Skills::Adapter)
Returns the skill adapter for the mock model — XML when the model name contains
claude(mirroring a real Claude provider), else Markdown.
Instance Method Summary collapse
-
#build_request_params(messages, model, options) ⇒ Hash[Symbol, untyped]
-- : (Array, String?, Hash[Symbol, untyped]) -> Hash[Symbol, untyped].
-
#clear_stubs ⇒ void
Clears all stubbed responses.
-
#execute_generate(params) ⇒ Hash[Symbol, untyped]
-- : (Hash[Symbol, untyped]) -> Hash[Symbol, untyped].
-
#execute_stream(params, yielder) ⇒ void
-- : (Hash[Symbol, untyped], Riffer::Providers::_EventSink) -> void.
-
#extract_content(response) ⇒ String
-- : (untyped) -> String.
-
#extract_finish_reason(response) ⇒ Riffer::Providers::FinishReason?
-- : (untyped) -> Riffer::Providers::FinishReason?.
-
#extract_token_usage(response) ⇒ Riffer::Providers::TokenUsage?
-- : (untyped) -> Riffer::Providers::TokenUsage?.
-
#extract_tool_calls(response) ⇒ Array[Riffer::Messages::Assistant::ToolCall]
-- : (untyped) -> Array.
-
#initialize(**options) ⇒ Mock
constructor
responses:pre-configures canned responses (same shape as#stub_response), typically set viaprovider_options responses: [...]. -
#next_response ⇒ Hash[Symbol, untyped]
-- : () -> Hash[Symbol, untyped].
-
#normalize_response(response) ⇒ Hash[Symbol, untyped]
-- : (Hash[Symbol, untyped]) -> Hash[Symbol, untyped].
-
#stub_response(content, tool_calls: [], token_usage: nil, finish_reason: nil) ⇒ void
Stubs the next response; call repeatedly to queue several.
Methods inherited from Base
#apply_pricing, #capture_input, #capture_messages?, #capture_output, #chat_span_attributes, #decode_tool_name, #depends_on, #encode_tool_name, #generate_text, #in_chat_span, #merge_consecutive_messages, #normalize_messages, #parse_structured_output, #parse_tool_arguments, #pricing_rates, #record_finish_reason, #record_stream_outcome, #stream_text, #tag_attributes, #validate_input!, #validate_normalized_messages!, #yield_finish_reason
Constructor Details
#initialize(**options) ⇒ Mock
responses: pre-configures canned responses (same shape as
#stub_response), typically set via provider_options responses: [...].
Riffer::Providers::Mock.new(responses: [
{content: "", tool_calls: [{name: "tool_a", arguments: "{}"}]},
{content: "Final answer"}
])
-- : (**untyped) -> void
40 41 42 43 44 45 46 |
# File 'lib/riffer/providers/mock.rb', line 40 def initialize(**) super() @responses = ([:responses] || []).map { |r| normalize_response(r) } @current_index = 0 @calls = [] @stubbed_responses = [] end |
Instance Attribute Details
#calls ⇒ Array[Hash[Symbol, untyped]] (readonly)
Array of recorded method calls for assertions.
28 29 30 |
# File 'lib/riffer/providers/mock.rb', line 28 def calls @calls end |
Class Method Details
.semconv_provider_name ⇒ String
The GenAI semconv well-known provider name.
: () -> String
23 24 25 |
# File 'lib/riffer/providers/mock.rb', line 23 def self.semconv_provider_name "mock" end |
.skills_adapter(model = nil) ⇒ singleton(Riffer::Skills::Adapter)
Returns the skill adapter for the mock model — XML when the model name
contains claude (mirroring a real Claude provider), else Markdown.
: (?String?) -> singleton(Riffer::Skills::Adapter)
14 15 16 17 18 |
# File 'lib/riffer/providers/mock.rb', line 14 def self.skills_adapter(model = nil) return Riffer::Skills::XmlAdapter if model&.include?("claude") Riffer::Skills::MarkdownAdapter end |
Instance Method Details
#build_request_params(messages, model, options) ⇒ Hash[Symbol, untyped]
-- : (Array, String?, Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
101 102 103 104 105 106 107 |
# File 'lib/riffer/providers/mock.rb', line 101 def build_request_params(, model, ) web_search = [:web_search] @calls << { messages: .map(&:to_h), model: model, **.except(:web_search) } response = next_response response[:web_search] = web_search if web_search { response: response } end |
#clear_stubs ⇒ void
This method returns an undefined value.
Clears all stubbed responses.
-- : () -> void
72 73 74 |
# File 'lib/riffer/providers/mock.rb', line 72 def clear_stubs @stubbed_responses = [] end |
#execute_generate(params) ⇒ Hash[Symbol, untyped]
-- : (Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
111 112 113 |
# File 'lib/riffer/providers/mock.rb', line 111 def execute_generate(params) params[:response] end |
#execute_stream(params, yielder) ⇒ void
This method returns an undefined value.
-- : (Hash[Symbol, untyped], Riffer::Providers::_EventSink) -> void
145 146 147 148 149 150 151 152 153 154 155 156 157 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 |
# File 'lib/riffer/providers/mock.rb', line 145 def execute_stream(params, yielder) response = params[:response] full_content = response[:content] || "" tool_calls = response[:tool_calls] || [] token_usage = response[:token_usage] web_search = response[:web_search] if web_search yielder << Riffer::StreamEvents::WebSearchStatus.new("in_progress") yielder << Riffer::StreamEvents::WebSearchStatus.new("searching", query: "mock search query") yielder << Riffer::StreamEvents::WebSearchStatus.new("open_page", url: "https://example.com") yielder << Riffer::StreamEvents::WebSearchStatus.new("completed") yielder << Riffer::StreamEvents::WebSearchDone.new( "mock search query", sources: [{ title: "Example", url: "https://example.com" }], ) end unless full_content.empty? content_parts = full_content.split(". ").map { |part| part + (part.end_with?(".") ? "" : ".") } content_parts.each do |part| yielder << Riffer::StreamEvents::TextDelta.new("#{part} ") end end tool_calls.each do |tc| yielder << Riffer::StreamEvents::ToolCallDelta.new( item_id: tc.call_id, name: tc.name, arguments_delta: tc.arguments, ) yielder << Riffer::StreamEvents::ToolCallDone.new( item_id: tc.call_id, call_id: tc.call_id, name: tc.name, arguments: tc.arguments, ) end yielder << Riffer::StreamEvents::TextDone.new(full_content) yield_finish_reason(yielder, extract_finish_reason(response)) yielder << Riffer::StreamEvents::TokenUsageDone.new(token_usage: apply_pricing(token_usage)) if token_usage end |
#extract_content(response) ⇒ String
-- : (untyped) -> String
133 134 135 |
# File 'lib/riffer/providers/mock.rb', line 133 def extract_content(response) response.is_a?(Hash) ? (response[:content] || "") : response.content end |
#extract_finish_reason(response) ⇒ Riffer::Providers::FinishReason?
-- : (untyped) -> Riffer::Providers::FinishReason?
124 125 126 127 128 129 |
# File 'lib/riffer/providers/mock.rb', line 124 def extract_finish_reason(response) return nil unless response.is_a?(Hash) reason = response[:finish_reason] reason ? Riffer::Providers::FinishReason.new(reason: reason) : nil end |
#extract_token_usage(response) ⇒ Riffer::Providers::TokenUsage?
-- : (untyped) -> Riffer::Providers::TokenUsage?
117 118 119 120 |
# File 'lib/riffer/providers/mock.rb', line 117 def extract_token_usage(response) usage = response[:token_usage] usage && apply_pricing(usage) end |
#extract_tool_calls(response) ⇒ Array[Riffer::Messages::Assistant::ToolCall]
-- : (untyped) -> Array
139 140 141 |
# File 'lib/riffer/providers/mock.rb', line 139 def extract_tool_calls(response) response.is_a?(Hash) ? (response[:tool_calls] || []) : response.tool_calls end |
#next_response ⇒ Hash[Symbol, untyped]
-- : () -> Hash[Symbol, untyped]
191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/riffer/providers/mock.rb', line 191 def next_response if @stubbed_responses.any? @stubbed_responses.shift elsif @current_index < @responses.size response = @responses[@current_index] @current_index += 1 response else normalize_response(content: "Mock response") end end |
#normalize_response(response) ⇒ Hash[Symbol, untyped]
-- : (Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/riffer/providers/mock.rb', line 80 def normalize_response(response) formatted_tool_calls = (response[:tool_calls] || []).map.with_index do |tc, idx| next tc if tc.is_a?(Riffer::Messages::Assistant::ToolCall) Riffer::Messages::Assistant::ToolCall.new( call_id: tc[:call_id] || tc[:id] || "mock_call_#{idx}", name: tc[:name], arguments: tc[:arguments].is_a?(String) ? tc[:arguments] : tc[:arguments].to_json, ) end { role: response[:role] || "assistant", content: response[:content] || "", tool_calls: formatted_tool_calls, token_usage: response[:token_usage], finish_reason: response[:finish_reason] || (formatted_tool_calls.empty? ? :stop : :tool_calls), } end |
#stub_response(content, tool_calls: [], token_usage: nil, finish_reason: nil) ⇒ void
This method returns an undefined value.
Stubs the next response; call repeatedly to queue several. finish_reason
defaults to :tool_calls when tool calls are present, else :stop.
provider.stub_response("Hello")
provider.stub_response("", tool_calls: [{name: "my_tool", arguments: '{"key":"value"}'}])
provider.stub_response("Final response",
token_usage: Riffer::Providers::TokenUsage.new(input_tokens: 10, output_tokens: 5))
provider.stub_response("Truncated...", finish_reason: :length)
-- : (String, ?tool_calls: Array[Hash[Symbol, untyped]], ?token_usage: Riffer::Providers::TokenUsage?, ?finish_reason: Symbol?) -> void
59 60 61 62 63 64 65 66 |
# File 'lib/riffer/providers/mock.rb', line 59 def stub_response(content, tool_calls: [], token_usage: nil, finish_reason: nil) @stubbed_responses << normalize_response( content: content, tool_calls: tool_calls, token_usage: token_usage, finish_reason: finish_reason, ) end |