active_genie 0.25.0 → 0.25.2

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -5
  3. data/VERSION +1 -1
  4. data/lib/active_genie/battle/README.md +7 -7
  5. data/lib/active_genie/battle/generalist.json +36 -0
  6. data/lib/active_genie/battle/generalist.md +16 -0
  7. data/lib/active_genie/battle/generalist.rb +16 -69
  8. data/lib/active_genie/clients/providers/anthropic_client.rb +62 -39
  9. data/lib/active_genie/clients/providers/base_client.rb +38 -51
  10. data/lib/active_genie/clients/providers/deepseek_client.rb +56 -49
  11. data/lib/active_genie/clients/providers/google_client.rb +53 -53
  12. data/lib/active_genie/clients/providers/openai_client.rb +53 -54
  13. data/lib/active_genie/clients/unified_client.rb +6 -5
  14. data/lib/active_genie/config/battle_config.rb +2 -0
  15. data/lib/active_genie/config/data_extractor_config.rb +3 -3
  16. data/lib/active_genie/config/llm_config.rb +3 -1
  17. data/lib/active_genie/config/log_config.rb +33 -11
  18. data/lib/active_genie/config/providers/anthropic_config.rb +2 -2
  19. data/lib/active_genie/config/providers/deepseek_config.rb +2 -2
  20. data/lib/active_genie/config/providers/google_config.rb +2 -2
  21. data/lib/active_genie/config/providers/openai_config.rb +2 -2
  22. data/lib/active_genie/config/providers_config.rb +6 -5
  23. data/lib/active_genie/config/scoring_config.rb +2 -0
  24. data/lib/active_genie/configuration.rb +14 -6
  25. data/lib/active_genie/data_extractor/from_informal.json +11 -0
  26. data/lib/active_genie/data_extractor/from_informal.rb +3 -11
  27. data/lib/active_genie/data_extractor/generalist.json +9 -0
  28. data/lib/active_genie/data_extractor/generalist.rb +14 -13
  29. data/lib/active_genie/errors/invalid_log_output_error.rb +19 -0
  30. data/lib/active_genie/errors/invalid_provider_error.rb +8 -4
  31. data/lib/active_genie/logger.rb +10 -4
  32. data/lib/active_genie/{concerns → ranking/concerns}/loggable.rb +2 -5
  33. data/lib/active_genie/ranking/elo_round.rb +31 -27
  34. data/lib/active_genie/ranking/free_for_all.rb +29 -21
  35. data/lib/active_genie/ranking/player.rb +45 -17
  36. data/lib/active_genie/ranking/players_collection.rb +16 -6
  37. data/lib/active_genie/ranking/ranking.rb +21 -20
  38. data/lib/active_genie/ranking/ranking_scoring.rb +2 -19
  39. data/lib/active_genie/scoring/generalist.json +9 -0
  40. data/lib/active_genie/scoring/generalist.md +46 -0
  41. data/lib/active_genie/scoring/generalist.rb +13 -65
  42. data/lib/active_genie/scoring/recommended_reviewers.rb +2 -2
  43. metadata +11 -4
@@ -3,88 +3,95 @@
3
3
  require 'json'
4
4
  require 'net/http'
5
5
 
6
- require_relative './base_client'
6
+ require_relative 'base_client'
7
7
 
8
8
  module ActiveGenie
9
9
  module Clients
10
10
  class DeepseekClient < BaseClient
11
- class DeepseekError < ClientError; end
12
- class RateLimitError < DeepseekError; end
13
11
  class InvalidResponseError < StandardError; end
14
12
 
15
- # Requests structured JSON output from the DeepSeek model based on a schema.
13
+ # Requests structured JSON output from the Deepseek model based on a schema.
16
14
  #
17
15
  # @param messages [Array<Hash>] A list of messages representing the conversation history.
18
16
  # Each hash should have :role ('user', 'assistant', or 'system') and :content (String).
19
17
  # @param function [Hash] A JSON schema definition describing the desired output format.
20
18
  # @return [Hash, nil] The parsed JSON object matching the schema, or nil if parsing fails or content is empty.
21
19
  def function_calling(messages, function)
22
- model = @config.llm.model || @config.providers.deepseek.tier_to_model(@config.llm.model_tier)
23
-
24
20
  payload = {
25
21
  messages:,
26
- tools: [{
27
- type: 'function',
28
- function: {
29
- **function,
30
- parameters: {
31
- **function[:parameters],
32
- additionalProperties: false
33
- },
34
- strict: true
35
- }.compact
36
- }],
22
+ tools: [function_to_tool(function)],
37
23
  tool_choice: { type: 'function', function: { name: function[:name] } },
38
24
  stream: false,
39
25
  model:
40
26
  }
41
27
 
42
- headers = {
43
- 'Authorization': "Bearer #{@config.providers.deepseek.api_key}"
44
- }.compact
45
-
46
28
  retry_with_backoff do
47
- response = request_deepseek(payload, headers)
29
+ response = request(payload)
48
30
 
49
- parsed_response = JSON.parse(response.dig('choices', 0, 'message', 'tool_calls', 0, 'function', 'arguments'))
50
- parsed_response = parsed_response['message'] || parsed_response
51
-
52
- if parsed_response.nil? || parsed_response.keys.empty?
31
+ if response.nil? || response.keys.empty?
53
32
  raise InvalidResponseError,
54
- "Invalid response: #{parsed_response}"
33
+ "Invalid response: #{response}"
55
34
  end
56
35
 
57
- ActiveGenie::Logger.call({ code: :function_calling, payload:, parsed_response: })
36
+ ActiveGenie::Logger.call({ code: :function_calling, fine_tune: true, payload:, response: })
58
37
 
59
- parsed_response
38
+ response
60
39
  end
61
40
  end
62
41
 
63
42
  private
64
43
 
65
- # Make a request to the DeepSeek API
66
- #
67
- # @param payload [Hash] The request payload
68
- # @param headers [Hash] Additional headers
69
- # @return [Hash] The parsed response
70
- def request_deepseek(payload, headers)
71
- start_time = Time.now
72
- url = "#{@config.providers.deepseek.api_url}/chat/completions"
73
-
44
+ def request(payload)
74
45
  response = post(url, payload, headers: headers)
46
+
75
47
  return nil if response.nil?
76
48
 
77
- ActiveGenie::Logger.call({
78
- code: :llm_usage,
79
- input_tokens: response.dig('usage', 'prompt_tokens'),
80
- output_tokens: response.dig('usage', 'completion_tokens'),
81
- total_tokens: response.dig('usage', 'total_tokens'),
82
- model: payload[:model],
83
- duration: Time.now - start_time,
84
- usage: response['usage']
85
- })
86
-
87
- response
49
+ ActiveGenie::Logger.call(
50
+ {
51
+ code: :llm_usage,
52
+ input_tokens: response.dig('usage', 'prompt_tokens'),
53
+ output_tokens: response.dig('usage', 'completion_tokens'),
54
+ total_tokens: response.dig('usage', 'total_tokens'),
55
+ model:,
56
+ usage: response['usage']
57
+ }
58
+ )
59
+
60
+ parsed_response = JSON.parse(response.dig('choices', 0, 'message', 'tool_calls', 0, 'function', 'arguments'))
61
+ parsed_response['message'] || parsed_response
62
+ end
63
+
64
+ def function_to_tool(function)
65
+ {
66
+ type: 'function',
67
+ function: {
68
+ **function,
69
+ parameters: {
70
+ **function[:parameters],
71
+ additionalProperties: false
72
+ },
73
+ strict: true
74
+ }.compact
75
+ }
76
+ end
77
+
78
+ def model
79
+ @config.llm.model || provider_config.tier_to_model(@config.llm.model_tier)
80
+ end
81
+
82
+ def url
83
+ "#{provider_config.api_url}/chat/completions"
84
+ end
85
+
86
+ def headers
87
+ {
88
+ Authorization: "Bearer #{provider_config.api_key}",
89
+ 'Content-Type': 'application/json'
90
+ }.compact
91
+ end
92
+
93
+ def provider_config
94
+ @config.providers.deepseek
88
95
  end
89
96
  end
90
97
  end
@@ -3,17 +3,12 @@
3
3
  require 'json'
4
4
  require 'net/http'
5
5
  require 'uri'
6
- require_relative './base_client'
6
+ require_relative 'base_client'
7
7
 
8
8
  module ActiveGenie
9
9
  module Clients
10
10
  # Client for interacting with the Google Generative Language API.
11
11
  class GoogleClient < BaseClient
12
- class GoogleError < ClientError; end
13
- class RateLimitError < GoogleError; end
14
-
15
- API_VERSION_PATH = '/v1beta/models'
16
-
17
12
  # Requests structured JSON output from the Google Generative Language model based on a schema.
18
13
  #
19
14
  # @param messages [Array<Hash>] A list of messages representing the conversation history.
@@ -22,81 +17,74 @@ module ActiveGenie
22
17
  # @param function [Hash] A JSON schema definition describing the desired output format.
23
18
  # @return [Hash, nil] The parsed JSON object matching the schema, or nil if parsing fails or content is empty.
24
19
  def function_calling(messages, function)
25
- model = @config.llm.model || provider_config.tier_to_model(@config.llm.model_tier)
26
-
27
20
  contents = convert_messages_to_contents(messages, function)
28
21
  contents << output_as_json_schema(function)
29
22
 
30
23
  payload = {
31
- contents: contents,
24
+ contents:,
32
25
  generationConfig: {
33
26
  response_mime_type: 'application/json',
34
27
  temperature: 0.1
35
28
  }
36
29
  }
37
-
38
- endpoint = "#{API_VERSION_PATH}/#{model}:generateContent"
39
30
  params = { key: provider_config.api_key }
40
- headers = DEFAULT_HEADERS
41
31
 
42
32
  retry_with_backoff do
43
- start_time = Time.now
44
- url = "#{provider_config.api_url}#{endpoint}"
45
-
46
- response = post(url, payload, headers:, params:)
33
+ response = request(payload, params)
47
34
 
48
35
  json_string = response&.dig('candidates', 0, 'content', 'parts', 0, 'text')
49
36
  return nil if json_string.nil? || json_string.empty?
50
37
 
51
- begin
52
- parsed_response = JSON.parse(json_string)
53
-
54
- # Log usage metrics
55
- usage_metadata = response['usageMetadata'] || {}
56
- prompt_tokens = usage_metadata['promptTokenCount'] || 0
57
- candidates_tokens = usage_metadata['candidatesTokenCount'] || 0
58
- total_tokens = usage_metadata['totalTokenCount'] || (prompt_tokens + candidates_tokens)
59
-
60
- ActiveGenie::Logger.call({
61
- code: :llm_usage,
62
- input_tokens: prompt_tokens,
63
- output_tokens: candidates_tokens,
64
- total_tokens: total_tokens,
65
- model: model,
66
- duration: Time.now - start_time,
67
- usage: usage_metadata
68
- })
69
-
70
- ActiveGenie::Logger.call({ code: :function_calling, payload:, parsed_response: })
71
-
72
- normalize_function_output(parsed_response)
73
- rescue JSON::ParserError => e
74
- raise GoogleError, "Failed to parse Google API response: #{e.message} - Content: #{json_string}"
75
- end
38
+ normalize_response(json_string)
76
39
  end
77
40
  end
78
41
 
79
- def provider_config
80
- @config.providers.google
81
- end
42
+ API_VERSION_PATH = 'v1beta/models'
43
+ ROLE_TO_GOOGLE_ROLE = {
44
+ user: 'user',
45
+ assistant: 'model'
46
+ }.freeze
82
47
 
83
48
  private
84
49
 
85
- def normalize_function_output(output)
86
- output = if output.is_a?(Array)
87
- output.dig(0, 'properties') || output[0]
50
+ def request(payload, params)
51
+ response = post(url, payload, headers: DEFAULT_HEADERS, params:)
52
+
53
+ ActiveGenie::Logger.call(
54
+ {
55
+ code: :llm_usage,
56
+ input_tokens: response['usageMetadata']['promptTokenCount'] || 0,
57
+ output_tokens: response['usageMetadata']['candidatesTokenCount'] || 0,
58
+ total_tokens: response['usageMetadata']['totalTokenCount'] || (prompt_tokens + candidates_tokens),
59
+ model:,
60
+ usage: response['usageMetadata'] || {}
61
+ }
62
+ )
63
+
64
+ ActiveGenie::Logger.call(
65
+ {
66
+ code: :function_calling,
67
+ fine_tune: true,
68
+ payload:,
69
+ parsed_response: response&.dig('candidates', 0, 'content', 'parts', 0, 'text')
70
+ }
71
+ )
72
+
73
+ response
74
+ end
75
+
76
+ def normalize_response(json_string)
77
+ parsed_response = JSON.parse(json_string)
78
+
79
+ output = if parsed_response.is_a?(Array)
80
+ parsed_response.dig(0, 'properties') || parsed_response[0]
88
81
  else
89
- output
82
+ parsed_response
90
83
  end
91
84
 
92
85
  output.dig('input_schema', 'properties') || output
93
86
  end
94
87
 
95
- ROLE_TO_GOOGLE_ROLE = {
96
- user: 'user',
97
- assistant: 'model'
98
- }.freeze
99
-
100
88
  # Converts standard message format to Google's 'contents' format
101
89
  # and injects JSON schema instructions.
102
90
  # @param messages [Array<Hash>] Array of { role: 'user'/'assistant'/'system', content: '...' }
@@ -127,6 +115,18 @@ module ActiveGenie
127
115
  parts: [{ text: json_instruction }]
128
116
  }
129
117
  end
118
+
119
+ def model
120
+ @config.llm.model || provider_config.tier_to_model(@config.llm.model_tier)
121
+ end
122
+
123
+ def url
124
+ "#{provider_config.api_url}/#{API_VERSION_PATH}/#{model}:generateContent"
125
+ end
126
+
127
+ def provider_config
128
+ @config.providers.google
129
+ end
130
130
  end
131
131
  end
132
132
  end
@@ -3,13 +3,11 @@
3
3
  require 'json'
4
4
  require 'net/http'
5
5
 
6
- require_relative './base_client'
6
+ require_relative 'base_client'
7
7
 
8
8
  module ActiveGenie
9
9
  module Clients
10
10
  class OpenaiClient < BaseClient
11
- class OpenaiError < ClientError; end
12
- class RateLimitError < OpenaiError; end
13
11
  class InvalidResponseError < StandardError; end
14
12
 
15
13
  # Requests structured JSON output from the OpenAI model based on a schema.
@@ -19,77 +17,78 @@ module ActiveGenie
19
17
  # @param function [Hash] A JSON schema definition describing the desired output format.
20
18
  # @return [Hash, nil] The parsed JSON object matching the schema, or nil if parsing fails or content is empty.
21
19
  def function_calling(messages, function)
22
- model = @config.llm.model || provider_config.tier_to_model(@config.llm.model_tier)
23
-
24
20
  payload = {
25
21
  messages:,
26
- tools: [{
27
- type: 'function',
28
- function: {
29
- **function,
30
- parameters: {
31
- **function[:parameters],
32
- additionalProperties: false
33
- },
34
- strict: true
35
- }.compact
36
- }],
22
+ tools: [function_to_tool(function)],
37
23
  tool_choice: { type: 'function', function: { name: function[:name] } },
38
24
  stream: false,
39
25
  model:
40
26
  }
41
27
 
42
- headers = {
43
- 'Authorization': "Bearer #{provider_config.api_key}"
44
- }.compact
45
-
46
28
  retry_with_backoff do
47
- response = request_openai(payload, headers)
29
+ response = request(payload)
48
30
 
49
- parsed_response = JSON.parse(response.dig('choices', 0, 'message', 'tool_calls', 0, 'function', 'arguments'))
50
- parsed_response = parsed_response['message'] || parsed_response
31
+ raise InvalidResponseError, "Invalid response: #{response}" if response.nil? || response.keys.empty?
51
32
 
52
- if parsed_response.nil? || parsed_response.keys.empty?
53
- raise InvalidResponseError,
54
- "Invalid response: #{parsed_response}"
55
- end
33
+ ActiveGenie::Logger.call({ code: :function_calling, fine_tune: true, payload:, response: })
56
34
 
57
- ActiveGenie::Logger.call({ code: :function_calling, payload:, parsed_response: })
58
-
59
- parsed_response
35
+ response
60
36
  end
61
37
  end
62
38
 
63
- def provider_config
64
- @config.providers.openai
65
- end
66
-
67
39
  private
68
40
 
69
- # Make a request to the OpenAI API
70
- #
71
- # @param payload [Hash] The request payload
72
- # @param headers [Hash] Additional headers
73
- # @return [Hash] The parsed response
74
- def request_openai(payload, headers)
75
- start_time = Time.now
76
- url = "#{provider_config.api_url}/chat/completions"
77
-
41
+ def request(payload)
78
42
  response = post(url, payload, headers: headers)
79
43
 
80
44
  return nil if response.nil?
81
45
 
82
- ActiveGenie::Logger.call({
83
- code: :llm_usage,
84
- input_tokens: response.dig('usage', 'prompt_tokens'),
85
- output_tokens: response.dig('usage', 'completion_tokens'),
86
- total_tokens: response.dig('usage', 'total_tokens'),
87
- model: payload[:model],
88
- duration: Time.now - start_time,
89
- usage: response['usage']
90
- })
91
-
92
- response
46
+ ActiveGenie::Logger.call(
47
+ {
48
+ code: :llm_usage,
49
+ input_tokens: response.dig('usage', 'prompt_tokens'),
50
+ output_tokens: response.dig('usage', 'completion_tokens'),
51
+ total_tokens: response.dig('usage', 'total_tokens'),
52
+ model:,
53
+ usage: response['usage']
54
+ }
55
+ )
56
+
57
+ parsed_response = JSON.parse(response.dig('choices', 0, 'message', 'tool_calls', 0, 'function', 'arguments'))
58
+ parsed_response['message'] || parsed_response
59
+ end
60
+
61
+ def function_to_tool(function)
62
+ {
63
+ type: 'function',
64
+ function: {
65
+ **function,
66
+ parameters: {
67
+ **function[:parameters],
68
+ additionalProperties: false
69
+ },
70
+ strict: true
71
+ }.compact
72
+ }
73
+ end
74
+
75
+ def model
76
+ @config.llm.model || provider_config.tier_to_model(@config.llm.model_tier)
77
+ end
78
+
79
+ def url
80
+ "#{provider_config.api_url}/chat/completions"
81
+ end
82
+
83
+ def headers
84
+ {
85
+ Authorization: "Bearer #{provider_config.api_key}",
86
+ 'Content-Type': 'application/json'
87
+ }.compact
88
+ end
89
+
90
+ def provider_config
91
+ @config.providers.openai
93
92
  end
94
93
  end
95
94
  end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './providers/openai_client'
4
- require_relative './providers/anthropic_client'
5
- require_relative './providers/google_client'
6
- require_relative './providers/deepseek_client'
3
+ require_relative 'providers/openai_client'
4
+ require_relative 'providers/anthropic_client'
5
+ require_relative 'providers/google_client'
6
+ require_relative 'providers/deepseek_client'
7
7
  require_relative '../errors/invalid_provider_error'
8
8
 
9
9
  module ActiveGenie
@@ -38,7 +38,8 @@ module ActiveGenie
38
38
 
39
39
  def normalize_response(response)
40
40
  response.each do |key, value|
41
- response[key] = nil if ['null', 'none', 'undefined', '', 'unknown', '<unknown>'].include?(value.to_s.strip.downcase)
41
+ response[key] = nil if ['null', 'none', 'undefined', '', 'unknown',
42
+ '<unknown>'].include?(value.to_s.strip.downcase)
42
43
  end
43
44
 
44
45
  response
@@ -2,7 +2,9 @@
2
2
 
3
3
  module ActiveGenie
4
4
  module Config
5
+ # rubocop:disable Lint/EmptyClass
5
6
  class BattleConfig
6
7
  end
8
+ # rubocop:enable Lint/EmptyClass
7
9
  end
8
10
  end
@@ -13,9 +13,9 @@ module ActiveGenie
13
13
 
14
14
  def merge(config_params = {})
15
15
  dup.tap do |config|
16
- config.with_explanation = config_params[:with_explanation] if config_params[:with_explanation]
17
- config.min_accuracy = config_params[:min_accuracy] if config_params[:min_accuracy]
18
- config.verbose = config_params[:verbose] if config_params[:verbose]
16
+ config.with_explanation = config_params[:with_explanation] if config_params.key?(:with_explanation)
17
+ config.min_accuracy = config_params[:min_accuracy] if config_params.key?(:min_accuracy)
18
+ config.verbose = config_params[:verbose] if config_params.key?(:verbose)
19
19
  end
20
20
  end
21
21
  end
@@ -21,7 +21,9 @@ module ActiveGenie
21
21
  end
22
22
 
23
23
  def provider=(provider)
24
- @provider = provider&.to_s&.downcase&.strip&.to_sym
24
+ return if provider.nil? || provider.empty?
25
+
26
+ @provider = provider.to_s.downcase.strip.to_sym
25
27
  end
26
28
 
27
29
  def merge(config_params = {})
@@ -3,10 +3,42 @@
3
3
  module ActiveGenie
4
4
  module Config
5
5
  class LogConfig
6
+ attr_writer :file_path, :fine_tune_file_path
7
+
8
+ def file_path
9
+ @file_path || 'log/active_genie.log'
10
+ end
11
+
12
+ def fine_tune_file_path
13
+ @fine_tune_file_path || 'log/active_genie_fine_tune.log'
14
+ end
15
+
16
+ def output
17
+ @output || ->(log) { $stdout.puts log }
18
+ end
19
+
20
+ def output=(output)
21
+ raise InvalidLogOutputError, output unless output.respond_to?(:call)
22
+
23
+ @output = output
24
+ end
25
+
26
+ def output_call(log)
27
+ output.call(log)
28
+
29
+ Array(@observers).each do |obs|
30
+ next unless obs[:scope].all? { |key, value| log[key.to_sym] == value }
31
+
32
+ obs[:observer].call(log)
33
+ rescue StandardError => e
34
+ ActiveGenie::Logger.call(code: :observer_error, **obs, error: e.message)
35
+ end
36
+ end
37
+
6
38
  def add_observer(observers: [], scope: nil, &block)
7
39
  @observers ||= []
8
40
 
9
- raise ArgumentError, 'Scope must be a hash' if !scope.nil? && !scope.is_a?(Hash)
41
+ raise ArgumentError, 'Scope must be a hash' unless scope.is_a?(Hash)
10
42
 
11
43
  @observers << { observer: block, scope: scope || {} } if block_given?
12
44
  Array(observers).each do |observer|
@@ -24,16 +56,6 @@ module ActiveGenie
24
56
  @observers = []
25
57
  end
26
58
 
27
- def call_observers(log)
28
- Array(@observers).each do |obs|
29
- next unless obs[:scope].all? { |key, value| log[key.to_sym] == value }
30
-
31
- obs[:observer].call(log)
32
- rescue StandardError => e
33
- ActiveGenie::Logger.call(code: :observer_error, **obs, error: e.message)
34
- end
35
- end
36
-
37
59
  def merge(config_params = {})
38
60
  dup.tap do |config|
39
61
  config.add_observer(config_params[:observers]) if config_params[:observers]
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './provider_base'
3
+ require_relative 'provider_base'
4
4
 
5
5
  module ActiveGenie
6
6
  module Config
@@ -14,7 +14,7 @@ module ActiveGenie
14
14
  # Falls back to the ANTHROPIC_API_KEY environment variable if not set.
15
15
  # @return [String, nil] The API key.
16
16
  def api_key
17
- @api_key || ENV['ANTHROPIC_API_KEY']
17
+ @api_key || ENV.fetch('ANTHROPIC_API_KEY', nil)
18
18
  end
19
19
 
20
20
  # Retrieves the base API URL for Anthropic API.
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './provider_base'
3
+ require_relative 'provider_base'
4
4
 
5
5
  module ActiveGenie
6
6
  module Config
@@ -14,7 +14,7 @@ module ActiveGenie
14
14
  # Falls back to the DEEPSEEK_API_KEY environment variable if not set.
15
15
  # @return [String, nil] The API key.
16
16
  def api_key
17
- @api_key || ENV['DEEPSEEK_API_KEY']
17
+ @api_key || ENV.fetch('DEEPSEEK_API_KEY', nil)
18
18
  end
19
19
 
20
20
  # Retrieves the base API URL for DeepSeek API.
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './provider_base'
3
+ require_relative 'provider_base'
4
4
 
5
5
  module ActiveGenie
6
6
  module Config
@@ -14,7 +14,7 @@ module ActiveGenie
14
14
  # Falls back to the GENERATIVE_LANGUAGE_GOOGLE_API_KEY environment variable if not set.
15
15
  # @return [String, nil] The API key.
16
16
  def api_key
17
- @api_key || ENV['GENERATIVE_LANGUAGE_GOOGLE_API_KEY'] || ENV['GEMINI_API_KEY']
17
+ @api_key || ENV['GENERATIVE_LANGUAGE_GOOGLE_API_KEY'] || ENV.fetch('GEMINI_API_KEY', nil)
18
18
  end
19
19
 
20
20
  # Retrieves the base API URL for Google Generative Language API.
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './provider_base'
3
+ require_relative 'provider_base'
4
4
 
5
5
  module ActiveGenie
6
6
  module Config
@@ -14,7 +14,7 @@ module ActiveGenie
14
14
  # Falls back to the OPENAI_API_KEY environment variable if not set.
15
15
  # @return [String, nil] The API key.
16
16
  def api_key
17
- @api_key || ENV['OPENAI_API_KEY']
17
+ @api_key || ENV.fetch('OPENAI_API_KEY', nil)
18
18
  end
19
19
 
20
20
  # Retrieves the base API URL for OpenAI API.
@@ -16,7 +16,7 @@ module ActiveGenie
16
16
 
17
17
  def default=(provider)
18
18
  normalized_provider = provider.to_s.downcase.strip
19
- @default = normalized_provider.size > 0 ? normalized_provider : valid.keys.first
19
+ @default = normalized_provider.size.positive? ? normalized_provider : valid.keys.first
20
20
  end
21
21
 
22
22
  def valid
@@ -47,15 +47,16 @@ module ActiveGenie
47
47
  def merge(config_params = {})
48
48
  dup.tap do |config|
49
49
  config.add(config_params[:providers]) if config_params[:providers]
50
+ config.default = config_params[:default] if config_params[:default]
50
51
  end
51
52
  end
52
53
 
53
- def method_missing(m, *args, &block)
54
- @all[m] || super
54
+ def method_missing(method_name, *args, &)
55
+ @all[method_name] || super
55
56
  end
56
57
 
57
- def respond_to_missing?(m, include_private = false)
58
- @all.key?(m) || super
58
+ def respond_to_missing?(method_name, include_private = false)
59
+ @all.key?(method_name) || super
59
60
  end
60
61
  end
61
62
  end