riffer 0.40.0 → 0.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.agents/providers.md +10 -1
  3. data/.agents/rbs-inline.md +2 -2
  4. data/.release-please-manifest.json +1 -1
  5. data/CHANGELOG.md +11 -0
  6. data/README.md +20 -24
  7. data/docs/AGENTS.md +0 -11
  8. data/docs/CONFIGURATION.md +105 -21
  9. data/docs/SERIALIZATION.md +4 -3
  10. data/docs/providers/AMAZON_BEDROCK.md +14 -8
  11. data/docs/providers/ANTHROPIC.md +9 -7
  12. data/docs/providers/AZURE_OPENAI.md +12 -12
  13. data/docs/providers/CUSTOM_PROVIDERS.md +32 -16
  14. data/docs/providers/GEMINI.md +28 -5
  15. data/docs/providers/OPENAI.md +21 -5
  16. data/docs/providers/OPENROUTER.md +11 -6
  17. data/docs/providers/PROVIDERS.md +16 -1
  18. data/lib/riffer/agent/config.rb +0 -6
  19. data/lib/riffer/agent/serializer.rb +0 -2
  20. data/lib/riffer/agent.rb +1 -9
  21. data/lib/riffer/config.rb +6 -6
  22. data/lib/riffer/evals/judge.rb +3 -5
  23. data/lib/riffer/providers/amazon_bedrock.rb +31 -19
  24. data/lib/riffer/providers/anthropic.rb +19 -8
  25. data/lib/riffer/providers/azure_open_ai.rb +19 -11
  26. data/lib/riffer/providers/base.rb +28 -0
  27. data/lib/riffer/providers/gemini/client.rb +120 -0
  28. data/lib/riffer/providers/gemini.rb +13 -62
  29. data/lib/riffer/providers/mock.rb +5 -4
  30. data/lib/riffer/providers/open_ai.rb +23 -8
  31. data/lib/riffer/providers/open_router.rb +23 -9
  32. data/lib/riffer/version.rb +1 -1
  33. data/sig/_private/riffer/providers/amazon_bedrock.rbs +4 -2
  34. data/sig/_private/riffer/providers/anthropic.rbs +4 -2
  35. data/sig/_private/riffer/providers/gemini.rbs +7 -0
  36. data/sig/_private/riffer/providers/open_ai.rbs +4 -2
  37. data/sig/_private/riffer/providers/open_router.rbs +4 -2
  38. data/sig/generated/riffer/agent/config.rbs +1 -5
  39. data/sig/generated/riffer/agent.rbs +0 -6
  40. data/sig/generated/riffer/config.rbs +25 -15
  41. data/sig/generated/riffer/evals/judge.rbs +2 -4
  42. data/sig/generated/riffer/providers/amazon_bedrock.rbs +13 -2
  43. data/sig/generated/riffer/providers/anthropic.rbs +13 -2
  44. data/sig/generated/riffer/providers/azure_open_ai.rbs +14 -4
  45. data/sig/generated/riffer/providers/base.rbs +20 -0
  46. data/sig/generated/riffer/providers/gemini/client.rbs +65 -0
  47. data/sig/generated/riffer/providers/gemini.rbs +7 -23
  48. data/sig/generated/riffer/providers/mock.rbs +4 -3
  49. data/sig/generated/riffer/providers/open_ai.rbs +13 -2
  50. data/sig/generated/riffer/providers/open_router.rbs +16 -3
  51. metadata +4 -1
@@ -2,20 +2,11 @@
2
2
  # rbs_inline: enabled
3
3
 
4
4
  require "json"
5
- require "net/http"
6
5
  require "securerandom"
7
- require "uri"
8
6
 
9
7
  # Google Gemini provider for Gemini models via the Gemini REST API.
10
8
  class Riffer::Providers::Gemini < Riffer::Providers::Base
11
- # @rbs @api_key: String?
12
- # @rbs @open_timeout: Integer
13
- # @rbs @read_timeout: Integer
14
-
15
- BASE_URI = URI("https://generativelanguage.googleapis.com") #: URI::Generic
16
9
  VALID_MODEL_PATTERN = /\A[a-zA-Z0-9._-]+\z/ #: Regexp
17
- DEFAULT_OPEN_TIMEOUT = 10 #: Integer
18
- DEFAULT_READ_TIMEOUT = 60 #: Integer
19
10
 
20
11
  FINISH_REASONS = {
21
12
  "STOP" => :stop,
@@ -36,17 +27,19 @@ class Riffer::Providers::Gemini < Riffer::Providers::Base
36
27
  "gcp.gemini"
37
28
  end
38
29
 
30
+ private
31
+
39
32
  #--
40
- #: (?api_key: String?, ?open_timeout: Integer?, ?read_timeout: Integer?, **untyped) -> void
41
- def initialize(api_key: nil, open_timeout: nil, read_timeout: nil, **_options)
42
- super()
43
- api_key ||= Riffer.config.gemini.api_key
44
- @api_key = api_key
45
- @open_timeout = open_timeout || Riffer.config.gemini.open_timeout || DEFAULT_OPEN_TIMEOUT
46
- @read_timeout = read_timeout || Riffer.config.gemini.read_timeout || DEFAULT_READ_TIMEOUT
33
+ #: () -> untyped
34
+ def global_client
35
+ Riffer.config.gemini.client
47
36
  end
48
37
 
49
- private
38
+ #--
39
+ #: () -> untyped
40
+ def build_client
41
+ Riffer::Providers::Gemini::Client.new(**{ api_key: Riffer.config.gemini.api_key }.compact)
42
+ end
50
43
 
51
44
  #--
52
45
  #: (Array[Riffer::Messages::Base], String?, Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
@@ -89,9 +82,7 @@ class Riffer::Providers::Gemini < Riffer::Providers::Base
89
82
  def execute_generate(params)
90
83
  model = params[:model]
91
84
  body = params.except(:model)
92
- response = post_request(api_path(model, "generateContent"), body)
93
- handle_api_error!(response) unless response.is_a?(Net::HTTPSuccess)
94
- JSON.parse(response.body, symbolize_names: true)
85
+ client.post(api_path(model, "generateContent"), body)
95
86
  end
96
87
 
97
88
  #--
@@ -171,13 +162,6 @@ class Riffer::Providers::Gemini < Riffer::Providers::Base
171
162
  model = params[:model]
172
163
  body = params.except(:model)
173
164
 
174
- uri = URI("#{BASE_URI}/#{api_path(model, 'streamGenerateContent')}?alt=sse")
175
- host = uri.hostname #: String
176
- request = Net::HTTP::Post.new(uri)
177
- request["Content-Type"] = "application/json"
178
- request["x-goog-api-key"] = @api_key
179
- request.body = body.to_json
180
-
181
165
  full_text = +""
182
166
  buffer = +""
183
167
  raw_finish_reason = nil #: String?
@@ -224,17 +208,8 @@ class Riffer::Providers::Gemini < Riffer::Providers::Base
224
208
  end
225
209
  end
226
210
 
227
- Net::HTTP.start(host, uri.port, use_ssl: true, open_timeout: @open_timeout, read_timeout: @read_timeout) do |http|
228
- http.request(request) do |response|
229
- handle_api_error!(response) unless response.is_a?(Net::HTTPSuccess)
230
-
231
- begin
232
- response.read_body { |chunk| process_chunk.call(chunk) }
233
- rescue IOError
234
- process_chunk.call(response.body)
235
- end
236
- end
237
- end
211
+ path = "#{api_path(model, 'streamGenerateContent')}?alt=sse"
212
+ client.post_stream(path, body) { |chunk| process_chunk.call(chunk) }
238
213
 
239
214
  yielder << Riffer::StreamEvents::TextDone.new(full_text) unless full_text.empty?
240
215
  yield_finish_reason(yielder, build_finish_reason(raw_finish_reason, tool_calls: saw_function_call))
@@ -325,18 +300,6 @@ class Riffer::Providers::Gemini < Riffer::Providers::Base
325
300
  args.is_a?(String) ? args : args.to_json
326
301
  end
327
302
 
328
- #--
329
- #: (String, Hash[Symbol, untyped]) -> Net::HTTPResponse
330
- def post_request(path, body)
331
- uri = URI("#{BASE_URI}/#{path}")
332
- host = uri.hostname #: String
333
- request = Net::HTTP::Post.new(uri)
334
- request["Content-Type"] = "application/json"
335
- request["x-goog-api-key"] = @api_key
336
- request.body = body.to_json
337
- Net::HTTP.start(host, uri.port, use_ssl: true, open_timeout: @open_timeout, read_timeout: @read_timeout) { |http| http.request(request) }
338
- end
339
-
340
303
  #--
341
304
  #: (String, String) -> String
342
305
  def api_path(model, method)
@@ -370,16 +333,4 @@ class Riffer::Providers::Gemini < Riffer::Providers::Base
370
333
 
371
334
  schema
372
335
  end
373
-
374
- #--
375
- #: (Net::HTTPResponse) -> void
376
- def handle_api_error!(response)
377
- body = begin
378
- JSON.parse(response.body, symbolize_names: true)
379
- rescue JSON::ParserError
380
- { message: response.body }
381
- end
382
- error_message = body.dig(:error, :message) || body[:message] || response.body
383
- raise Riffer::Error, "Gemini API error (#{response.code}): #{error_message}"
384
- end
385
336
  end
@@ -28,7 +28,8 @@ class Riffer::Providers::Mock < Riffer::Providers::Base
28
28
  attr_reader :calls #: Array[Hash[Symbol, untyped]]
29
29
 
30
30
  # +responses:+ pre-configures canned responses (same shape as
31
- # +#stub_response+), typically set via +provider_options responses: [...]+.
31
+ # +#stub_response+) for standalone use; agent tests queue responses on
32
+ # <tt>agent.provider</tt> via +#stub_response+ instead.
32
33
  #
33
34
  # Riffer::Providers::Mock.new(responses: [
34
35
  # {content: "", tool_calls: [{name: "tool_a", arguments: "{}"}]},
@@ -36,10 +37,10 @@ class Riffer::Providers::Mock < Riffer::Providers::Base
36
37
  # ])
37
38
  #
38
39
  #--
39
- #: (**untyped) -> void
40
- def initialize(**options)
40
+ #: (?responses: Array[Hash[Symbol, untyped]]) -> void
41
+ def initialize(responses: [])
41
42
  super()
42
- @responses = (options[:responses] || []).map { |r| normalize_response(r) }
43
+ @responses = responses.map { |r| normalize_response(r) }
43
44
  @current_index = 0
44
45
  @calls = []
45
46
  @stubbed_responses = []
@@ -13,17 +13,32 @@ class Riffer::Providers::OpenAI < Riffer::Providers::Base
13
13
  end
14
14
 
15
15
  #--
16
- #: (**untyped) -> void
17
- def initialize(**options)
18
- super()
16
+ #: () -> void
17
+ def initialize
18
+ super
19
19
  depends_on "openai"
20
-
21
- api_key = options.fetch(:api_key, Riffer.config.openai.api_key)
22
- @client = ::OpenAI::Client.new(api_key: api_key, **options.except(:api_key))
23
20
  end
24
21
 
25
22
  private
26
23
 
24
+ #--
25
+ #: () -> untyped
26
+ def global_client
27
+ Riffer.config.openai.client
28
+ end
29
+
30
+ # Compacted so an unset value stays absent: the SDK reads +OPENAI_API_KEY+ /
31
+ # +OPENAI_BASE_URL+ only for a missing argument, and an explicit nil would
32
+ # suppress that fallback.
33
+ #--
34
+ #: () -> untyped
35
+ def build_client
36
+ ::OpenAI::Client.new(**{
37
+ api_key: Riffer.config.openai.api_key,
38
+ base_url: Riffer.config.openai.base_url,
39
+ }.compact)
40
+ end
41
+
27
42
  #--
28
43
  #: (Array[Riffer::Messages::Base], String?, Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
29
44
  def build_request_params(messages, model, options)
@@ -81,7 +96,7 @@ class Riffer::Providers::OpenAI < Riffer::Providers::Base
81
96
  #--
82
97
  #: (Hash[Symbol, untyped]) -> untyped
83
98
  def execute_generate(params)
84
- @client.responses.create(params)
99
+ client.responses.create(params)
85
100
  end
86
101
 
87
102
  #--
@@ -190,7 +205,7 @@ class Riffer::Providers::OpenAI < Riffer::Providers::Base
190
205
  tool_info: {},
191
206
  } #: Hash[Symbol, untyped]
192
207
 
193
- stream = @client.responses.stream(params)
208
+ stream = client.responses.stream(params)
194
209
  begin
195
210
  stream.each do |event|
196
211
  case event.type
@@ -5,7 +5,7 @@ require "json"
5
5
 
6
6
  # OpenRouter provider (https://openrouter.ai). Requires the +openai+ gem —
7
7
  # OpenRouter exposes an OpenAI-compatible endpoint, so this reuses the OpenAI
8
- # SDK with a +base_url+ override. +api_key+ falls back to config, then
8
+ # SDK with a +base_url+ override. +api_key+ resolves from config, then
9
9
  # +OPENROUTER_API_KEY+.
10
10
  class Riffer::Providers::OpenRouter < Riffer::Providers::Base
11
11
  BASE_URL = "https://openrouter.ai/api/v1" #: String
@@ -27,17 +27,31 @@ class Riffer::Providers::OpenRouter < Riffer::Providers::Base
27
27
  end
28
28
 
29
29
  #--
30
- #: (?api_key: String?, **untyped) -> void
31
- def initialize(api_key: nil, **)
32
- super()
30
+ #: () -> void
31
+ def initialize
32
+ super
33
33
  depends_on "openai"
34
+ end
34
35
 
35
- api_key ||= Riffer.config.openrouter.api_key || ENV.fetch("OPENROUTER_API_KEY", nil)
36
+ private
36
37
 
37
- @client = ::OpenAI::Client.new(api_key: api_key, base_url: BASE_URL, **)
38
+ #--
39
+ #: () -> untyped
40
+ def global_client
41
+ Riffer.config.openrouter.client
38
42
  end
39
43
 
40
- private
44
+ # Deliberately not compacted: this borrows the OpenAI SDK to talk to a
45
+ # different vendor, so omitting an unset +api_key+ would let the SDK fall
46
+ # back to +OPENAI_API_KEY+ and send an OpenAI credential to OpenRouter.
47
+ # Passing nil raises in the SDK instead. +OPENROUTER_API_KEY+ is read here
48
+ # rather than left to the SDK for the same reason.
49
+ #--
50
+ #: () -> untyped
51
+ def build_client
52
+ api_key = Riffer.config.openrouter.api_key || ENV.fetch("OPENROUTER_API_KEY", nil)
53
+ ::OpenAI::Client.new(api_key: api_key, base_url: BASE_URL)
54
+ end
41
55
 
42
56
  #--
43
57
  #: (Array[Riffer::Messages::Base], String?, Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
@@ -84,7 +98,7 @@ class Riffer::Providers::OpenRouter < Riffer::Providers::Base
84
98
  #--
85
99
  #: (Hash[Symbol, untyped]) -> untyped
86
100
  def execute_generate(params)
87
- @client.chat.completions.create(**params)
101
+ client.chat.completions.create(**params)
88
102
  end
89
103
 
90
104
  #--
@@ -174,7 +188,7 @@ class Riffer::Providers::OpenRouter < Riffer::Providers::Base
174
188
  # events. We want raw ChatCompletionChunk objects with
175
189
  # +choices.first.delta+ so we can map deltas to Riffer::StreamEvents
176
190
  # ourselves.
177
- stream = @client.chat.completions.stream_raw(**stream_params)
191
+ stream = client.chat.completions.stream_raw(**stream_params)
178
192
  begin
179
193
  stream.each do |chunk|
180
194
  handle_stream_chunk(chunk, state: state, yielder: yielder)
@@ -2,5 +2,5 @@
2
2
  # rbs_inline: enabled
3
3
 
4
4
  module Riffer
5
- VERSION = "0.40.0" #: String
5
+ VERSION = "0.41.0" #: String
6
6
  end
@@ -1,4 +1,6 @@
1
- # `@client` is typed against the SDK, so this ivar stub stays in _private (never ships)
1
+ # `client` is typed against the SDK, so this narrowing stays in _private (never ships)
2
2
  class Riffer::Providers::AmazonBedrock
3
- @client: ::Aws::BedrockRuntime::Client
3
+ private
4
+
5
+ def client: () -> ::Aws::BedrockRuntime::Client
4
6
  end
@@ -1,4 +1,6 @@
1
- # `@client` is typed against the SDK, so this ivar stub stays in _private (never ships)
1
+ # `client` is typed against the SDK, so this narrowing stays in _private (never ships)
2
2
  class Riffer::Providers::Anthropic
3
- @client: ::Anthropic::Client
3
+ private
4
+
5
+ def client: () -> ::Anthropic::Client
4
6
  end
@@ -0,0 +1,7 @@
1
+ # Narrows the private `client` to riffer's Gemini transport, mirroring the
2
+ # SDK-typed narrowings alongside it.
3
+ class Riffer::Providers::Gemini
4
+ private
5
+
6
+ def client: () -> Riffer::Providers::Gemini::Client
7
+ end
@@ -1,4 +1,6 @@
1
- # `@client` is typed against the SDK, so this ivar stub stays in _private (never ships)
1
+ # `client` is typed against the SDK, so this narrowing stays in _private (never ships)
2
2
  class Riffer::Providers::OpenAI
3
- @client: ::OpenAI::Client
3
+ private
4
+
5
+ def client: () -> ::OpenAI::Client
4
6
  end
@@ -1,4 +1,6 @@
1
- # `@client` is typed against the SDK, so this ivar stub stays in _private (never ships)
1
+ # `client` is typed against the SDK, so this narrowing stays in _private (never ships)
2
2
  class Riffer::Providers::OpenRouter
3
- @client: ::OpenAI::Client
3
+ private
4
+
5
+ def client: () -> ::OpenAI::Client
4
6
  end
@@ -15,9 +15,6 @@ class Riffer::Agent::Config
15
15
  # The configured instructions.
16
16
  attr_reader instructions: (String | Proc)?
17
17
 
18
- # Options passed to the provider client.
19
- attr_accessor provider_options: Hash[Symbol, untyped]
20
-
21
18
  # Options passed to generate_text/stream_text.
22
19
  attr_accessor model_options: Hash[Symbol, untyped]
23
20
 
@@ -49,7 +46,6 @@ class Riffer::Agent::Config
49
46
  # ?identifier: String?,
50
47
  # ?model: (String | Proc)?,
51
48
  # ?instructions: (String | Proc)?,
52
- # ?provider_options: Hash[Symbol, untyped],
53
49
  # ?model_options: Hash[Symbol, untyped],
54
50
  # ?structured_output: Riffer::Params?,
55
51
  # ?max_steps: Numeric?,
@@ -59,7 +55,7 @@ class Riffer::Agent::Config
59
55
  # ?skills_config: Riffer::Skills::Config?,
60
56
  # ?guardrails: Hash[Symbol, Array[Hash[Symbol, untyped]]]
61
57
  # ) -> void
62
- def initialize: (?identifier: String?, ?model: (String | Proc)?, ?instructions: (String | Proc)?, ?provider_options: Hash[Symbol, untyped], ?model_options: Hash[Symbol, untyped], ?structured_output: Riffer::Params?, ?max_steps: Numeric?, ?tools_config: (Array[singleton(Riffer::Tool)] | Proc)?, ?mcp_configs: Array[Hash[Symbol, untyped]], ?tool_runtime: singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc, ?skills_config: Riffer::Skills::Config?, ?guardrails: Hash[Symbol, Array[Hash[Symbol, untyped]]]) -> void
58
+ def initialize: (?identifier: String?, ?model: (String | Proc)?, ?instructions: (String | Proc)?, ?model_options: Hash[Symbol, untyped], ?structured_output: Riffer::Params?, ?max_steps: Numeric?, ?tools_config: (Array[singleton(Riffer::Tool)] | Proc)?, ?mcp_configs: Array[Hash[Symbol, untyped]], ?tool_runtime: singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc, ?skills_config: Riffer::Skills::Config?, ?guardrails: Hash[Symbol, Array[Hash[Symbol, untyped]]]) -> void
63
59
 
64
60
  # Sets +identifier+, coercing the value to String.
65
61
  # --
@@ -45,12 +45,6 @@ class Riffer::Agent
45
45
  # : (?(String | Proc)?) -> (String | Proc)?
46
46
  def self.instructions: (?(String | Proc)?) -> (String | Proc)?
47
47
 
48
- # Gets or sets provider options passed to the provider client.
49
- #
50
- # --
51
- # : (?Hash[Symbol, untyped]?) -> Hash[Symbol, untyped]
52
- def self.provider_options: (?Hash[Symbol, untyped]?) -> Hash[Symbol, untyped]
53
-
54
48
  # Gets or sets model options passed to generate_text/stream_text.
55
49
  #
56
50
  # --
@@ -7,15 +7,19 @@ class Riffer::Config
7
7
 
8
8
  attr_accessor region(): untyped
9
9
 
10
- def self.new: (?untyped api_token, ?untyped region) -> instance
11
- | (?api_token: untyped, ?region: untyped) -> instance
10
+ attr_accessor client(): untyped
11
+
12
+ def self.new: (?untyped api_token, ?untyped region, ?untyped client) -> instance
13
+ | (?api_token: untyped, ?region: untyped, ?client: untyped) -> instance
12
14
  end
13
15
 
14
16
  class Anthropic < Struct[untyped]
15
17
  attr_accessor api_key(): untyped
16
18
 
17
- def self.new: (?untyped api_key) -> instance
18
- | (?api_key: untyped) -> instance
19
+ attr_accessor client(): untyped
20
+
21
+ def self.new: (?untyped api_key, ?untyped client) -> instance
22
+ | (?api_key: untyped, ?client: untyped) -> instance
19
23
  end
20
24
 
21
25
  class AzureOpenAI < Struct[untyped]
@@ -23,33 +27,39 @@ class Riffer::Config
23
27
 
24
28
  attr_accessor endpoint(): untyped
25
29
 
26
- def self.new: (?untyped api_key, ?untyped endpoint) -> instance
27
- | (?api_key: untyped, ?endpoint: untyped) -> instance
30
+ attr_accessor client(): untyped
31
+
32
+ def self.new: (?untyped api_key, ?untyped endpoint, ?untyped client) -> instance
33
+ | (?api_key: untyped, ?endpoint: untyped, ?client: untyped) -> instance
28
34
  end
29
35
 
30
36
  class Gemini < Struct[untyped]
31
37
  attr_accessor api_key(): untyped
32
38
 
33
- attr_accessor open_timeout(): untyped
34
-
35
- attr_accessor read_timeout(): untyped
39
+ attr_accessor client(): untyped
36
40
 
37
- def self.new: (?untyped api_key, ?untyped open_timeout, ?untyped read_timeout) -> instance
38
- | (?api_key: untyped, ?open_timeout: untyped, ?read_timeout: untyped) -> instance
41
+ def self.new: (?untyped api_key, ?untyped client) -> instance
42
+ | (?api_key: untyped, ?client: untyped) -> instance
39
43
  end
40
44
 
41
45
  class OpenAI < Struct[untyped]
42
46
  attr_accessor api_key(): untyped
43
47
 
44
- def self.new: (?untyped api_key) -> instance
45
- | (?api_key: untyped) -> instance
48
+ attr_accessor base_url(): untyped
49
+
50
+ attr_accessor client(): untyped
51
+
52
+ def self.new: (?untyped api_key, ?untyped base_url, ?untyped client) -> instance
53
+ | (?api_key: untyped, ?base_url: untyped, ?client: untyped) -> instance
46
54
  end
47
55
 
48
56
  class OpenRouter < Struct[untyped]
49
57
  attr_accessor api_key(): untyped
50
58
 
51
- def self.new: (?untyped api_key) -> instance
52
- | (?api_key: untyped) -> instance
59
+ attr_accessor client(): untyped
60
+
61
+ def self.new: (?untyped api_key, ?untyped client) -> instance
62
+ | (?api_key: untyped, ?client: untyped) -> instance
53
63
  end
54
64
 
55
65
  class Evals < Struct[untyped]
@@ -3,8 +3,6 @@
3
3
  # Executes LLM-as-judge evaluations, using tool calling internally to get
4
4
  # structured output from the judge model.
5
5
  class Riffer::Evals::Judge
6
- @provider_options: Hash[Symbol, untyped]
7
-
8
6
  @provider_instance: Riffer::Providers::Base?
9
7
 
10
8
  @provider_name: String?
@@ -23,8 +21,8 @@ class Riffer::Evals::Judge
23
21
 
24
22
  # Raises Riffer::ArgumentError unless +model+ is "provider/model" format.
25
23
  # --
26
- # : (model: String, ?provider_options: Hash[Symbol, untyped]) -> void
27
- def initialize: (model: String, ?provider_options: Hash[Symbol, untyped]) -> void
24
+ # : (model: String) -> void
25
+ def initialize: (model: String) -> void
28
26
 
29
27
  # Evaluates an input/output pair using the configured LLM.
30
28
  # --
@@ -21,11 +21,22 @@ class Riffer::Providers::AmazonBedrock < Riffer::Providers::Base
21
21
  def self.semconv_provider_name: () -> String
22
22
 
23
23
  # --
24
- # : (?api_token: String?, ?region: String?, **untyped) -> void
25
- def initialize: (?api_token: String?, ?region: String?, **untyped) -> void
24
+ # : () -> void
25
+ def initialize: () -> void
26
26
 
27
27
  private
28
28
 
29
+ # --
30
+ # : () -> untyped
31
+ def global_client: () -> untyped
32
+
33
+ # Compacted so an unset region stays absent: the AWS SDK resolves +AWS_REGION+
34
+ # and the shared config only for a missing argument, and raises
35
+ # +Aws::Errors::MissingRegionError+ on an explicit nil.
36
+ # --
37
+ # : () -> untyped
38
+ def build_client: () -> untyped
39
+
29
40
  # --
30
41
  # : (Array[Riffer::Messages::Base], String?, Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
31
42
  def build_request_params: (Array[Riffer::Messages::Base], String?, Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
@@ -19,11 +19,22 @@ class Riffer::Providers::Anthropic < Riffer::Providers::Base
19
19
  def self.semconv_provider_name: () -> String
20
20
 
21
21
  # --
22
- # : (?api_key: String?, **untyped) -> void
23
- def initialize: (?api_key: String?, **untyped) -> void
22
+ # : () -> void
23
+ def initialize: () -> void
24
24
 
25
25
  private
26
26
 
27
+ # --
28
+ # : () -> untyped
29
+ def global_client: () -> untyped
30
+
31
+ # Compacted for the same reason as the other providers: never hand an SDK an
32
+ # explicit nil credential, so its own +ANTHROPIC_API_KEY+ resolution stays
33
+ # reachable regardless of how that SDK distinguishes nil from absent.
34
+ # --
35
+ # : () -> untyped
36
+ def build_client: () -> untyped
37
+
27
38
  # --
28
39
  # : (Array[Riffer::Messages::Base], String?, Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
29
40
  def build_request_params: (Array[Riffer::Messages::Base], String?, Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
@@ -1,15 +1,25 @@
1
1
  # Generated from lib/riffer/providers/azure_open_ai.rb with RBS::Inline
2
2
 
3
3
  # Azure OpenAI provider for GPT models hosted on Azure. Requires the +openai+
4
- # gem. Credentials resolve from kwargs, then config, then
5
- # +AZURE_OPENAI_API_KEY+ / +AZURE_OPENAI_ENDPOINT+.
4
+ # gem. Credentials resolve from config, then +AZURE_OPENAI_API_KEY+ /
5
+ # +AZURE_OPENAI_ENDPOINT+.
6
6
  class Riffer::Providers::AzureOpenAI < Riffer::Providers::OpenAI
7
7
  # The GenAI semconv well-known provider name.
8
8
  # --
9
9
  # : () -> String
10
10
  def self.semconv_provider_name: () -> String
11
11
 
12
+ private
13
+
14
+ # --
15
+ # : () -> untyped
16
+ def global_client: () -> untyped
17
+
18
+ # Deliberately not compacted: this borrows the OpenAI SDK to talk to Azure, so
19
+ # omitting an unset argument would let the SDK fall back to +OPENAI_API_KEY+
20
+ # and +OPENAI_BASE_URL+ — sending Azure traffic, and an OpenAI credential, to
21
+ # whatever those name. Passing nil raises in the SDK instead.
12
22
  # --
13
- # : (**untyped) -> void
14
- def initialize: (**untyped) -> void
23
+ # : () -> untyped
24
+ def build_client: () -> untyped
15
25
  end
@@ -5,6 +5,8 @@
5
5
  # +extract_token_usage+, +extract_content+, +extract_tool_calls+) and the base
6
6
  # class orchestrates them.
7
7
  class Riffer::Providers::Base
8
+ @client: untyped
9
+
8
10
  @current_model: String?
9
11
 
10
12
  @current_tools: Array[singleton(Riffer::Tool)]
@@ -42,6 +44,24 @@ class Riffer::Providers::Base
42
44
  # : (String) -> true
43
45
  def depends_on: (String) -> true
44
46
 
47
+ # Returns the client for the current LLM call. A configured client wins,
48
+ # resolved on every call so a Proc can vary the client by process or
49
+ # credential lifetime; otherwise the provider builds one from the configured
50
+ # credentials, memoized for the life of the provider.
51
+ # --
52
+ # : () -> untyped
53
+ def client: () -> untyped
54
+
55
+ # Returns the consumer-configured client for this provider; nil when none is
56
+ # configured, and for providers that take no configuration at all.
57
+ # --
58
+ # : () -> untyped
59
+ def global_client: () -> untyped
60
+
61
+ # --
62
+ # : () -> untyped
63
+ def build_client: () -> untyped
64
+
45
65
  # --
46
66
  # : (String) -> String
47
67
  def encode_tool_name: (String) -> String
@@ -0,0 +1,65 @@
1
+ # Generated from lib/riffer/providers/gemini/client.rb with RBS::Inline
2
+
3
+ # HTTP transport for the Gemini REST API. Riffer builds one from the
4
+ # configured +api_key+ by default; construct your own to tune the HTTP knobs
5
+ # and assign it to <tt>Riffer.config.gemini.client</tt>. Any object
6
+ # implementing +post+ and +post_stream+ with these contracts works there —
7
+ # the class is a default implementation, not a required base.
8
+ #
9
+ # Riffer.configure do |config|
10
+ # config.gemini.client = Riffer::Providers::Gemini::Client.new(
11
+ # api_key: ENV["GEMINI_API_KEY"],
12
+ # read_timeout: 120
13
+ # )
14
+ # end
15
+ class Riffer::Providers::Gemini::Client
16
+ @proxy_port: Integer?
17
+
18
+ @proxy_address: String?
19
+
20
+ @write_timeout: Integer?
21
+
22
+ @read_timeout: Integer
23
+
24
+ @open_timeout: Integer
25
+
26
+ @base_url: String
27
+
28
+ @api_key: String?
29
+
30
+ DEFAULT_BASE_URL: String
31
+
32
+ DEFAULT_OPEN_TIMEOUT: Integer
33
+
34
+ DEFAULT_READ_TIMEOUT: Integer
35
+
36
+ # : (?api_key: String?, ?base_url: String, ?open_timeout: Integer, ?read_timeout: Integer, ?write_timeout: Integer?, ?proxy_address: String?, ?proxy_port: Integer?) -> void
37
+ def initialize: (?api_key: String?, ?base_url: String, ?open_timeout: Integer, ?read_timeout: Integer, ?write_timeout: Integer?, ?proxy_address: String?, ?proxy_port: Integer?) -> void
38
+
39
+ # POSTs a JSON body to an API path and returns the parsed response hash.
40
+ # Raises Riffer::Error when the API responds with a non-success status.
41
+ # --
42
+ # : (String, Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
43
+ def post: (String, Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
44
+
45
+ # POSTs a JSON body to an API path, yielding raw response body chunks as
46
+ # they arrive. Raises Riffer::Error when the API responds with a
47
+ # non-success status.
48
+ # --
49
+ # : (String, Hash[Symbol, untyped]) { (String) -> void } -> void
50
+ def post_stream: (String, Hash[Symbol, untyped]) { (String) -> void } -> void
51
+
52
+ private
53
+
54
+ # --
55
+ # : (URI::Generic, Hash[Symbol, untyped]) -> Net::HTTP::Post
56
+ def build_request: (URI::Generic, Hash[Symbol, untyped]) -> Net::HTTP::Post
57
+
58
+ # --
59
+ # : [R] (URI::Generic) { (Net::HTTP) -> R } -> R
60
+ def start_http: [R] (URI::Generic) { (Net::HTTP) -> R } -> R
61
+
62
+ # --
63
+ # : (Net::HTTPResponse) -> void
64
+ def handle_api_error!: (Net::HTTPResponse) -> void
65
+ end