active_genie 0.26.5 → 0.27.1

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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -193
  3. data/VERSION +1 -1
  4. data/lib/active_genie/battle/generalist.rb +9 -9
  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/log_config.rb +12 -15
  11. data/lib/active_genie/config/providers/anthropic_config.rb +2 -2
  12. data/lib/active_genie/config/providers/deepseek_config.rb +2 -2
  13. data/lib/active_genie/config/providers/google_config.rb +2 -2
  14. data/lib/active_genie/config/providers/openai_config.rb +2 -2
  15. data/lib/active_genie/config/providers/provider_base.rb +4 -4
  16. data/lib/active_genie/configuration.rb +16 -4
  17. data/lib/active_genie/data_extractor/generalist.rb +2 -2
  18. data/lib/active_genie/logger.rb +40 -22
  19. data/lib/active_genie/ranking/elo_round.rb +30 -37
  20. data/lib/active_genie/ranking/free_for_all.rb +29 -28
  21. data/lib/active_genie/ranking/player.rb +1 -9
  22. data/lib/active_genie/ranking/ranking.rb +32 -25
  23. data/lib/active_genie/ranking/ranking_scoring.rb +12 -17
  24. data/lib/active_genie/scoring/generalist.rb +9 -9
  25. data/lib/tasks/templates/active_genie.rb +1 -1
  26. metadata +8 -13
  27. data/lib/active_genie/data_extractor/README.md +0 -131
  28. data/lib/active_genie/ranking/README.md +0 -73
  29. data/lib/active_genie/ranking/concerns/loggable.rb +0 -29
  30. data/lib/active_genie/scoring/README.md +0 -76
  31. /data/lib/active_genie/battle/{generalist.md → generalist.prompt.md} +0 -0
  32. /data/lib/active_genie/data_extractor/{generalist.md → generalist.prompt.md} +0 -0
  33. /data/lib/active_genie/scoring/{generalist.md → generalist.prompt.md} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ad223ae84ac62d9375af07ba18dca9152296cac5e7c432a6fd07118107c4ab6e
4
- data.tar.gz: 5faa2d7ad89cf2200296f94892470248a544d86cd37880191777226f121d9cf4
3
+ metadata.gz: 752b254545d62f5e618ca56935dca6e89dce29e0353af995f96f276ac2852662
4
+ data.tar.gz: 7df0c3373970c5da1cf12cea004839a17e8489bf52b2e3d14dedcd61c04f91cc
5
5
  SHA512:
6
- metadata.gz: 2a6bd702967d5948e37fed48a9b92d0c2061b4893579e67d526cb3bf9b71b44889b7a5a457d5c78a768f74d6cc650beb17a52c1ce00bf1a5c7d2dc553d88b866
7
- data.tar.gz: 845fecdb4f036ce4a97b99ea54d0f5600544d6ce0b052da99e4533e4555d774ce09bff1dc037d3752f30aabe69acc5b9362ab77eaf17c62b8ebb5244e02b5df8
6
+ metadata.gz: dd4d86d0bd8a8be3c44355d997883db01b424a070ab51b555e1f1b8a10c3c0fbc46f8afc4c5b8764b4462b9e1e5e711be38ad25a77a21e26b3ea55668fd61ccf
7
+ data.tar.gz: bb69781f7f05a339833e553ab7cb1dc805044cda7fb2615712c7fea3f5ce9f71dce82b38edd1b70aa6fca36d6ef6538c573b34058e80498a0a5a65bf6fd58821
data/README.md CHANGED
@@ -1,13 +1,16 @@
1
1
  # ActiveGenie 🧞‍♂️
2
- > The lodash for GenAI, stop reinventing the wheel
2
+ > The Lodash for GenAI: Real Value + Consistent + Model-Agnostic
3
3
 
4
4
  [![Gem Version](https://badge.fury.io/rb/active_genie.svg?icon=si%3Arubygems)](https://badge.fury.io/rb/active_genie)
5
5
  [![Ruby](https://github.com/roriz/active_genie/actions/workflows/benchmark.yml/badge.svg)](https://github.com/roriz/active_genie/actions/workflows/benchmark.yml)
6
6
 
7
- ActiveGenie is a Ruby gem that provides valuable solutions powered by Generative AI (GenAI) models. Just like Lodash or ActiveStorage, ActiveGenie brings a set of Modules to reach real value fast and reliably.
8
- ActiveGenie is backed by a custom benchmarking system that ensures consistent quality and performance across different models and providers in every release.
7
+ ActiveGenie is a developer-first library for GenAI workflows, designed to help you compare, rank, and score LLM outputs with consistency and model-agnostic flexibility. Think of it as the Lodash for GenAI: built for real value, consistent results, and freedom from vendor lock-in. It solves the biggest pain in GenAI today: getting predictable, trustworthy answers across use cases, models, and providers.
9
8
 
10
- ## Installation
9
+ Behind the scenes, a custom benchmarking system keeps everything consistent across LLM vendors and versions, release after release.
10
+
11
+ For full documentation, visit [activegenie.ai](https://activegenie.ai).
12
+
13
+ # Installation
11
14
 
12
15
  1. Add to your Gemfile:
13
16
  ```ruby
@@ -32,9 +35,7 @@ ActiveGenie.configure do |config|
32
35
  end
33
36
  ```
34
37
 
35
- ## Quick Start
36
-
37
- ### Data Extractor
38
+ ## Quick start example
38
39
 
39
40
  Extract structured data from text using AI-powered analysis, handling informal language and complex expressions.
40
41
 
@@ -71,193 +72,9 @@ result = ActiveGenie::DataExtractor.call(
71
72
  # }
72
73
  ```
73
74
 
74
- *Recommended model*: `gpt-4o-mini`
75
-
76
- Features:
77
- - Structured data extraction with type validation
78
- - Schema-based extraction with custom constraints
79
- - Informal text analysis (litotes, hedging)
80
- - Detailed explanations for extracted values
81
-
82
- See the [Data Extractor README](lib/active_genie/data_extractor/README.md) for informal text processing, advanced schemas, and detailed interface documentation.
83
-
84
- ### Scoring
85
- Text evaluation system that provides detailed scoring and feedback using multiple expert reviewers. Get balanced scoring through AI-powered expert reviewers that automatically adapt to your content.
86
-
87
- ```ruby
88
- text = "The code implements a binary search algorithm with O(log n) complexity"
89
- criteria = "Evaluate technical accuracy and clarity"
90
-
91
- result = ActiveGenie::Scoring.call(
92
- text,
93
- criteria,
94
- config: { provider: :anthropic, model: 'claude-3-5-haiku-20241022' } # optional
95
- )
96
- # => {
97
- # algorithm_expert_score: 95,
98
- # algorithm_expert_reasoning: "Accurately describes binary search and its complexity",
99
- # technical_writer_score: 90,
100
- # technical_writer_reasoning: "Clear and concise explanation of the algorithm",
101
- # final_score: 92.5
102
- # }
103
- ```
104
-
105
- *Recommended model*: `claude-3-5-haiku-20241022`
106
-
107
- Features:
108
- - Multi-reviewer evaluation with automatic expert selection
109
- - Detailed feedback with scoring reasoning
110
- - Customizable reviewer weights
111
- - Flexible evaluation criteria
112
-
113
- See the [Scoring README](lib/active_genie/scoring/README.md) for advanced usage, custom reviewers, and detailed interface documentation.
114
-
115
- ### Battle
116
- AI-powered battle evaluation system that determines winners between two players based on specified criteria.
117
-
118
- ```ruby
119
- require 'active_genie'
120
-
121
- player_a = "Implementation uses dependency injection for better testability"
122
- player_b = "Code has high test coverage but tightly coupled components"
123
- criteria = "Evaluate code quality and maintainability"
124
-
125
- result = ActiveGenie::Battle.call(
126
- player_a,
127
- player_b,
128
- criteria,
129
- config: { provider: :google, model: 'gemini-2.0-flash-lite' } # optional
130
- )
131
- # => {
132
- # winner_player: "Implementation uses dependency injection for better testability",
133
- # reasoning: "Player 1 implementation demonstrates better maintainability through dependency injection,
134
- # which allows for easier testing and component replacement. While Player 2 has good test coverage,
135
- # the tight coupling makes the code harder to maintain and modify.",
136
- # what_could_be_changed_to_avoid_draw: "Focus on specific architectural patterns and design principles"
137
- # }
138
- ```
139
-
140
- *Recommended model*: `claude-3-5-haiku`
141
-
142
- Features:
143
- - Multi-reviewer evaluation with automatic expert selection
144
- - Detailed feedback with scoring reasoning
145
- - Customizable reviewer weights
146
- - Flexible evaluation criteria
147
-
148
- See the [Battle README](lib/active_genie/battle/README.md) for advanced usage, custom reviewers, and detailed interface documentation.
149
-
150
- ### Ranking
151
- The Ranking module provides competitive ranking through multi-stage evaluation:
152
-
153
- ```ruby
154
- require 'active_genie'
155
-
156
- players = ['REST API', 'GraphQL API', 'SOAP API', 'gRPC API', 'Websocket API']
157
- criteria = "Best one to be used into a high changing environment"
158
-
159
- result = ActiveGenie::Ranking.call(
160
- players,
161
- criteria,
162
- config: { provider: :google, model: 'gemini-2.0-flash-lite' } # optional
163
- )
164
- # => {
165
- # winner_player: "gRPC API",
166
- # reasoning: "gRPC API is the best one to be used into a high changing environment",
167
- # }
168
- ```
169
-
170
- *Recommended model*: `gemini-2.0-flash-lite`
171
-
172
- - **Multi-phase ranking system** combining expert scoring and ELO algorithms
173
- - **Automatic elimination** of inconsistent performers using statistical analysis
174
- - **Dynamic ranking adjustments** based on simulated pairwise battles, from bottom to top
175
-
176
- See the [Ranking README](lib/active_genie/ranking/README.md) for implementation details, configuration, and advanced ranking strategies.
177
-
178
- ### Text Summarizer (Future)
179
- ### Categorizer (Future)
180
- ### Language detector (Future)
181
- ### Translator (Future)
182
- ### Sentiment analyzer (Future)
183
-
184
- ## Benchmarking 🧪
185
-
186
- ActiveGenie includes a comprehensive benchmarking system to ensure consistent, high-quality outputs across different LLM models and providers.
187
-
188
- ```ruby
189
- # Run all benchmarks
190
- bundle exec rake active_genie:benchmark
191
-
192
- # Run benchmarks for a specific module
193
- bundle exec rake active_genie:benchmark[data_extractor]
194
- ```
195
-
196
- ### Latest Results
197
-
198
- | Model | Overall Precision |
199
- |-------|-------------------|
200
- | claude-3-5-haiku-20241022 | 92.25% |
201
- | gemini-2.0-flash-lite | 84.25% |
202
- | gpt-4o-mini | 62.75% |
203
- | deepseek-chat | 57.25% |
204
-
205
- See the [Benchmark README](benchmark/README.md) for detailed results, methodology, and how to contribute to our test suite.
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` |
75
+ ## Documentation
216
76
 
217
- > **Note:** Each module can append its own set of configuration, see the individual module documentation for details.
218
-
219
- ## How to create a new provider
220
-
221
- ActiveGenie supports adding custom providers to integrate with different LLM services. To create a new provider:
222
-
223
- 1. Create a configuration class for your provider in `lib/active_genie/configuration/providers/`:
224
- 2. Register your client
225
-
226
- ```ruby
227
- class InternalCompanyApi
228
- # @param messages [Array<Hash>] A list of messages representing the conversation history.
229
- # Each hash should have :role ('user', 'assistant', or 'system') and :content (String).
230
- # @param function [Hash] A JSON schema definition describing the desired output format.
231
- # @return [Hash, nil] The parsed JSON object matching the schema, or nil if parsing fails or content is empty.
232
- def function_calling(messages, function)
233
- # ...
234
- end
235
- end
236
-
237
- ActiveGenie.configure do |config|
238
- config.llm.client = InternalCompanyApi
239
- end
240
- # or
241
- ActiveGenie::Battle.call('player_a', 'player_b', 'criteria', { client: InternalCompanyApi })
242
- ```
243
-
244
- ## Observability
245
- Fundamental to managing any production system, observability is crucial for GenAI features. At a minimum, track these key metrics:
246
-
247
- - Usage Rate (e.g., uses_per_minute): Detect anomalies like sudden traffic spikes (potential DDoS) or drops (feature outage or declining usage).
248
- - Failure/Retry Rate (e.g., retry_count, fail_count): Monitor the frequency of errors. Exceeding a defined threshold should trigger downtime or degradation alerts.
249
- - Token Consumption (e.g., tokens_used): Track usage to monitor costs. Set alerts if tokens_used * price_per_token exceeds budget thresholds.
250
-
251
- ```ruby
252
- ActiveGenie.configure do |config|
253
- config.log.add_observer(scope: { code: :llm_usage }) do |log|
254
- puts "LLM Usage: #{log[:model]} - #{log[:total_tokens]} tokens"
255
- end
256
- config.log.add_observer(scope: { code: :retry_attempt }) do |log|
257
- puts "Retry Attempt: #{log[:attempt]} of #{log[:max_retries]}"
258
- end
259
- end
260
- ```
77
+ For full documentation, visit [activegenie.ai](https://activegenie.ai).
261
78
 
262
79
  ## Contributing
263
80
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.26.5
1
+ 0.27.1
@@ -48,19 +48,19 @@ 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
62
62
 
63
- PROMPT = File.read(File.join(__dir__, 'generalist.md'))
63
+ PROMPT = File.read(File.join(__dir__, 'generalist.prompt.md'))
64
64
  FUNCTION = JSON.parse(File.read(File.join(__dir__, 'generalist.json')), symbolize_names: true)
65
65
 
66
66
  private
@@ -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'),
@@ -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],
@@ -119,7 +119,7 @@ module ActiveGenie
119
119
  end
120
120
 
121
121
  def prompt
122
- File.read(File.join(__dir__, 'generalist.md'))
122
+ File.read(File.join(__dir__, 'generalist.prompt.md'))
123
123
  end
124
124
  end
125
125
  end