active_genie 0.26.0 → 0.26.6

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 (29) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +13 -9
  3. data/VERSION +1 -1
  4. data/lib/active_genie/battle/generalist.rb +8 -8
  5. data/lib/active_genie/clients/providers/anthropic_client.rb +2 -2
  6. data/lib/active_genie/clients/providers/base_client.rb +2 -2
  7. data/lib/active_genie/clients/providers/deepseek_client.rb +2 -2
  8. data/lib/active_genie/clients/providers/google_client.rb +3 -10
  9. data/lib/active_genie/clients/providers/openai_client.rb +2 -2
  10. data/lib/active_genie/config/README.md +235 -0
  11. data/lib/active_genie/config/log_config.rb +12 -15
  12. data/lib/active_genie/config/providers/anthropic_config.rb +2 -2
  13. data/lib/active_genie/config/providers/deepseek_config.rb +2 -2
  14. data/lib/active_genie/config/providers/google_config.rb +2 -2
  15. data/lib/active_genie/config/providers/openai_config.rb +2 -2
  16. data/lib/active_genie/config/providers/provider_base.rb +4 -4
  17. data/lib/active_genie/configuration.rb +16 -4
  18. data/lib/active_genie/data_extractor/generalist.rb +1 -1
  19. data/lib/active_genie/logger.rb +40 -22
  20. data/lib/active_genie/ranking/elo_round.rb +38 -38
  21. data/lib/active_genie/ranking/free_for_all.rb +29 -28
  22. data/lib/active_genie/ranking/player.rb +6 -18
  23. data/lib/active_genie/ranking/players_collection.rb +6 -2
  24. data/lib/active_genie/ranking/ranking.rb +32 -25
  25. data/lib/active_genie/ranking/ranking_scoring.rb +12 -17
  26. data/lib/active_genie/scoring/generalist.rb +8 -8
  27. data/lib/tasks/templates/active_genie.rb +1 -1
  28. metadata +3 -3
  29. data/lib/active_genie/ranking/concerns/loggable.rb +0 -29
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 848de45258263935accfd02ce16d14eb96c6b5a73f42979fe0a74e5b9b746fb4
4
- data.tar.gz: cb1f9eb843559a7040dc20b1442e120325d734b7610a75b57bfde91cba63dc4e
3
+ metadata.gz: 47ad459a3acb6b1e37b8cc9d30e072fb1a699177a628212a2b33406c7d019ace
4
+ data.tar.gz: 9ce87f41aa773aae74fa0a822e492263e95d7c4f63aac26eb77a8385351eaa6b
5
5
  SHA512:
6
- metadata.gz: 12f38e95348fae6beab3cc7a74cfdefc132ef9e8308303f2df04a40703d6f86a9fe6f859c958dc8a1d9fca52d41ceb1f380324c5b34d5d13e0bde4a702362830
7
- data.tar.gz: c512e6ae0fb0b47403f9c2fc3be5c55a458320877fe2aae93780af1fab4e95b2fa1553ff60a62ec30653933f77a5c8892e96227ee637e8ab03a30cf0c9ad177d
6
+ metadata.gz: 3a15a6742977c8f1a38f1eab5e5d9a5ef30d79cfd9db8ba630775f7629cd25486e00b63a2122a01281a303e396a0d5faefdcb4b399a65b4eaaf9aeb50ddac116
7
+ data.tar.gz: 4f24bee6532d2251e9051067582bc4bcd1e9ef685996ff34eff15416976774387731917f66482c62efc9d205c552fcd3aacb8b73f869005585fd66fd86b89d12
data/README.md CHANGED
@@ -204,18 +204,22 @@ bundle exec rake active_genie:benchmark[data_extractor]
204
204
 
205
205
  See the [Benchmark README](benchmark/README.md) for detailed results, methodology, and how to contribute to our test suite.
206
206
 
207
- ## Configuration
208
-
209
- | Config | Description | Default |
210
- |--------|-------------|---------|
211
- | `provider` | LLM provider (openai, anthropic, etc) | `nil` |
212
- | `model` | Model to use | `nil` |
213
- | `api_key` | Provider API key | `nil` |
214
- | `timeout` | Request timeout in seconds | `5` |
215
- | `max_retries` | Maximum retry attempts | `3` |
207
+ ## Basic Configuration
208
+
209
+ | Config | Type | Description | Default |
210
+ |--------|------|-------------|---------|
211
+ | `llm.provider` | Symbol | LLM provider (openai, anthropic, etc) | `nil` |
212
+ | `llm.model` | String | Model to use | `nil` |
213
+ | `llm.temperature` | Float | Temperature to use | `0` |
214
+ | `llm.max_tokens` | Integer | Maximum tokens to use | `4096` |
215
+ | `llm.max_retries` | Integer | Maximum retry attempts | `3` |
216
+ | `log.output` | Proc | Log output | `->(log) { $stdout.puts log }` |
217
+ | `ranking.score_variation_threshold` | Integer | Score variation threshold | `30` |
216
218
 
217
219
  > **Note:** Each module can append its own set of configuration, see the individual module documentation for details.
218
220
 
221
+ Read all [configuration](lib/active_genie/config/README.md) for all available options.
222
+
219
223
  ## How to create a new provider
220
224
 
221
225
  ActiveGenie supports adding custom providers to integrate with different LLM services. To create a new provider:
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.26.0
1
+ 0.26.6
@@ -48,14 +48,14 @@ module ActiveGenie
48
48
  config: @config
49
49
  )
50
50
 
51
- ActiveGenie::Logger.call({
52
- code: :battle,
53
- player_a: @player_a[0..30],
54
- player_b: @player_b[0..30],
55
- criteria: @criteria[0..30],
56
- winner: response['impartial_judge_winner'],
57
- reasoning: response['impartial_judge_winner_reasoning']
58
- })
51
+ @config.logger.call({
52
+ code: :battle,
53
+ player_a: @player_a[0..30],
54
+ player_b: @player_b[0..30],
55
+ criteria: @criteria[0..30],
56
+ winner: response['impartial_judge_winner'],
57
+ reasoning: response['impartial_judge_winner_reasoning']
58
+ })
59
59
 
60
60
  response_formatted(response)
61
61
  end
@@ -55,7 +55,7 @@ module ActiveGenie
55
55
  def request(payload)
56
56
  response = post(url, payload, headers:)
57
57
 
58
- ActiveGenie::Logger.call(
58
+ @config.logger.call(
59
59
  {
60
60
  code: :llm_usage,
61
61
  input_tokens: response.dig('usage', 'input_tokens'),
@@ -67,7 +67,7 @@ module ActiveGenie
67
67
  usage: response['usage']
68
68
  }
69
69
  )
70
- ActiveGenie::Logger.call(
70
+ @config.logger.call(
71
71
  {
72
72
  code: :function_calling,
73
73
  fine_tune: true,
@@ -151,7 +151,7 @@ module ActiveGenie
151
151
  #
152
152
  # @param details [Hash] Request and response details
153
153
  def log_request_details(uri:, request:, response:, start_time:, parsed_response:)
154
- ActiveGenie::Logger.call(
154
+ @config.logger.call(
155
155
  {
156
156
  code: :http_request,
157
157
  uri: uri.to_s,
@@ -176,7 +176,7 @@ module ActiveGenie
176
176
  sleep_time = retry_delay * (2**retries)
177
177
  retries += 1
178
178
 
179
- ActiveGenie::Logger.call(
179
+ @config.logger.call(
180
180
  code: :retry_attempt,
181
181
  attempt: retries,
182
182
  max_retries: max_retries,
@@ -32,7 +32,7 @@ module ActiveGenie
32
32
  "Invalid response: #{response}"
33
33
  end
34
34
 
35
- ActiveGenie::Logger.call({ code: :function_calling, fine_tune: true, payload:, response: })
35
+ @config.logger.call({ code: :function_calling, fine_tune: true, payload:, response: })
36
36
 
37
37
  response
38
38
  end
@@ -44,7 +44,7 @@ module ActiveGenie
44
44
 
45
45
  return nil if response.nil?
46
46
 
47
- ActiveGenie::Logger.call(
47
+ @config.logger.call(
48
48
  {
49
49
  code: :llm_usage,
50
50
  input_tokens: response.dig('usage', 'prompt_tokens'),
@@ -34,6 +34,8 @@ module ActiveGenie
34
34
  json_string = response&.dig('candidates', 0, 'content', 'parts', 0, 'text')
35
35
  return nil if json_string.nil? || json_string.empty?
36
36
 
37
+ @config.logger.call({ code: :function_calling, fine_tune: true, payload:, parsed_response: json_string })
38
+
37
39
  normalize_response(json_string)
38
40
  end
39
41
 
@@ -48,7 +50,7 @@ module ActiveGenie
48
50
  def request(payload, params)
49
51
  response = post(url, payload, headers: DEFAULT_HEADERS, params:)
50
52
 
51
- ActiveGenie::Logger.call(
53
+ @config.logger.call(
52
54
  {
53
55
  code: :llm_usage,
54
56
  input_tokens: response['usageMetadata']['promptTokenCount'] || 0,
@@ -59,15 +61,6 @@ module ActiveGenie
59
61
  }
60
62
  )
61
63
 
62
- ActiveGenie::Logger.call(
63
- {
64
- code: :function_calling,
65
- fine_tune: true,
66
- payload:,
67
- parsed_response: response&.dig('candidates', 0, 'content', 'parts', 0, 'text')
68
- }
69
- )
70
-
71
64
  response
72
65
  end
73
66
 
@@ -29,7 +29,7 @@ module ActiveGenie
29
29
 
30
30
  raise InvalidResponseError, "Invalid response: #{response}" if response.nil? || response.keys.empty?
31
31
 
32
- ActiveGenie::Logger.call({ code: :function_calling, fine_tune: true, payload:, response: })
32
+ @config.logger.call({ code: :function_calling, fine_tune: true, payload:, response: })
33
33
 
34
34
  response
35
35
  end
@@ -41,7 +41,7 @@ module ActiveGenie
41
41
 
42
42
  return nil if response.nil?
43
43
 
44
- ActiveGenie::Logger.call(
44
+ @config.logger.call(
45
45
  {
46
46
  code: :llm_usage,
47
47
  input_tokens: response.dig('usage', 'prompt_tokens'),
@@ -0,0 +1,235 @@
1
+ # ActiveGenie Configuration
2
+
3
+ ActiveGenie provides a flexible configuration system that can be customized to suit your needs. This document details all available configuration options.
4
+
5
+ ## Initial Configuration
6
+
7
+ To configure ActiveGenie, use the `ActiveGenie.configure` block in your application's initialization:
8
+
9
+ ```ruby
10
+ ActiveGenie.configure do |config|
11
+ # Provider configurations
12
+ config.providers.openai.api_key = ENV['OPENAI_API_KEY']
13
+
14
+ # Log configurations
15
+ config.log.file_path = 'log/custom_genie.log'
16
+ config.log.fine_tune_file_path = 'log/fine_tune_genie.log'
17
+
18
+ # Other configurations can be added here
19
+ end
20
+ ```
21
+
22
+ ## Configuration Sections
23
+
24
+ ### 1. Log Configuration (`config.log`)
25
+
26
+ The log configuration controls how ActiveGenie handles logging of its operations.
27
+
28
+ #### Available Settings:
29
+
30
+ | Setting | Type | Default | Description |
31
+ |---------|------|---------|-------------|
32
+ | `file_path` | String | `'log/active_genie.log'` | Path to the main log file where all logs will be written. |
33
+ | `fine_tune_file_path` | String | `'log/active_genie_fine_tune.log'` | Path to the fine-tuning specific log file. |
34
+ | `output` | Proc | `->(log) { $stdout.puts log }` | Custom output handler for logs. Must respond to `call`. |
35
+ | `additional_context` | Hash | `{}` | Additional context to be added to each log entry. |
36
+
37
+ #### Methods:
38
+
39
+ - `add_observer(observers: [], scope: {}, &block)`
40
+ - Adds log observers that will be notified of log events.
41
+ - `observers`: An array of callable objects or a single callable object.
42
+ - `scope`: A hash to filter which logs the observer receives.
43
+ - `block`: A block that will be called for matching logs.
44
+
45
+ - `remove_observer(observers)`
46
+ - Removes the specified observers.
47
+
48
+ #### Example:
49
+
50
+ ```ruby
51
+ ActiveGenie.configure do |config|
52
+ config.log.file_path = 'log/my_app/genie.log'
53
+ config.log.fine_tune_file_path = 'log/my_app/fine_tune.log'
54
+
55
+ # Add a custom output handler
56
+ config.log.output = ->(log) { MyLogger.info(log) }
57
+
58
+ # Add an observer for specific log events
59
+ config.log.add_observer(scope: { event: :api_call }) do |log|
60
+ StatsD.increment("genie.api_calls")
61
+ end
62
+ end
63
+ ```
64
+
65
+ ### 2. LLM Configuration (`config.llm`)
66
+
67
+ The LLM configuration is used to define settings for interacting with Large Language Models.
68
+
69
+ #### Available Settings:
70
+
71
+ | Setting | Type | Default | Description |
72
+ |-----------------|---------------------|------------------------------|------------------------------------------------------------------------------------------------------------|
73
+ | `model` | String / nil | `nil` | The specific LLM model to use (e.g., 'gpt-4', 'claude-2'). |
74
+ | `provider` | Symbol / nil | `nil` | The LLM provider (e.g., `:openai`, `:anthropic`). Set as a string, stored as a symbol. |
75
+ | `client` | Object / nil | `nil` | A pre-configured client instance for the LLM provider. If set, other settings might be ignored. |
76
+ | `temperature` | Numeric | `0` | Controls randomness. Higher values (e.g., 0.8) = more random, lower (e.g., 0.2) = more deterministic. |
77
+ | `max_tokens` | Integer | `4096` | Maximum number of tokens to generate in the LLM response. |
78
+ | `max_retries` | Integer / nil | `nil` | Maximum number of times to retry a failed API call to the LLM. |
79
+ | `retry_delay` | Numeric / nil | `nil` | Delay (in seconds) between retries for failed API calls. |
80
+ | `model_tier` | Enum [lower_tier, middle_tier, higher_tier] | `'lower_tier'` | Specifies the model tier, potentially affecting cost/performance. Will be used if `model` is not set. |
81
+ | `read_timeout` | Numeric / nil | `nil` | Timeout (in seconds) for reading data from the LLM API. |
82
+ | `open_timeout` | Numeric / nil | `nil` | Timeout (in seconds) for establishing a connection to the LLM API. |
83
+
84
+ #### Example:
85
+
86
+ ```ruby
87
+ ActiveGenie.configure do |config|
88
+ config.llm.provider = :openai
89
+ config.llm.model = 'gpt-999'
90
+ config.llm.temperature = 0.1
91
+ config.llm.max_tokens = 8000
92
+ config.llm.max_retries = 1
93
+ config.llm.retry_delay = 5 # seconds
94
+ config.llm.model_tier = 'higher_tier' # If model is not set, will use the model tier to select a model
95
+ end
96
+ ```
97
+
98
+ ### 4. Providers Configuration (`config.providers`)
99
+
100
+ The Providers configuration (`config.providers`) manages settings for various Large Language Model (LLM) providers. It allows you to configure multiple providers, set a default, and access individual provider settings. Active Genie automatically loads configurations for supported providers (OpenAI, Anthropic, DeepSeek, Google).
101
+
102
+ #### Main Provider Settings:
103
+
104
+ | Setting | Type | Default | Description |
105
+ |-----------|---------------------|--------------------------------|-----------------------------------------------------------------------------|
106
+ | `default` | String / Symbol | First validly configured provider (often `:openai` if its API key is set) | The name of the default LLM provider to use (e.g., `:openai`, `:anthropic`). You can set this to your preferred provider. |
107
+
108
+ #### Methods for `config.providers`:
109
+
110
+ - `add(provider_classes)`
111
+ - Adds one or more custom provider configuration classes. `provider_classes` can be a single class or an array of classes. (Typically not needed for built-in providers).
112
+ - `remove(provider_classes)`
113
+ - Removes one or more provider configurations based on their classes.
114
+
115
+ #### Example for Main Provider Settings:
116
+
117
+ ```ruby
118
+ ActiveGenie.configure do |config|
119
+ # Set the default provider to use if not specifying one explicitly in operations
120
+ config.providers.default = :anthropic
121
+
122
+ # Configure individual providers (API keys are often set via ENV variables)
123
+ config.providers.openai.api_key = ENV['OPENAI_API_KEY']
124
+ config.providers.anthropic.api_key = ENV['ANTHROPIC_API_KEY']
125
+ # config.providers.deepseek.api_key = ENV['DEEPSEEK_API_KEY']
126
+ # config.providers.google.api_key = ENV['GEMINI_API_KEY']
127
+ end
128
+ ```
129
+
130
+ #### Individual Provider Configurations:
131
+
132
+ The following subsections detail the configurations for each supported LLM provider. Each provider configuration (e.g., `config.providers.openai`) allows setting an `api_key`, `api_url`, and specific model names for `lower_tier_model`, `middle_tier_model`, and `higher_tier_model`.
133
+
134
+ ##### a. OpenAI (`config.providers.openai`)
135
+
136
+ Internal Name: `:openai`
137
+
138
+ | Setting | Type | Default | Description |
139
+ |---------------------|--------|---------------------------------------|-----------------------------------------------------------------------------|
140
+ | `api_key` | String | `ENV['OPENAI_API_KEY']` | Your OpenAI API key. |
141
+ | `api_url` | String | `'https://api.openai.com/v1'` | Base URL for the OpenAI API. |
142
+ | `lower_tier_model` | String | `'gpt-4.1-mini'` | Model for lower-tier usage (cost-effective, faster). |
143
+ | `middle_tier_model` | String | `'gpt-4.1'` | Model for middle-tier usage (balanced performance). |
144
+ | `higher_tier_model` | String | `'o3-mini'` | Model for higher-tier usage (most capable). |
145
+
146
+ ##### b. Anthropic (`config.providers.anthropic`)
147
+
148
+ Internal Name: `:anthropic`
149
+
150
+ | Setting | Type | Default | Description |
151
+ |---------------------|--------|------------------------------------------|-----------------------------------------------------------------------------|
152
+ | `api_key` | String | `ENV['ANTHROPIC_API_KEY']` | Your Anthropic API key. |
153
+ | `api_url` | String | `'https://api.anthropic.com'` | Base URL for the Anthropic API. |
154
+ | `anthropic_version` | String | `'2023-06-01'` | The API version for Anthropic. |
155
+ | `lower_tier_model` | String | `'claude-3-5-haiku-20241022'` | Model for lower-tier usage. |
156
+ | `middle_tier_model` | String | `'claude-3-7-sonnet-20250219'` | Model for middle-tier usage. |
157
+ | `higher_tier_model` | String | `'claude-3-opus-20240229'` | Model for higher-tier usage. |
158
+
159
+ ##### c. DeepSeek (`config.providers.deepseek`)
160
+
161
+ Internal Name: `:deepseek`
162
+
163
+ | Setting | Type | Default | Description |
164
+ |---------------------|--------|---------------------------------------|-----------------------------------------------------------------------------|
165
+ | `api_key` | String | `ENV['DEEPSEEK_API_KEY']` | Your DeepSeek API key. |
166
+ | `api_url` | String | `'https://api.deepseek.com/v1'` | Base URL for the DeepSeek API. |
167
+ | `lower_tier_model` | String | `'deepseek-chat'` | Model for lower-tier usage. |
168
+ | `middle_tier_model` | String | `'deepseek-chat'` | Model for middle-tier usage. |
169
+ | `higher_tier_model` | String | `'deepseek-reasoner'` | Model for higher-tier usage. |
170
+
171
+ ##### d. Google (`config.providers.google`)
172
+
173
+ Internal Name: `:google`
174
+
175
+ | Setting | Type | Default | Description |
176
+ |---------------------|--------|---------------------------------------------------------------|-----------------------------------------------------------------------------|
177
+ | `api_key` | String | `ENV['GENERATIVE_LANGUAGE_GOOGLE_API_KEY']` or `ENV['GEMINI_API_KEY']` | Your Google API key for Gemini. |
178
+ | `api_url` | String | `'https://generativelanguage.googleapis.com'` | Base URL for the Google Generative Language API. |
179
+ | `lower_tier_model` | String | `'gemini-2.0-flash-lite'` | Model for lower-tier usage. |
180
+ | `middle_tier_model` | String | `'gemini-2.0-flash'` | Model for middle-tier usage. |
181
+ | `higher_tier_model` | String | `'gemini-2.5-pro-experimental'` | Model for higher-tier usage. |
182
+
183
+ #### Example for Overriding Individual Provider Settings:
184
+
185
+ ```ruby
186
+ ActiveGenie.configure do |config|
187
+ config.providers.openai.api_key = 'sk-yourOpenAiKey...'
188
+ config.providers.openai.middle_tier_model = 'gpt-4o' # Override default middle tier for OpenAI
189
+
190
+ config.providers.anthropic.api_key = 'sk-ant-yourAnthropicKey...'
191
+ config.providers.anthropic.anthropic_version = '2024-02-15' # Override Anthropic API version
192
+
193
+ config.providers.google.higher_tier_model = 'gemini-1.5-pro-latest' # Use a specific Google model
194
+ end
195
+ ```
196
+
197
+ ### 5. Ranking Configuration (`config.ranking`)
198
+
199
+ The Ranking configuration (`config.ranking`) deals with settings related to how results or items are ranked.
200
+
201
+ #### Available Settings:
202
+
203
+ | Setting | Type | Default | Description |
204
+ |-----------------------------|---------|---------|------------------------------------------------------------------------------------------------------------|
205
+ | `score_variation_threshold` | Integer | `30` | A threshold (percentage) used to determine significant variations in scores when ranking or comparing items. |
206
+
207
+ #### Example:
208
+
209
+ ```ruby
210
+ ActiveGenie.configure do |config|
211
+ config.ranking.score_variation_threshold = 25 # Set to 25%
212
+ end
213
+ ```
214
+
215
+ ### 6. Data Extractor Configuration (`config.data_extractor`)
216
+
217
+ The Data Extractor configuration (`config.data_extractor`) provides settings for how data is extracted and processed, likely from text or other sources using LLMs.
218
+
219
+ #### Available Settings:
220
+
221
+ | Setting | Type | Default | Description |
222
+ |--------------------|---------|---------|---------------------------------------------------------------------------------------------------------|
223
+ | `with_explanation` | Boolean | `true` | Whether the data extraction process should also attempt to provide an explanation for the extracted data. |
224
+ | `min_accuracy` | Integer | `70` | The minimum accuracy (percentage) required for extracted data to be considered valid or useful. |
225
+ | `verbose` | Boolean | `false` | If true, enables more detailed logging or output from the data extraction process. |
226
+
227
+ #### Example:
228
+
229
+ ```ruby
230
+ ActiveGenie.configure do |config|
231
+ config.data_extractor.with_explanation = false
232
+ config.data_extractor.min_accuracy = 85
233
+ config.data_extractor.verbose = true
234
+ end
235
+ ```
@@ -4,6 +4,7 @@ module ActiveGenie
4
4
  module Config
5
5
  class LogConfig
6
6
  attr_writer :file_path, :fine_tune_file_path
7
+ attr_reader :output, :observers
7
8
 
8
9
  def file_path
9
10
  @file_path || 'log/active_genie.log'
@@ -13,8 +14,12 @@ module ActiveGenie
13
14
  @fine_tune_file_path || 'log/active_genie_fine_tune.log'
14
15
  end
15
16
 
16
- def output
17
- @output || ->(log) { $stdout.puts log }
17
+ def additional_context
18
+ @additional_context || {}
19
+ end
20
+
21
+ def additional_context=(context)
22
+ @additional_context = additional_context.merge(context).compact
18
23
  end
19
24
 
20
25
  def output=(output)
@@ -23,29 +28,17 @@ module ActiveGenie
23
28
  @output = output
24
29
  end
25
30
 
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
-
38
31
  def add_observer(observers: [], scope: {}, &block)
39
32
  @observers ||= []
40
33
 
41
34
  raise ArgumentError, 'Scope must be a hash' if scope && !scope.is_a?(Hash)
42
35
 
43
- @observers << { observer: block, scope: } if block_given?
44
36
  Array(observers).each do |observer|
45
37
  next unless observer.respond_to?(:call)
46
38
 
47
39
  @observers << { observer:, scope: }
48
40
  end
41
+ @observers << { observer: block, scope: } if block_given?
49
42
  end
50
43
 
51
44
  def remove_observer(observers)
@@ -60,6 +53,10 @@ module ActiveGenie
60
53
 
61
54
  def merge(config_params = {})
62
55
  dup.tap do |config|
56
+ config_params.compact.each do |key, value|
57
+ config.send("#{key}=", value) if config.respond_to?("#{key}=")
58
+ end
59
+
63
60
  config.add_observer(config_params[:observers]) if config_params[:observers]
64
61
  end
65
62
  end
@@ -48,8 +48,8 @@ module ActiveGenie
48
48
  # Retrieves the model name designated for the upper tier (e.g., most capable).
49
49
  # Defaults to 'claude-3-opus'.
50
50
  # @return [String] The upper tier model name.
51
- def upper_tier_model
52
- @upper_tier_model || 'claude-3-opus-20240229'
51
+ def higher_tier_model
52
+ @higher_tier_model || 'claude-3-opus-20240229'
53
53
  end
54
54
  end
55
55
  end
@@ -41,8 +41,8 @@ module ActiveGenie
41
41
  # Retrieves the model name designated for the upper tier (e.g., most capable).
42
42
  # Defaults to 'deepseek-reasoner'.
43
43
  # @return [String] The upper tier model name.
44
- def upper_tier_model
45
- @upper_tier_model || 'deepseek-reasoner'
44
+ def higher_tier_model
45
+ @higher_tier_model || 'deepseek-reasoner'
46
46
  end
47
47
  end
48
48
  end
@@ -43,8 +43,8 @@ module ActiveGenie
43
43
  # Retrieves the model name designated for the upper tier (e.g., most capable).
44
44
  # Defaults to 'gemini-2.5-pro-experimental'.
45
45
  # @return [String] The upper tier model name.
46
- def upper_tier_model
47
- @upper_tier_model || 'gemini-2.5-pro-experimental'
46
+ def higher_tier_model
47
+ @higher_tier_model || 'gemini-2.5-pro-experimental'
48
48
  end
49
49
  end
50
50
  end
@@ -41,8 +41,8 @@ module ActiveGenie
41
41
  # Retrieves the model name designated for the upper tier (e.g., most capable).
42
42
  # Defaults to 'o1-preview'.
43
43
  # @return [String] The upper tier model name.
44
- def upper_tier_model
45
- @upper_tier_model || 'o3-mini'
44
+ def higher_tier_model
45
+ @higher_tier_model || 'o3-mini'
46
46
  end
47
47
  end
48
48
  end
@@ -7,7 +7,7 @@ module ActiveGenie
7
7
  NAME = :unknown
8
8
 
9
9
  attr_writer :api_key, :organization, :api_url, :client,
10
- :lower_tier_model, :middle_tier_model, :upper_tier_model
10
+ :lower_tier_model, :middle_tier_model, :higher_tier_model
11
11
 
12
12
  # Maps a symbolic tier (:lower_tier, :middle_tier, :upper_tier) to a specific model name.
13
13
  # Falls back to the lower_tier_model if the tier is nil or unrecognized.
@@ -17,7 +17,7 @@ module ActiveGenie
17
17
  {
18
18
  lower_tier: lower_tier_model,
19
19
  middle_tier: middle_tier_model,
20
- upper_tier: upper_tier_model
20
+ upper_tier: higher_tier_model
21
21
  }[tier&.to_sym] || lower_tier_model
22
22
  end
23
23
 
@@ -31,7 +31,7 @@ module ActiveGenie
31
31
  api_url:,
32
32
  lower_tier_model:,
33
33
  middle_tier_model:,
34
- upper_tier_model:,
34
+ higher_tier_model:,
35
35
  **config
36
36
  }
37
37
  end
@@ -80,7 +80,7 @@ module ActiveGenie
80
80
  # Retrieves the model name designated for the upper tier (e.g., most capable).
81
81
  # Defaults to 'o1-preview'.
82
82
  # @return [String] The upper tier model name.
83
- def upper_tier_model
83
+ def higher_tier_model
84
84
  raise NotImplementedError, 'Subclasses must implement this method'
85
85
  end
86
86
  end
@@ -38,6 +38,10 @@ module ActiveGenie
38
38
  @llm ||= Config::LlmConfig.new
39
39
  end
40
40
 
41
+ def logger
42
+ @logger ||= ActiveGenie::Logger.new(log_config: log)
43
+ end
44
+
41
45
  SUB_CONFIGS = %w[log providers llm ranking scoring data_extractor battle].freeze
42
46
 
43
47
  def merge(config_params = {})
@@ -54,20 +58,28 @@ module ActiveGenie
54
58
 
55
59
  new_configuration.send("#{key}=", new_config)
56
60
  end
61
+ @logger = nil
57
62
 
58
63
  new_configuration
59
64
  end
60
65
 
66
+ # Merges a sub config with the config_params.
67
+ # Examples:
68
+ # config.merge({ 'log' => { file_path: 'log/active_genie.log' } })
69
+ # config.merge({ log: { file_path: 'log/active_genie.log' } })
70
+ # config.merge({ file_path: 'log/active_genie.log' })
71
+ # these are all the same
72
+
61
73
  def sub_config_merge(config, key, config_params)
62
- if config_params.key?(key.to_s)
74
+ if config_params&.key?(key.to_s)
63
75
  config.merge(config_params[key.to_s])
64
- elsif config_params.key?(key.to_sym)
76
+ elsif config_params&.key?(key.to_sym)
65
77
  config.merge(config_params[key.to_sym])
66
78
  else
67
- config.merge(config_params)
79
+ config.merge(config_params || {})
68
80
  end
69
81
  end
70
82
 
71
- attr_writer :log, :providers, :ranking, :scoring, :data_extractor, :battle, :llm
83
+ attr_writer :log, :providers, :ranking, :scoring, :data_extractor, :battle, :llm, :logger
72
84
  end
73
85
  end
@@ -87,7 +87,7 @@ module ActiveGenie
87
87
  config: @config
88
88
  )
89
89
 
90
- ActiveGenie::Logger.call(
90
+ @config.logger.call(
91
91
  {
92
92
  code: :data_extractor,
93
93
  text: @text[0..30],