active_genie 0.25.1 → 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 (41) 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 +4 -4
  14. data/lib/active_genie/config/battle_config.rb +2 -0
  15. data/lib/active_genie/config/llm_config.rb +3 -1
  16. data/lib/active_genie/config/log_config.rb +33 -11
  17. data/lib/active_genie/config/providers/anthropic_config.rb +2 -2
  18. data/lib/active_genie/config/providers/deepseek_config.rb +2 -2
  19. data/lib/active_genie/config/providers/google_config.rb +2 -2
  20. data/lib/active_genie/config/providers/openai_config.rb +2 -2
  21. data/lib/active_genie/config/providers_config.rb +4 -4
  22. data/lib/active_genie/config/scoring_config.rb +2 -0
  23. data/lib/active_genie/configuration.rb +14 -8
  24. data/lib/active_genie/data_extractor/from_informal.json +11 -0
  25. data/lib/active_genie/data_extractor/from_informal.rb +3 -11
  26. data/lib/active_genie/data_extractor/generalist.json +9 -0
  27. data/lib/active_genie/data_extractor/generalist.rb +12 -11
  28. data/lib/active_genie/errors/invalid_log_output_error.rb +19 -0
  29. data/lib/active_genie/logger.rb +10 -4
  30. data/lib/active_genie/{concerns → ranking/concerns}/loggable.rb +2 -5
  31. data/lib/active_genie/ranking/elo_round.rb +31 -27
  32. data/lib/active_genie/ranking/free_for_all.rb +29 -21
  33. data/lib/active_genie/ranking/player.rb +45 -17
  34. data/lib/active_genie/ranking/players_collection.rb +16 -6
  35. data/lib/active_genie/ranking/ranking.rb +21 -20
  36. data/lib/active_genie/ranking/ranking_scoring.rb +2 -19
  37. data/lib/active_genie/scoring/generalist.json +9 -0
  38. data/lib/active_genie/scoring/generalist.md +46 -0
  39. data/lib/active_genie/scoring/generalist.rb +13 -65
  40. data/lib/active_genie/scoring/recommended_reviewers.rb +2 -2
  41. metadata +11 -4
@@ -38,23 +38,19 @@ module ActiveGenie
38
38
  @llm ||= Config::LlmConfig.new
39
39
  end
40
40
 
41
+ SUB_CONFIGS = %w[log providers llm ranking scoring data_extractor battle].freeze
42
+
41
43
  def merge(config_params = {})
42
44
  return config_params if config_params.is_a?(Configuration)
43
45
 
44
46
  new_configuration = dup
45
47
 
46
- %w[log providers llm ranking scoring data_extractor battle].each do |key|
48
+ SUB_CONFIGS.each do |key|
47
49
  config = new_configuration.send(key)
48
50
 
49
51
  next unless config.respond_to?(:merge)
50
52
 
51
- new_config = if config_params.key?(key.to_s)
52
- config.merge(config_params[key.to_s])
53
- elsif config_params.key?(key.to_sym)
54
- config.merge(config_params[key.to_sym])
55
- else
56
- config.merge(config_params)
57
- end
53
+ new_config = sub_config_merge(config, key, config_params)
58
54
 
59
55
  new_configuration.send("#{key}=", new_config)
60
56
  end
@@ -62,6 +58,16 @@ module ActiveGenie
62
58
  new_configuration
63
59
  end
64
60
 
61
+ def sub_config_merge(config, key, config_params)
62
+ if config_params.key?(key.to_s)
63
+ config.merge(config_params[key.to_s])
64
+ elsif config_params.key?(key.to_sym)
65
+ config.merge(config_params[key.to_sym])
66
+ else
67
+ config.merge(config_params)
68
+ end
69
+ end
70
+
65
71
  attr_writer :log, :providers, :ranking, :scoring, :data_extractor, :battle, :llm
66
72
  end
67
73
  end
@@ -0,0 +1,11 @@
1
+
2
+ {
3
+ "message_litote": {
4
+ "type": "boolean",
5
+ "description": "Return true if the message is a litote. A litote is a figure of speech that uses understatement to emphasize a point by stating a negative to further affirm a positive, often incorporating double negatives for effect."
6
+ },
7
+ "litote_rephrased": {
8
+ "type": "string",
9
+ "description": "The true meaning of the litote. Rephrase the message to a positive and active statement."
10
+ }
11
+ }
@@ -47,17 +47,9 @@ module ActiveGenie
47
47
  private
48
48
 
49
49
  def data_to_extract_with_litote
50
- {
51
- **@data_to_extract,
52
- message_litote: {
53
- type: 'boolean',
54
- description: 'Return true if the message is a litote. A litote is a figure of speech that uses understatement to emphasize a point by stating a negative to further affirm a positive, often incorporating double negatives for effect.'
55
- },
56
- litote_rephrased: {
57
- type: 'string',
58
- description: 'The true meaning of the litote. Rephrase the message to a positive and active statement.'
59
- }
60
- }
50
+ parameters = JSON.parse(File.read(File.join(__dir__, 'from_informal.json')), symbolize_names: true)
51
+
52
+ @data_to_extract.merge(parameters)
61
53
  end
62
54
  end
63
55
  end
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "data_extractor",
3
+ "description": "Extract structured and typed data from text",
4
+ "parameters": {
5
+ "type": "object",
6
+ "properties": {},
7
+ "required": {}
8
+ }
9
+ }
@@ -42,15 +42,9 @@ module ActiveGenie
42
42
 
43
43
  properties = data_to_extract_with_explanation
44
44
 
45
- function = {
46
- name: 'data_extractor',
47
- description: 'Extract structured and typed data from text',
48
- parameters: {
49
- type: 'object',
50
- properties:,
51
- required: properties.keys
52
- }
53
- }
45
+ function = JSON.parse(File.read(File.join(__dir__, 'generalist.json')), symbolize_names: true)
46
+ function[:parameters][:properties] = properties
47
+ function[:parameters][:required] = properties.keys
54
48
 
55
49
  response = function_calling(messages, function)
56
50
 
@@ -68,11 +62,18 @@ module ActiveGenie
68
62
  with_explanation[key] = value
69
63
  with_explanation["#{key}_explanation"] = {
70
64
  type: 'string',
71
- description: "The chain of thought that led to the conclusion about: #{key}. Can be blank if the user didn't provide any context"
65
+ description: "
66
+ The chain of thought that led to the conclusion about: #{key}.
67
+ Can be blank if the user didn't provide any context
68
+ "
72
69
  }
73
70
  with_explanation["#{key}_accuracy"] = {
74
71
  type: 'integer',
75
- description: 'The accuracy of the extracted data, what is the percentage of confidence? When 100 it means the data is explicitly stated in the text. When 0 it means is no way to discover the data from the text'
72
+ description: '
73
+ The accuracy of the extracted data, what is the percentage of confidence?
74
+ When 100 it means the data is explicitly stated in the text.
75
+ When 0 it means is no way to discover the data from the text
76
+ '
76
77
  }
77
78
  end
78
79
 
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveGenie
4
+ class InvalidLogOutputError < StandardError
5
+ TEXT = <<~TEXT
6
+ Invalid log output option. Must be a callable object. Given: %<output>s
7
+ Example:
8
+ ```ruby
9
+ ActiveGenie.configure do |config|
10
+ config.log.output = ->(log) { puts log }
11
+ end
12
+ ```
13
+ TEXT
14
+
15
+ def initialize(output)
16
+ super(format(TEXT, output:))
17
+ end
18
+ end
19
+ end
@@ -16,8 +16,7 @@ module ActiveGenie
16
16
  }
17
17
 
18
18
  persist!(log)
19
- $stdout.puts log
20
- ActiveGenie.configuration.log.call_observers(log)
19
+ config.output_call(log)
21
20
 
22
21
  log
23
22
  end
@@ -35,8 +34,15 @@ module ActiveGenie
35
34
  attr_accessor :context
36
35
 
37
36
  def persist!(log)
38
- FileUtils.mkdir_p('log')
39
- File.write('log/active_genie.log', "#{JSON.generate(log)}\n", mode: 'a')
37
+ file_path = log.key?(:fine_tune) && log[:fine_tune] ? config.fine_tune_file_path : config.file_path
38
+ folder_path = File.dirname(file_path)
39
+
40
+ FileUtils.mkdir_p(folder_path)
41
+ File.write(file_path, "#{JSON.generate(log)}\n", mode: 'a')
42
+ end
43
+
44
+ def config
45
+ ActiveGenie.configuration.log
40
46
  end
41
47
  end
42
48
  end
@@ -20,11 +20,8 @@ module ActiveGenie
20
20
  end
21
21
  end
22
22
 
23
- def logger(data)
24
- log = ActiveGenie::Logger.call(data)
25
- @config&.log&.call_observers(log)
26
-
27
- log
23
+ def logger(...)
24
+ ActiveGenie::Logger.call(...)
28
25
  end
29
26
  end
30
27
  end
@@ -16,37 +16,30 @@ module ActiveGenie
16
16
  @criteria = criteria
17
17
  @config = config
18
18
  @tmp_defenders = []
19
- @start_time = Time.now
20
19
  @total_tokens = 0
21
- @previous_elo = {}
20
+ @previous_elo = players.to_h { |player| [player.id, player.elo] }
22
21
  @previous_highest_elo = @defender_tier.max_by(&:elo).elo
23
22
  end
24
23
 
25
24
  def call
26
25
  ActiveGenie::Logger.with_context(log_context) do
27
- save_previous_elo
28
- matches.each do |player_1, player_2|
26
+ matches.each do |player_a, player_b|
29
27
  # TODO: battle can take a while, can be parallelized
30
- winner, loser = battle(player_1, player_2)
31
- next if winner.nil? || loser.nil?
32
-
33
- winner.elo = calculate_new_elo(winner.elo, loser.elo, 1)
34
- loser.elo = calculate_new_elo(loser.elo, winner.elo, 0)
28
+ winner, loser = battle(player_a, player_b)
29
+ update_players_elo(winner, loser)
35
30
  end
36
31
  end
37
32
 
38
- ActiveGenie::Logger.call({ code: :elo_round_report, **report })
39
-
40
- report
33
+ build_report
41
34
  end
42
35
 
43
- private
44
-
45
36
  BATTLE_PER_PLAYER = 3
46
37
  K = 32
47
38
 
39
+ private
40
+
48
41
  def save_previous_elo
49
- @previous_elo = @players.map { |player| [player.id, player.elo] }.to_h
42
+ @previous_elo = @players.to_h { |player| [player.id, player.elo] }
50
43
  end
51
44
 
52
45
  def matches
@@ -63,18 +56,18 @@ module ActiveGenie
63
56
  @tmp_defenders.pop
64
57
  end
65
58
 
66
- def battle(player_1, player_2)
67
- ActiveGenie::Logger.with_context({ player_1_id: player_1.id, player_2_id: player_2.id }) do
59
+ def battle(player_a, player_b)
60
+ ActiveGenie::Logger.with_context({ player_a_id: player_a.id, player_b_id: player_b.id }) do
68
61
  result = ActiveGenie::Battle.call(
69
- player_1.content,
70
- player_2.content,
62
+ player_a.content,
63
+ player_b.content,
71
64
  @criteria,
72
65
  config: @config
73
66
  )
74
67
 
75
68
  winner, loser = case result['winner']
76
- when 'player_1' then [player_1, player_2]
77
- when 'player_2' then [player_2, player_1]
69
+ when 'player_a' then [player_a, player_b]
70
+ when 'player_b' then [player_b, player_a]
78
71
  when 'draw' then [nil, nil]
79
72
  end
80
73
 
@@ -82,9 +75,16 @@ module ActiveGenie
82
75
  end
83
76
  end
84
77
 
78
+ def update_players_elo(winner, loser)
79
+ return if winner.nil? || loser.nil?
80
+
81
+ winner.elo = calculate_new_elo(winner.elo, loser.elo, 1)
82
+ loser.elo = calculate_new_elo(loser.elo, winner.elo, 0)
83
+ end
84
+
85
85
  # INFO: Read more about the Elo rating system on https://en.wikipedia.org/wiki/Elo_rating_system
86
86
  def calculate_new_elo(player_rating, opponent_rating, score)
87
- expected_score = 1.0 / (1.0 + 10.0**((opponent_rating - player_rating) / 400.0))
87
+ expected_score = 1.0 / (1.0 + (10.0**((opponent_rating - player_rating) / 400.0)))
88
88
 
89
89
  player_rating + (K * (score - expected_score)).round
90
90
  end
@@ -101,18 +101,21 @@ module ActiveGenie
101
101
  Digest::MD5.hexdigest(ranking_unique_key)
102
102
  end
103
103
 
104
- def report
105
- {
104
+ def build_report
105
+ report = {
106
106
  elo_round_id:,
107
107
  players_in_round: players_in_round.map(&:id),
108
108
  battles_count: matches.size,
109
- duration_seconds: Time.now - @start_time,
110
109
  total_tokens: @total_tokens,
111
110
  previous_highest_elo: @previous_highest_elo,
112
111
  highest_elo:,
113
112
  highest_elo_diff: highest_elo - @previous_highest_elo,
114
113
  players_elo_diff:
115
114
  }
115
+
116
+ ActiveGenie::Logger.call({ code: :elo_round_report, **report })
117
+
118
+ report
116
119
  end
117
120
 
118
121
  def players_in_round
@@ -124,9 +127,10 @@ module ActiveGenie
124
127
  end
125
128
 
126
129
  def players_elo_diff
127
- players_in_round.map do |player|
130
+ elo_diffs = players_in_round.map do |player|
128
131
  [player.id, player.elo - @previous_elo[player.id]]
129
- end.sort_by { |_, diff| -diff }.to_h
132
+ end
133
+ elo_diffs.sort_by { |_, diff| -diff }.to_h
130
134
  end
131
135
 
132
136
  def log_observer(log)
@@ -19,22 +19,14 @@ module ActiveGenie
19
19
 
20
20
  def call
21
21
  ActiveGenie::Logger.with_context(log_context, observer: method(:log_observer)) do
22
- matches.each do |player_1, player_2|
23
- winner, loser = battle(player_1, player_2)
24
-
25
- if winner.nil? || loser.nil?
26
- player_1.draw!
27
- player_2.draw!
28
- else
29
- winner.win!
30
- loser.lose!
31
- end
22
+ matches.each do |player_a, player_b|
23
+ winner, loser = battle(player_a, player_b)
24
+
25
+ update_players_score(winner, loser)
32
26
  end
33
27
  end
34
28
 
35
- ActiveGenie::Logger.call({ code: :free_for_all_report, **report })
36
-
37
- report
29
+ build_report
38
30
  end
39
31
 
40
32
  private
@@ -45,23 +37,23 @@ module ActiveGenie
45
37
  @players.eligible.combination(2).to_a
46
38
  end
47
39
 
48
- def battle(player_1, player_2)
40
+ def battle(player_a, player_b)
49
41
  result = ActiveGenie::Battle.call(
50
- player_1.content,
51
- player_2.content,
42
+ player_a.content,
43
+ player_b.content,
52
44
  @criteria,
53
45
  config: @config
54
46
  )
55
47
 
56
48
  winner, loser = case result['winner']
57
- when 'player_1' then [player_1, player_2, result['reasoning']]
58
- when 'player_2' then [player_2, player_1, result['reasoning']]
49
+ when 'player_a' then [player_a, player_b, result['reasoning']]
50
+ when 'player_b' then [player_b, player_a, result['reasoning']]
59
51
  when 'draw' then [nil, nil, result['reasoning']]
60
52
  end
61
53
 
62
54
  ActiveGenie::Logger.call({
63
55
  code: :free_for_all_battle,
64
- player_ids: [player_1.id, player_2.id],
56
+ player_ids: [player_a.id, player_b.id],
65
57
  winner_id: winner&.id,
66
58
  loser_id: loser&.id,
67
59
  reasoning: result['reasoning']
@@ -70,6 +62,18 @@ module ActiveGenie
70
62
  [winner, loser]
71
63
  end
72
64
 
65
+ def update_players_score(winner, loser)
66
+ return if winner.nil? || loser.nil?
67
+
68
+ if winner.nil? || loser.nil?
69
+ player_a.draw!
70
+ player_b.draw!
71
+ else
72
+ winner.win!
73
+ loser.lose!
74
+ end
75
+ end
76
+
73
77
  def log_context
74
78
  { free_for_all_id: }
75
79
  end
@@ -80,13 +84,17 @@ module ActiveGenie
80
84
  Digest::MD5.hexdigest(ranking_unique_key)
81
85
  end
82
86
 
83
- def report
84
- {
87
+ def build_report
88
+ report = {
85
89
  free_for_all_id:,
86
90
  battles_count: matches.size,
87
91
  duration_seconds: Time.now - @start_time,
88
92
  total_tokens: @total_tokens
89
93
  }
94
+
95
+ ActiveGenie::Logger.call({ code: :free_for_all_report, **report })
96
+
97
+ report
90
98
  end
91
99
 
92
100
  def log_observer(log)
@@ -6,20 +6,48 @@ module ActiveGenie
6
6
  module Ranking
7
7
  class Player
8
8
  def initialize(params)
9
- params = { content: params } if params.is_a?(String)
10
-
11
- @content = params[:content] || params
12
- @name = params[:name] || params[:content][0..10]
13
- @id = params[:id] || Digest::MD5.hexdigest(@content)
14
- @score = params[:score] || nil
15
- @elo = params[:elo] || nil
16
- @ffa_win_count = params[:ffa_win_count] || 0
17
- @ffa_lose_count = params[:ffa_lose_count] || 0
18
- @ffa_draw_count = params[:ffa_draw_count] || 0
19
- @eliminated = params[:eliminated] || nil
9
+ @params = params.is_a?(String) ? { content: params } : params.dup
10
+ @params[:content] ||= @params
20
11
  end
12
+
21
13
  attr_accessor :rank
22
14
 
15
+ def content
16
+ @content ||= @params[:content]
17
+ end
18
+
19
+ def name
20
+ @name ||= @params[:name] || content[0..10]
21
+ end
22
+
23
+ def id
24
+ @id ||= @params[:id] || Digest::MD5.hexdigest(content.to_s)
25
+ end
26
+
27
+ def score
28
+ @score ||= @params[:score]
29
+ end
30
+
31
+ def elo
32
+ @elo ||= @params[:elo]
33
+ end
34
+
35
+ def ffa_win_count
36
+ @ffa_win_count ||= @params[:ffa_win_count] || 0
37
+ end
38
+
39
+ def ffa_lose_count
40
+ @ffa_lose_count ||= @params[:ffa_lose_count] || 0
41
+ end
42
+
43
+ def ffa_draw_count
44
+ @ffa_draw_count ||= @params[:ffa_draw_count] || 0
45
+ end
46
+
47
+ def eliminated
48
+ @eliminated ||= @params[:eliminated]
49
+ end
50
+
23
51
  def score=(value)
24
52
  ActiveGenie::Logger.call({ code: :new_score, player_id: id, score: value }) if value != @score
25
53
  @score = value
@@ -51,10 +79,8 @@ module ActiveGenie
51
79
  ActiveGenie::Logger.call({ code: :new_ffa_score, player_id: id, result: 'lose', ffa_score: })
52
80
  end
53
81
 
54
- attr_reader :id, :content, :score, :elo, :ffa_win_count, :ffa_lose_count, :ffa_draw_count, :eliminated, :name
55
-
56
82
  def ffa_score
57
- @ffa_win_count * 3 + @ffa_draw_count
83
+ (@ffa_win_count * 3) + @ffa_draw_count
58
84
  end
59
85
 
60
86
  def sort_value
@@ -73,7 +99,7 @@ module ActiveGenie
73
99
  }
74
100
  end
75
101
 
76
- def method_missing(method_name, *args, &block)
102
+ def method_missing(method_name, *args, &)
77
103
  if method_name == :[] && args.size == 1
78
104
  attr_name = args.first.to_sym
79
105
 
@@ -90,11 +116,13 @@ module ActiveGenie
90
116
  method_name == :[] || super
91
117
  end
92
118
 
119
+ BASE_ELO = 1000
120
+
121
+ private
122
+
93
123
  def generate_elo_by_score
94
124
  BASE_ELO + ((@score || 0) - 50)
95
125
  end
96
-
97
- BASE_ELO = 1000
98
126
  end
99
127
  end
100
128
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './player'
3
+ require_relative 'player'
4
4
 
5
5
  module ActiveGenie
6
6
  module Ranking
@@ -11,18 +11,26 @@ module ActiveGenie
11
11
  attr_reader :players
12
12
 
13
13
  def coefficient_of_variation
14
- score_list = eligible.map(&:score).compact
15
- return nil if score_list.empty?
14
+ mean = score_mean
16
15
 
17
- mean = score_list.sum.to_f / score_list.size
18
16
  return nil if mean.zero?
19
17
 
20
- variance = score_list.map { |num| (num - mean)**2 }.sum / score_list.size
18
+ variance = all_scores.map { |num| (num - mean)**2 }.sum / all_scores.size
21
19
  standard_deviation = Math.sqrt(variance)
22
20
 
23
21
  (standard_deviation / mean) * 100
24
22
  end
25
23
 
24
+ def all_scores
25
+ eligible.map(&:score).compact
26
+ end
27
+
28
+ def score_mean
29
+ return 0 if all_scores.empty?
30
+
31
+ all_scores.sum.to_f / all_scores.size
32
+ end
33
+
26
34
  def calc_relegation_tier
27
35
  eligible[(tier_size * -1)..]
28
36
  end
@@ -80,7 +88,9 @@ module ActiveGenie
80
88
  # - 14 eligible, tier_size: 4
81
89
  # 4 rounds to reach top 10 with 50 players
82
90
  def tier_size
83
- [[(eligible_size / 3).ceil, 10].max, eligible_size - 10].min
91
+ size = (eligible_size / 3).ceil
92
+
93
+ size.clamp(10, eligible_size - 10)
84
94
  end
85
95
  end
86
96
  end
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../concerns/loggable'
4
- require_relative './players_collection'
5
- require_relative './free_for_all'
6
- require_relative './elo_round'
7
- require_relative './ranking_scoring'
3
+ require_relative 'concerns/loggable'
4
+ require_relative 'players_collection'
5
+ require_relative 'free_for_all'
6
+ require_relative 'elo_round'
7
+ require_relative 'ranking_scoring'
8
8
 
9
9
  # This class orchestrates player ranking through multiple evaluation stages
10
10
  # using Elo ranking and free-for-all match simulations.
@@ -38,7 +38,8 @@ module ActiveGenie
38
38
  new(...).call
39
39
  end
40
40
 
41
- def initialize(_param_players, criteria, reviewers: [], config: {})
41
+ def initialize(param_players, criteria, reviewers: [], config: {})
42
+ @param_players = param_players
42
43
  @criteria = criteria
43
44
  @reviewers = Array(reviewers).compact.uniq
44
45
  @config = ActiveGenie.configuration.merge(config)
@@ -48,30 +49,30 @@ module ActiveGenie
48
49
  def call
49
50
  @players = create_players
50
51
 
51
- set_initial_player_scores!
52
- eliminate_obvious_bad_players!
52
+ ActiveGenie::Logger.with_context(log_context) do
53
+ set_initial_player_scores!
54
+ eliminate_obvious_bad_players!
53
55
 
54
- while @players.elo_eligible?
55
- elo_report = run_elo_round!
56
- eliminate_relegation_players!
57
- rebalance_players!(elo_report)
58
- end
56
+ while @players.elo_eligible?
57
+ elo_report = run_elo_round!
58
+ eliminate_relegation_players!
59
+ rebalance_players!(elo_report)
60
+ end
59
61
 
60
- run_free_for_all!
62
+ run_free_for_all!
63
+ end
61
64
 
62
65
  sorted_players
63
66
  end
64
67
 
65
- private
66
-
67
68
  ELIMINATION_VARIATION = 'variation_too_high'
68
69
  ELIMINATION_RELEGATION = 'relegation_tier'
69
70
 
70
- call_with_log_context :log_context
71
+ private
71
72
 
72
73
  def create_players
73
- players = PlayersCollection.new(param_players)
74
- players.each { |p| logger({ code: :new_player, player: p.to_h }) }
74
+ players = PlayersCollection.new(@param_players)
75
+ players.each { |p| ActiveGenie::Logger.call({ code: :new_player, player: p.to_h }) }
75
76
 
76
77
  players
77
78
  end
@@ -110,7 +111,7 @@ module ActiveGenie
110
111
 
111
112
  def sorted_players
112
113
  players = @players.sorted
113
- logger({ code: :ranking_final, players: players.map(&:to_h) })
114
+ ActiveGenie::Logger.call({ code: :ranking_final, players: players.map(&:to_h) })
114
115
 
115
116
  players.map(&:to_h)
116
117
  end
@@ -21,25 +21,8 @@ module ActiveGenie
21
21
  @reviewers = generate_reviewers
22
22
 
23
23
  players_to_score = players_without_score
24
- until players_to_score.empty?
25
- threads = []
26
- mutex = Mutex.new
27
-
28
- # Take up to 3 players for parallel processing
29
- current_batch = players_to_score.shift(3)
30
-
31
- current_batch.each do |player|
32
- threads << Thread.new(player) do |p|
33
- score = generate_score(p)
34
-
35
- mutex.synchronize do
36
- p.score = score
37
- end
38
- end
39
- end
40
-
41
- # Wait for all threads in this batch to complete
42
- threads.each(&:join)
24
+ players_to_score.each do |player|
25
+ player.score = generate_score(player)
43
26
  end
44
27
  end
45
28
  end
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "scoring",
3
+ "description": "Score the text based on the given criteria.",
4
+ "parameters": {
5
+ "type": "object",
6
+ "properties": {},
7
+ "required": {}
8
+ }
9
+ }