Class: Kernai::Mock::Provider

Inherits:
Provider
  • Object
show all
Defined in:
lib/kernai/mock/provider.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Provider

#encode, #encode_part, #fallback_for

Constructor Details

#initializeProvider

Returns a new instance of Provider.



8
9
10
11
12
13
14
15
16
# File 'lib/kernai/mock/provider.rb', line 8

def initialize
  super
  @responses = []
  @call_count = 0
  @calls = []
  @on_call = nil
  @token_provider = nil
  @failures = {}
end

Instance Attribute Details

#callsObject (readonly)

Returns the value of attribute calls.



6
7
8
# File 'lib/kernai/mock/provider.rb', line 6

def calls
  @calls
end

Instance Method Details

#call(messages:, model:, generation: nil, &block) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/kernai/mock/provider.rb', line 65

def call(messages:, model:, generation: nil, &block)
  @calls << { messages: messages, model: model, generation: generation }
  maybe_raise_scheduled_failure

  started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  content = resolve_response(messages, model)
  @call_count += 1

  content.to_s.each_char { |c| block.call(c) } if block

  latency_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - started) * 1000).round
  tokens = @token_provider&.call(messages, content) || {}

  LlmResponse.new(
    content: content,
    latency_ms: latency_ms,
    prompt_tokens: tokens[:prompt_tokens],
    completion_tokens: tokens[:completion_tokens]
  )
end

#call_countObject



86
87
88
# File 'lib/kernai/mock/provider.rb', line 86

def call_count
  @calls.size
end

#fail_on_step(step, error_class: Kernai::ProviderError, message: 'mock provider failure') ⇒ Object

Schedule an exception at a specific call index (0-based). Lets scenarios exercise provider-error branches deterministically, without reaching for stub libraries. The raised class defaults to Kernai::ProviderError — the same error the base provider contract raises for wiring problems.



60
61
62
63
# File 'lib/kernai/mock/provider.rb', line 60

def fail_on_step(step, error_class: Kernai::ProviderError, message: 'mock provider failure')
  @failures[step] = { class: error_class, message: message }
  self
end

#generation_at(index) ⇒ Object

GenerationOptions the provider received at call index ‘i`.



102
103
104
# File 'lib/kernai/mock/provider.rb', line 102

def generation_at(index)
  @calls.dig(index, :generation)
end

#last_callObject



90
91
92
# File 'lib/kernai/mock/provider.rb', line 90

def last_call
  @calls.last
end

#messages_at(index) ⇒ Object

Messages the provider received at call index ‘i` (0-based).



97
98
99
# File 'lib/kernai/mock/provider.rb', line 97

def messages_at(index)
  @calls.dig(index, :messages)
end

#on_call(&block) ⇒ Object

Set a dynamic response handler.



42
43
44
45
# File 'lib/kernai/mock/provider.rb', line 42

def on_call(&block)
  @on_call = block
  self
end

#reset!Object



106
107
108
109
110
111
112
113
# File 'lib/kernai/mock/provider.rb', line 106

def reset!
  @responses = []
  @call_count = 0
  @calls = []
  @on_call = nil
  @token_provider = nil
  @failures = {}
end

#respond_with(*texts) ⇒ Object

Queue a response (consumed in order, last one repeats).



19
20
21
22
# File 'lib/kernai/mock/provider.rb', line 19

def respond_with(*texts)
  @responses.concat(texts)
  self
end

#respond_with_script(script) ⇒ Object

Same behavior as ‘respond_with` but indexed by step number for scenarios where associating a response with its step is clearer than positional ordering. Keys must be a contiguous run of non-negative integers starting at 0; missing steps would break the “last response repeats” contract.



29
30
31
32
33
34
35
36
37
38
39
# File 'lib/kernai/mock/provider.rb', line 29

def respond_with_script(script)
  sorted_keys = script.keys.sort
  unless sorted_keys == (0..sorted_keys.max.to_i).to_a
    raise ArgumentError,
          'respond_with_script keys must be contiguous non-negative integers starting at 0, ' \
          "got #{sorted_keys.inspect}"
  end

  @responses = sorted_keys.map { |k| script[k] }
  self
end

#with_token_counter(&block) ⇒ Object

Optional: deterministic token counter used by tests that assert on usage data. Receives (messages, content) and must return a hash like { prompt_tokens:, completion_tokens: }.



50
51
52
53
# File 'lib/kernai/mock/provider.rb', line 50

def with_token_counter(&block)
  @token_provider = block
  self
end