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
39 40 41 42 43 44 |
# File 'lib/riffer/providers/mock.rb', line 39 def initialize(**) @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.
27 28 29 |
# File 'lib/riffer/providers/mock.rb', line 27 def calls @calls end |
Class Method Details
.semconv_provider_name ⇒ String
The GenAI semconv well-known provider name.
: () -> String
22 23 24 |
# File 'lib/riffer/providers/mock.rb', line 22 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 |
# 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]
92 93 94 95 96 97 98 |
# File 'lib/riffer/providers/mock.rb', line 92 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
64 65 66 |
# File 'lib/riffer/providers/mock.rb', line 64 def clear_stubs @stubbed_responses = [] end |
#execute_generate(params) ⇒ Hash[Symbol, untyped]
-- : (Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
102 103 104 |
# File 'lib/riffer/providers/mock.rb', line 102 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
136 137 138 139 140 141 142 143 144 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 |
# File 'lib/riffer/providers/mock.rb', line 136 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
124 125 126 |
# File 'lib/riffer/providers/mock.rb', line 124 def extract_content(response) response.is_a?(Hash) ? (response[:content] || "") : response.content end |
#extract_finish_reason(response) ⇒ Riffer::Providers::FinishReason?
-- : (untyped) -> Riffer::Providers::FinishReason?
115 116 117 118 119 120 |
# File 'lib/riffer/providers/mock.rb', line 115 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?
108 109 110 111 |
# File 'lib/riffer/providers/mock.rb', line 108 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
130 131 132 |
# File 'lib/riffer/providers/mock.rb', line 130 def extract_tool_calls(response) response.is_a?(Hash) ? (response[:tool_calls] || []) : response.tool_calls end |
#next_response ⇒ Hash[Symbol, untyped]
-- : () -> Hash[Symbol, untyped]
179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/riffer/providers/mock.rb', line 179 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]
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/riffer/providers/mock.rb', line 72 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
56 57 58 |
# File 'lib/riffer/providers/mock.rb', line 56 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 |