Class: Riffer::Providers::Mock

Inherits:
Base
  • Object
show all
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

Class Method Summary collapse

Instance Method Summary collapse

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

Parameters:

  • (Object)


39
40
41
42
43
44
# File 'lib/riffer/providers/mock.rb', line 39

def initialize(**options)
  @responses = (options[:responses] || []).map { |r| normalize_response(r) }
  @current_index = 0
  @calls = []
  @stubbed_responses = []
end

Instance Attribute Details

#callsArray[Hash[Symbol, untyped]] (readonly)

Array of recorded method calls for assertions.

Returns:

  • (Array[Hash[Symbol, untyped]])


27
28
29
# File 'lib/riffer/providers/mock.rb', line 27

def calls
  @calls
end

Class Method Details

.semconv_provider_nameString

The GenAI semconv well-known provider name.

: () -> String

Returns:

  • (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)

Parameters:

  • (String, nil)

Returns:



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]

Parameters:

Returns:

  • (Hash[Symbol, untyped])


92
93
94
95
96
97
98
# File 'lib/riffer/providers/mock.rb', line 92

def build_request_params(messages, model, options)
  web_search = options[:web_search]
  @calls << {messages: messages.map(&:to_h), model: model, **options.except(:web_search)}
  response = next_response
  response[:web_search] = web_search if web_search
  {response: response}
end

#clear_stubsvoid

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]

Parameters:

  • (Hash[Symbol, untyped])

Returns:

  • (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

Parameters:



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

Parameters:

  • (Object)

Returns:

  • (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?

Parameters:

  • (Object)

Returns:



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?

Parameters:

  • (Object)

Returns:



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

Parameters:

  • (Object)

Returns:



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_responseHash[Symbol, untyped]

-- : () -> Hash[Symbol, untyped]

Returns:

  • (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]

Parameters:

  • (Hash[Symbol, untyped])

Returns:

  • (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

Parameters:

  • (String)
  • tool_calls: (Array[Hash[Symbol, untyped]]) (defaults to: [])
  • token_usage: (Riffer::Providers::TokenUsage, nil) (defaults to: nil)
  • finish_reason: (Symbol, nil) (defaults to: nil)


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