active_genie 0.0.8 → 0.0.10

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +34 -33
  3. data/VERSION +1 -1
  4. data/lib/active_genie/battle/README.md +2 -2
  5. data/lib/active_genie/battle/basic.rb +24 -19
  6. data/lib/active_genie/battle.rb +1 -1
  7. data/lib/active_genie/clients/openai_client.rb +77 -0
  8. data/lib/active_genie/clients/unified_client.rb +19 -0
  9. data/lib/active_genie/configuration/log_config.rb +14 -0
  10. data/lib/active_genie/configuration/openai_config.rb +56 -0
  11. data/lib/active_genie/configuration/providers_config.rb +37 -0
  12. data/lib/active_genie/configuration.rb +18 -23
  13. data/lib/active_genie/data_extractor/README.md +4 -4
  14. data/lib/active_genie/data_extractor/basic.rb +19 -9
  15. data/lib/active_genie/data_extractor/from_informal.rb +18 -7
  16. data/lib/active_genie/data_extractor.rb +1 -1
  17. data/lib/active_genie/league/README.md +43 -0
  18. data/lib/active_genie/{leaderboard → league}/elo_ranking.rb +41 -8
  19. data/lib/active_genie/league/free_for_all.rb +62 -0
  20. data/lib/active_genie/league/league.rb +120 -0
  21. data/lib/active_genie/{leaderboard → league}/player.rb +17 -10
  22. data/lib/active_genie/league.rb +12 -0
  23. data/lib/active_genie/logger.rb +45 -0
  24. data/lib/active_genie/scoring/README.md +4 -8
  25. data/lib/active_genie/scoring/basic.rb +19 -10
  26. data/lib/active_genie/scoring/recommended_reviews.rb +7 -9
  27. data/lib/active_genie/scoring.rb +1 -1
  28. data/lib/active_genie.rb +9 -17
  29. data/lib/tasks/install.rake +3 -3
  30. data/lib/tasks/templates/active_genie.rb +17 -0
  31. metadata +85 -80
  32. data/lib/active_genie/clients/openai.rb +0 -61
  33. data/lib/active_genie/clients/router.rb +0 -41
  34. data/lib/active_genie/leaderboard/leaderboard.rb +0 -72
  35. data/lib/active_genie/leaderboard/league.rb +0 -48
  36. data/lib/active_genie/leaderboard.rb +0 -11
  37. data/lib/active_genie/utils/math.rb +0 -15
  38. data/lib/tasks/templates/active_genie.yml +0 -7
  39. /data/lib/active_genie/{leaderboard → league}/players_collection.rb +0 -0
@@ -0,0 +1,43 @@
1
+ # league
2
+
3
+ The `ActiveGenie::League` module organizes players based on scores derived from textual evaluations and then ranks them using a multi-stage process. It leverages the scoring system from the `ActiveGenie::Scoring` module to assign initial scores, and then applies advanced ranking methods to produce a competitive league.
4
+
5
+ ## Overview
6
+
7
+ The league system performs the following steps:
8
+
9
+ 1. **Initial Scoring**: Each player’s textual content is evaluated using `ActiveGenie::Scoring`. This produces a `score` based on multiple expert reviews.
10
+
11
+ 2. **Elimination of Poor Performers**: Players whose scores show a high coefficient of variation (indicating inconsistency) are progressively eliminated. This ensures that only competitive candidates continue in the ranking process.
12
+
13
+ 3. **ELO Ranking**: If there are more than 10 eligible players, an ELO-based ranking is applied. Battles between players are simulated via `ActiveGenie::Battle`, and scores are updated using an ELO algorithm tailored to the league.
14
+
15
+ 4. **Free for all Matches**: Finally, the remaining players engage in head-to-head matches where each unique pair competes. Match outcomes (wins, losses, draws) are recorded to finalize the rankings.
16
+
17
+ ## Components
18
+
19
+ - **league**: Orchestrates the entire ranking process. Initializes player scores, eliminates outliers, and coordinates ranking rounds.
20
+
21
+ - **EloRanking**: Applies an ELO-based system to rank players through simulated battles. It updates players’ ELO scores based on match outcomes and predefined rules (including penalties for losses).
22
+
23
+ - **Free for all**: Conducts complete pairwise matches among eligible players to record win/loss/draw statistics and refine the final standings.
24
+
25
+ ## Usage
26
+
27
+ Call the league using:
28
+
29
+ ```ruby
30
+ result = ActiveGenie::League::league.call(players, criteria, config: {})
31
+ ```
32
+
33
+ - `players`: A collection of player instances, each containing textual content to be scored.
34
+ - `criteria`: A string defining the evaluation criteria used by the scoring system.
35
+ - `config`: A hash of additional parameters for customization (e.g., model, api_key).
36
+
37
+ The method processes the players through scoring, elimination, and ranking phases, then returns a hash containing the player statistics and rankings.
38
+
39
+ ## Possible improvements
40
+ - Adjust initial criteria to ensure consistency
41
+ - Adjust each player's content to ensure consistency
42
+ - Support players with images or audio
43
+ - Parallelize processing battles and scoring
@@ -1,32 +1,36 @@
1
1
  require_relative '../battle/basic'
2
- require_relative '../utils/math'
3
2
 
4
3
  module ActiveGenie::Leaderboard
5
4
  class EloRanking
6
- def self.call(players, criteria, options: {})
7
- new(players, criteria, options:).call
5
+ def self.call(players, criteria, config: {})
6
+ new(players, criteria, config:).call
8
7
  end
9
8
 
10
- def initialize(players, criteria, options: {})
9
+ def initialize(players, criteria, config: {})
11
10
  @players = players
12
11
  @criteria = criteria
13
- @options = options
12
+ @config = config
13
+ @start_time = Time.now
14
14
  end
15
15
 
16
16
  def call
17
17
  @players.each(&:generate_elo_by_score)
18
18
 
19
+ round_count = 0
19
20
  while @players.eligible_size > MINIMAL_PLAYERS_TO_BATTLE
20
21
  round = create_round(@players.tier_relegation, @players.tier_defense)
21
22
 
22
23
  round.each do |player_a, player_b|
23
24
  winner, loser = battle(player_a, player_b) # This can take a while, can be parallelized
24
25
  update_elo(winner, loser)
26
+ ActiveGenie::Logger.trace({ **log, step: :elo_battle, winner_id: winner.id, loser_id: loser.id, winner_elo: winner.elo, loser_elo: loser.elo })
25
27
  end
26
28
 
27
- @players.tier_relegation.each { |player| player.eliminated = "relegation/#{@players.eligible_size}" }
29
+ eliminate_all_relegation_players
30
+ round_count += 1
28
31
  end
29
32
 
33
+ ActiveGenie::Logger.info({ **log, step: :elo_end, round_count:, eligible_size: @players.eligible_size })
30
34
  @players
31
35
  end
32
36
 
@@ -35,6 +39,7 @@ module ActiveGenie::Leaderboard
35
39
  MATCHS_PER_PLAYER = 3
36
40
  LOSE_PENALTY = 15
37
41
  MINIMAL_PLAYERS_TO_BATTLE = 10
42
+ K = 32
38
43
 
39
44
  # Create a round of matches
40
45
  # each round is exactly 1 regation player vs 3 defense players for all regation players
@@ -64,14 +69,14 @@ module ActiveGenie::Leaderboard
64
69
  player_a,
65
70
  player_b,
66
71
  @criteria,
67
- options: @options
72
+ config:
68
73
  ).values_at('winner', 'loser')
69
74
  end
70
75
 
71
76
  def update_elo(winner, loser)
72
77
  return if winner.nil? || loser.nil?
73
78
 
74
- new_winner_elo, new_loser_elo = ActiveGenie::Utils::Math.calculate_new_elo(winner.elo, loser.elo)
79
+ new_winner_elo, new_loser_elo = calculate_new_elo(winner.elo, loser.elo)
75
80
 
76
81
  winner.elo = [new_winner_elo, max_defense_elo].min
77
82
  loser.elo = [new_loser_elo - LOSE_PENALTY, min_relegation_elo].max
@@ -84,5 +89,33 @@ module ActiveGenie::Leaderboard
84
89
  def min_relegation_elo
85
90
  @players.tier_relegation.min_by(&:elo).elo
86
91
  end
92
+
93
+ # Read more about the formula on https://en.wikipedia.org/wiki/Elo_rating_system
94
+ def calculate_new_elo(winner_elo, loser_elo)
95
+ expected_score_a = 1 / (1 + 10**((loser_elo - winner_elo) / 400))
96
+ expected_score_b = 1 - expected_score_a
97
+
98
+ new_elo_winner = winner_elo + K * (1 - expected_score_a)
99
+ new_elo_loser = loser_elo + K * (1 - expected_score_b)
100
+
101
+ [new_elo_winner, new_elo_loser]
102
+ end
103
+
104
+ def eliminate_all_relegation_players
105
+ eliminations = @players.tier_relegation.size
106
+ @players.tier_relegation.each { |player| player.eliminated = 'tier_relegation' }
107
+ ActiveGenie::Logger.trace({ **log, step: :elo_round, eligible_size: @players.eligible_size, eliminations: })
108
+ end
109
+
110
+ def config
111
+ { **@config }
112
+ end
113
+
114
+ def log
115
+ {
116
+ **(@config.dig(:log) || {}),
117
+ duration: Time.now - @start_time
118
+ }
119
+ end
87
120
  end
88
121
  end
@@ -0,0 +1,62 @@
1
+ require_relative '../battle/basic'
2
+
3
+ module ActiveGenie::Leaderboard
4
+ class FreeForAll
5
+ def self.call(players, criteria, config: {})
6
+ new(players, criteria, config:).call
7
+ end
8
+
9
+ def initialize(players, criteria, config: {})
10
+ @players = players
11
+ @criteria = criteria
12
+ @config = config
13
+ @start_time = Time.now
14
+ end
15
+
16
+ def call
17
+ matches.each do |player_a, player_b|
18
+ winner, loser = battle(player_a, player_b)
19
+
20
+ if winner.nil? || loser.nil?
21
+ player_a.free_for_all[:draw] += 1
22
+ player_b.free_for_all[:draw] += 1
23
+ else
24
+ winner.free_for_all[:win] += 1
25
+ loser.free_for_all[:lose] += 1
26
+ end
27
+
28
+ ActiveGenie::Logger.trace({**log, step: :free_for_all_battle, winner_id: winner&.id, player_a_id: player_a.id, player_a_free_for_all_score: player_a.free_for_all_score, player_b_id: player_b.id, player_b_free_for_all_score: player_b.free_for_all_score })
29
+ end
30
+
31
+ @players
32
+ end
33
+
34
+ private
35
+
36
+ # TODO: reduce the number of matches based on transitivity.
37
+ # For example, if A is better than B, and B is better than C, then A should clearly be better than C
38
+ def matches
39
+ @players.eligible.combination(2).to_a
40
+ end
41
+
42
+ def battle(player_a, player_b)
43
+ result = ActiveGenie::Battle.basic(
44
+ player_a,
45
+ player_b,
46
+ @criteria,
47
+ config:
48
+ )
49
+
50
+
51
+ result.values_at('winner', 'loser')
52
+ end
53
+
54
+ def config
55
+ { **@config }
56
+ end
57
+
58
+ def log
59
+ { **(@config.dig(:log) || {}), duration: Time.now - @start_time }
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,120 @@
1
+ require 'securerandom'
2
+
3
+ require_relative './players_collection'
4
+ require_relative './free_for_all'
5
+ require_relative './elo_ranking'
6
+ require_relative '../scoring/recommended_reviews'
7
+
8
+ # This class orchestrates player ranking through multiple evaluation stages
9
+ # using Elo ranking and free-for-all match simulations.
10
+ # 1. Sets initial scores
11
+ # 2. Eliminates low performers
12
+ # 3. Runs Elo ranking (for large groups)
13
+ # 4. Conducts free-for-all matches
14
+ #
15
+ # @example Basic usage
16
+ # League.call(players, criteria)
17
+ #
18
+ # @param param_players [Array] Collection of player objects to evaluate
19
+ # Example: ["Circle", "Triangle", "Square"]
20
+ # or
21
+ # [
22
+ # { content: "Circle", score: 10 },
23
+ # { content: "Triangle", score: 7 },
24
+ # { content: "Square", score: 5 }
25
+ # ]
26
+ # @param criteria [String] Evaluation criteria configuration
27
+ # Example: "What is more similar to the letter 'O'?"
28
+ # @param config [Hash] Additional configuration config
29
+ # Example: { model: "gpt-4o", api_key: ENV['OPENAI_API_KEY'] }
30
+ # @return [Hash] Final ranked player results
31
+ module ActiveGenie::League
32
+ class League
33
+ def self.call(param_players, criteria, config: {})
34
+ new(param_players, criteria, config:).call
35
+ end
36
+
37
+ def initialize(param_players, criteria, config: {})
38
+ @param_players = param_players
39
+ @criteria = criteria
40
+ @config = config
41
+ @league_id = SecureRandom.uuid
42
+ @start_time = Time.now
43
+ end
44
+
45
+ def call
46
+ set_initial_score_players
47
+ eliminate_obvious_bad_players
48
+ run_elo_ranking if players.eligible_size > 10
49
+ run_free_for_all
50
+
51
+ ActiveGenie::Logger.info({ **log, step: :league_end, top5: players.first(5).map(&:id) })
52
+ players.to_h
53
+ end
54
+
55
+ private
56
+
57
+ SCORE_VARIATION_THRESHOLD = 10
58
+
59
+ def set_initial_score_players
60
+ players_without_score = players.reject { |player| player.score }
61
+ players_without_score.each do |player|
62
+ player.score = generate_score(player.content) # This can take a while, can be parallelized
63
+ ActiveGenie::Logger.trace({ **log, step: :player_score, player_id: player.id, score: player.score })
64
+ end
65
+
66
+ ActiveGenie::Logger.info({ **log, step: :initial_score, evaluated_players: players_without_score.size })
67
+ end
68
+
69
+ def generate_score(content)
70
+ ActiveGenie::Scoring::Basic.call(content, @criteria, reviewers, config:)['final_score']
71
+ end
72
+
73
+ def eliminate_obvious_bad_players
74
+ eliminated_count = 0
75
+ while players.coefficient_of_variation >= SCORE_VARIATION_THRESHOLD
76
+ players.eligible.last.eliminated = 'variation_too_high'
77
+ eliminated_count += 1
78
+ end
79
+
80
+ ActiveGenie::Logger.info({ **log, step: :eliminate_obvious_bad_players, eliminated_count: })
81
+ end
82
+
83
+ def run_elo_ranking
84
+ EloRanking.call(players, @criteria, config:)
85
+ end
86
+
87
+ def run_free_for_all
88
+ FreeForAll.call(players, @criteria, config:)
89
+ end
90
+
91
+ def reviewers
92
+ [recommended_reviews['reviewer1'], recommended_reviews['reviewer2'], recommended_reviews['reviewer3']]
93
+ end
94
+
95
+ def recommended_reviews
96
+ @recommended_reviews ||= ActiveGenie::Scoring::RecommendedReviews.call(
97
+ [players.sample.content, players.sample.content].join("\n\n"),
98
+ @criteria,
99
+ config:
100
+ )
101
+ end
102
+
103
+ def players
104
+ @players ||= PlayersCollection.new(@param_players)
105
+ end
106
+
107
+ def config
108
+ { log:, **@config }
109
+ end
110
+
111
+ def log
112
+ {
113
+ **(@config.dig(:log) || {}),
114
+ league_id: @league_id,
115
+ league_start_time: @start_time,
116
+ duration: Time.now - @start_time
117
+ }
118
+ end
119
+ end
120
+ end
@@ -9,20 +9,24 @@ module ActiveGenie::Leaderboard
9
9
  @content = params.dig(:content) || params
10
10
  @score = params.dig(:score) || nil
11
11
  @elo = params.dig(:elo) || nil
12
- @league = {
13
- win: params.dig(:league, :win) || 0,
14
- lose: params.dig(:league, :lose) || 0,
15
- draw: params.dig(:league, :draw) || 0
12
+ @free_for_all = {
13
+ win: params.dig(:free_for_all, :win) || 0,
14
+ lose: params.dig(:free_for_all, :lose) || 0,
15
+ draw: params.dig(:free_for_all, :draw) || 0
16
16
  }
17
17
  @eliminated = params.dig(:eliminated) || nil
18
18
  end
19
19
 
20
- attr_reader :id, :content, :score, :elo, :league, :eliminated
20
+ attr_reader :id, :content, :score, :elo, :free_for_all, :eliminated
21
21
 
22
22
  def generate_elo_by_score
23
- return if !@elo.nil? || @score.nil?
23
+ return if !@elo.nil?
24
24
 
25
- @elo = BASE_ELO + (@score - 50)
25
+ if @score.nil?
26
+ @elo = BASE_ELO
27
+ else
28
+ @elo = BASE_ELO + (@score - 50)
29
+ end
26
30
  end
27
31
 
28
32
  def score=(value)
@@ -37,12 +41,15 @@ module ActiveGenie::Leaderboard
37
41
  @eliminated = value
38
42
  end
39
43
 
40
- def league_score
41
- @league[:win] * 3 + @league[:draw]
44
+ def free_for_all_score
45
+ @free_for_all[:win] * 3 + @free_for_all[:draw]
42
46
  end
43
47
 
44
48
  def to_h
45
- { id:, content:, score:, elo:, eliminated:, league: }
49
+ {
50
+ id:, content:, score:, elo:,
51
+ eliminated:, free_for_all:, free_for_all_score:
52
+ }
46
53
  end
47
54
 
48
55
  private
@@ -0,0 +1,12 @@
1
+ require_relative 'league/league'
2
+
3
+ module ActiveGenie
4
+ # See the [league README](lib/active_genie/league/README.md) for more information.
5
+ module League
6
+ module_function
7
+
8
+ def call(...)
9
+ league.call(...)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,45 @@
1
+ require 'json'
2
+ require 'fileutils'
3
+
4
+ module ActiveGenie
5
+ module Logger
6
+ module_function
7
+
8
+ def info(log)
9
+ save(log, level: :info)
10
+ end
11
+
12
+ def error(log)
13
+ save(log, level: :error)
14
+ end
15
+
16
+ def warn(log)
17
+ save(log, level: :warn)
18
+ end
19
+
20
+ def debug(log)
21
+ save(log, level: :debug)
22
+ end
23
+
24
+ def trace(log)
25
+ save(log, level: :trace)
26
+ end
27
+
28
+ LOG_LEVELS = { info: 0, error: 1, warn: 2, debug: 3, trace: 4 }.freeze
29
+
30
+ def save(log, level: :info)
31
+ return if LOG_LEVELS[log.dig(:log, :log_level)] || -1 < LOG_LEVELS[level]
32
+
33
+ log[:trace] = log.dig(:trace)&.to_s&.gsub('ActiveGenie::', '')
34
+ log[:timestamp] = Time.now
35
+ log[:level] = level.to_s.upcase
36
+ log[:process_id] = Process.pid
37
+
38
+ FileUtils.mkdir_p('logs')
39
+ File.write('logs/active_genie.log', "#{JSON.generate(log)}\n", mode: 'a')
40
+ puts log
41
+
42
+ log
43
+ end
44
+ end
45
+ end
@@ -49,26 +49,22 @@ result = ActiveGenie::Scoring::Basic.call(text, criteria)
49
49
 
50
50
  ## Interface
51
51
 
52
- ### `Basic.call(text, criteria, reviewers = [], options: {})`
52
+ ### `Basic.call(text, criteria, reviewers = [], config: {})`
53
53
  Main interface for scoring text content.
54
54
 
55
55
  #### Parameters
56
56
  - `text` [String] - The text content to be evaluated
57
57
  - `criteria` [String] - The evaluation criteria or rubric to assess against
58
58
  - `reviewers` [Array<String>] - Optional list of specific reviewers
59
- - `options` [Hash] - Additional configuration options
60
- - `:detailed_feedback` [Boolean] - Request more detailed feedback (WIP)
61
- - `:reviewer_weights` [Hash] - Custom weights for different reviewers (WIP)
59
+ - `config` [Hash] - Additional configuration config
62
60
 
63
- ### `RecommendedReviews.call(text, criteria, options: {})`
61
+ ### `RecommendedReviews.call(text, criteria, config: {})`
64
62
  Recommends appropriate reviewers based on content and criteria.
65
63
 
66
64
  #### Parameters
67
65
  - `text` [String] - The text content to analyze
68
66
  - `criteria` [String] - The evaluation criteria
69
- - `options` [Hash] - Additional configuration options
70
- - `:prefer_technical` [Boolean] - Favor technical expertise (WIP)
71
- - `:prefer_domain` [Boolean] - Favor domain expertise (WIP)
67
+ - `config` [Hash] - Additional configuration config
72
68
 
73
69
  ### Usage Notes
74
70
  - Best suited for objective evaluation of text content
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../clients/router'
3
+ require_relative '../clients/unified_client'
4
4
 
5
5
  module ActiveGenie::Scoring
6
6
  # The Basic class provides a foundation for scoring text content against specified criteria
@@ -21,21 +21,19 @@ module ActiveGenie::Scoring
21
21
  # @param criteria [String] The evaluation criteria or rubric to assess against
22
22
  # @param reviewers [Array<String>] Optional list of specific reviewers. If empty,
23
23
  # reviewers will be automatically recommended based on the content and criteria
24
- # @param options [Hash] Additional configuration options that modify the scoring behavior
25
- # @option options [Boolean] :detailed_feedback Request more detailed feedback in the reasoning
26
- # @option options [Hash] :reviewer_weights Custom weights for different reviewers
24
+ # @param config [Hash] Additional configuration config that modify the scoring behavior
27
25
  # @return [Hash] The evaluation result containing the scores and reasoning
28
26
  # @return [Number] :final_score The final score of the text based on the criteria and reviewers
29
27
  # @return [String] :final_reasoning Detailed explanation of why the final score was reached
30
- def self.call(text, criteria, reviewers = [], options: {})
31
- new(text, criteria, reviewers, options:).call
28
+ def self.call(text, criteria, reviewers = [], config: {})
29
+ new(text, criteria, reviewers, config:).call
32
30
  end
33
31
 
34
- def initialize(text, criteria, reviewers = [], options: {})
32
+ def initialize(text, criteria, reviewers = [], config: {})
35
33
  @text = text
36
34
  @criteria = criteria
37
35
  @reviewers = Array(reviewers).compact.uniq
38
- @options = options
36
+ @config = config
39
37
  end
40
38
 
41
39
  def call
@@ -78,7 +76,7 @@ module ActiveGenie::Scoring
78
76
  }
79
77
  }
80
78
 
81
- ::ActiveGenie::Clients::Router.function_calling(messages, function, options: @options)
79
+ ::ActiveGenie::Clients::UnifiedClient.function_calling(messages, function, config:)
82
80
  end
83
81
 
84
82
  private
@@ -87,7 +85,7 @@ module ActiveGenie::Scoring
87
85
  @get_or_recommend_reviewers ||= if @reviewers.count > 0
88
86
  @reviewers
89
87
  else
90
- recommended_reviews = RecommendedReviews.call(@text, @criteria, options: @options)
88
+ recommended_reviews = RecommendedReviews.call(@text, @criteria, config:)
91
89
 
92
90
  [recommended_reviews[:reviewer1], recommended_reviews[:reviewer2], recommended_reviews[:reviewer3]]
93
91
  end
@@ -113,5 +111,16 @@ module ActiveGenie::Scoring
113
111
  - Deconstruct each criterion into actionable components for a systematic evaluation.
114
112
  - If the text lacks information, apply reasonable judgment to assign a score while clearly explaining the rationale.
115
113
  PROMPT
114
+
115
+ def config
116
+ {
117
+ all_providers: { model_tier: 'lower_tier' },
118
+ log: {
119
+ **(@config.dig(:log) || {}),
120
+ trace: self.class.name,
121
+ },
122
+ **@config
123
+ }
124
+ end
116
125
  end
117
126
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../clients/router.rb'
3
+ require_relative '../clients/unified_client'
4
4
 
5
5
  module ActiveGenie::Scoring
6
6
  # The RecommendedReviews class intelligently suggests appropriate reviewer roles
@@ -17,21 +17,19 @@ module ActiveGenie::Scoring
17
17
  # # reviewer3: "Developer Advocate", reasoning: "..." }
18
18
  #
19
19
  class RecommendedReviews
20
- def self.call(text, criteria, options: {})
21
- new(text, criteria, options:).call
20
+ def self.call(text, criteria, config: {})
21
+ new(text, criteria, config:).call
22
22
  end
23
23
 
24
24
  # Initializes a new reviewer recommendation instance
25
25
  #
26
26
  # @param text [String] The text content to analyze for reviewer recommendations
27
27
  # @param criteria [String] The evaluation criteria that will guide reviewer selection
28
- # @param options [Hash] Additional configuration options that modify the recommendation process
29
- # @option options [Boolean] :prefer_technical Whether to favor technical expertise
30
- # @option options [Boolean] :prefer_domain Whether to favor domain expertise
31
- def initialize(text, criteria, options: {})
28
+ # @param config [Hash] Additional configuration config that modify the recommendation process
29
+ def initialize(text, criteria, config: {})
32
30
  @text = text
33
31
  @criteria = criteria
34
- @options = options
32
+ @config = config
35
33
  end
36
34
 
37
35
  def call
@@ -55,7 +53,7 @@ module ActiveGenie::Scoring
55
53
  }
56
54
  }
57
55
 
58
- ::ActiveGenie::Clients::Router.function_calling(messages, function, options: @options)
56
+ ::ActiveGenie::Clients::UnifiedClient.function_calling(messages, function, config: @config)
59
57
  end
60
58
 
61
59
  private
@@ -2,7 +2,7 @@ require_relative 'scoring/basic'
2
2
  require_relative 'scoring/recommended_reviews'
3
3
 
4
4
  module ActiveGenie
5
- # Text evaluation system that provides detailed scoring and feedback using multiple expert reviewers
5
+ # See the [Scoring README](lib/active_genie/scoring/README.md) for more information.
6
6
  module Scoring
7
7
  module_function
8
8
 
data/lib/active_genie.rb CHANGED
@@ -1,15 +1,19 @@
1
- module ActiveGenie
2
- autoload :Configuration, File.join(__dir__, 'active_genie/configuration')
1
+ require_relative 'active_genie/logger'
2
+ require_relative 'active_genie/configuration'
3
3
 
4
- # Modules
4
+ module ActiveGenie
5
5
  autoload :DataExtractor, File.join(__dir__, 'active_genie/data_extractor')
6
6
  autoload :Battle, File.join(__dir__, 'active_genie/battle')
7
7
  autoload :Scoring, File.join(__dir__, 'active_genie/scoring')
8
8
  autoload :Leaderboard, File.join(__dir__, 'active_genie/leaderboard')
9
9
 
10
- class << self
10
+ class << self
11
11
  def configure
12
- yield(config) if block_given?
12
+ yield(configuration) if block_given?
13
+ end
14
+
15
+ def configuration
16
+ @configuration ||= Configuration
13
17
  end
14
18
 
15
19
  def load_tasks
@@ -18,17 +22,5 @@ module ActiveGenie
18
22
  Rake::Task.define_task(:environment)
19
23
  Dir.glob(File.join(__dir__, 'tasks', '*.rake')).each { |r| load r }
20
24
  end
21
-
22
- def config
23
- @config ||= Configuration.new
24
- end
25
-
26
- def [](key)
27
- config.values[key.to_s]
28
- end
29
-
30
- def config_by_model(model = nil)
31
- config.values[model&.to_s&.downcase&.strip] || config.values.values.first || {}
32
- end
33
25
  end
34
26
  end
@@ -3,10 +3,10 @@ require 'fileutils'
3
3
  namespace :active_genie do
4
4
  desc 'Install active_genie configuration file'
5
5
  task :install do
6
- source = File.join(__dir__, 'templates', 'active_genie.yml')
7
- target = File.join('config', 'active_genie.yml')
6
+ source = File.join(__dir__, 'templates', 'active_genie.rb')
7
+ target = File.join('config', 'initializers', 'active_genie.rb')
8
8
 
9
9
  FileUtils.cp(source, target)
10
- puts "Successfully installed config/active_genie.yml to #{target}"
10
+ puts "Successfully installed active_genie!"
11
11
  end
12
12
  end
@@ -0,0 +1,17 @@
1
+
2
+ ActiveGenie.configure do |config|
3
+ # example with openai and the current default for each config
4
+ # config.providers.openai.api_key = ENV['OPENAI_API_KEY']
5
+ # config.providers.openai.organization = ENV['OPENAI_ORGANIZATION']
6
+ # config.providers.openai.api_url = 'https://api.openai.com/v1'
7
+ # config.providers.openai.lower_tier_model = 'gpt-4o-mini'
8
+ # config.providers.openai.middle_tier_model = 'gpt-4o'
9
+ # config.providers.openai.upper_tier_model = 'o1-preview'
10
+ # config.providers.openai.client = ActiveGenie::Providers::Openai::Client.new(config)
11
+
12
+ # example how add a new provider
13
+ # config.providers.register(:internal_company_api, InternalCompanyApi::Configuration)
14
+
15
+ # Logs configuration
16
+ # config.log_level = :debug # default is :info
17
+ end