active_genie 0.29.0 → 0.30.0
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.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/VERSION +1 -1
- data/lib/active_genie/{battle/generalist.json → comparator/debate.json} +2 -2
- data/lib/active_genie/{battle/generalist.prompt.md → comparator/debate.prompt.md} +1 -1
- data/lib/active_genie/{battle/generalist.rb → comparator/debate.rb} +20 -21
- data/lib/active_genie/{battle → comparator}/fight.rb +7 -7
- data/lib/active_genie/comparator.rb +24 -0
- data/lib/active_genie/{config/scoring_config.rb → configs/comparator_config.rb} +1 -1
- data/lib/active_genie/{config/data_extractor_config.rb → configs/extractor_config.rb} +1 -1
- data/lib/active_genie/{config/factory_config.rb → configs/lister_config.rb} +1 -1
- data/lib/active_genie/{config → configs}/llm_config.rb +6 -6
- data/lib/active_genie/{config/ranking_config.rb → configs/ranker_config.rb} +1 -1
- data/lib/active_genie/{config/battle_config.rb → configs/scorer_config.rb} +1 -1
- data/lib/active_genie/configuration.rb +19 -19
- data/lib/active_genie/errors/invalid_provider_error.rb +1 -1
- data/lib/active_genie/{data_extractor/generalist.rb → extractor/explanation.rb} +11 -11
- data/lib/active_genie/{data_extractor/from_informal.rb → extractor/litote.rb} +7 -7
- data/lib/active_genie/extractor.rb +22 -0
- data/lib/active_genie/{factory → lister}/feud.rb +6 -6
- data/lib/active_genie/lister/juries.prompt.md +20 -0
- data/lib/active_genie/lister/juries.rb +82 -0
- data/lib/active_genie/{factory.rb → lister.rb} +7 -6
- data/lib/active_genie/{clients/providers/anthropic_client.rb → providers/anthropic_provider.rb} +4 -4
- data/lib/active_genie/{clients/providers/base_client.rb → providers/base_provider.rb} +6 -6
- data/lib/active_genie/{clients/providers/deepseek_client.rb → providers/deepseek_provider.rb} +3 -3
- data/lib/active_genie/{clients/providers/google_client.rb → providers/google_provider.rb} +6 -6
- data/lib/active_genie/{clients/providers/openai_client.rb → providers/openai_provider.rb} +3 -3
- data/lib/active_genie/providers/unified_provider.rb +44 -0
- data/lib/active_genie/{ranking/elo_round.rb → ranker/elo.rb} +35 -35
- data/lib/active_genie/ranker/entities/player.rb +124 -0
- data/lib/active_genie/ranker/entities/players.rb +102 -0
- data/lib/active_genie/{ranking → ranker}/free_for_all.rb +8 -8
- data/lib/active_genie/ranker/scoring.rb +66 -0
- data/lib/active_genie/{ranking/ranking.rb → ranker/tournament.rb} +17 -25
- data/lib/active_genie/ranker.rb +32 -0
- data/lib/active_genie/scorer/jury_bench.rb +121 -0
- data/lib/active_genie/scorer.rb +17 -0
- data/lib/active_genie.rb +9 -9
- data/lib/tasks/test.rake +15 -0
- metadata +51 -50
- data/lib/active_genie/battle.rb +0 -31
- data/lib/active_genie/clients/unified_client.rb +0 -50
- data/lib/active_genie/data_extractor.rb +0 -23
- data/lib/active_genie/ranking/player.rb +0 -122
- data/lib/active_genie/ranking/players_collection.rb +0 -95
- data/lib/active_genie/ranking/ranking_scoring.rb +0 -69
- data/lib/active_genie/ranking.rb +0 -14
- data/lib/active_genie/scoring/generalist.json +0 -9
- data/lib/active_genie/scoring/generalist.rb +0 -119
- data/lib/active_genie/scoring/recommended_reviewers.rb +0 -87
- data/lib/active_genie/scoring.rb +0 -23
- /data/lib/active_genie/{battle → comparator}/fight.json +0 -0
- /data/lib/active_genie/{battle → comparator}/fight.prompt.md +0 -0
- /data/lib/active_genie/{config → configs}/log_config.rb +0 -0
- /data/lib/active_genie/{config → configs}/providers/anthropic_config.rb +0 -0
- /data/lib/active_genie/{config → configs}/providers/deepseek_config.rb +0 -0
- /data/lib/active_genie/{config → configs}/providers/google_config.rb +0 -0
- /data/lib/active_genie/{config → configs}/providers/openai_config.rb +0 -0
- /data/lib/active_genie/{config → configs}/providers/provider_base.rb +0 -0
- /data/lib/active_genie/{config → configs}/providers_config.rb +0 -0
- /data/lib/active_genie/{data_extractor/generalist.json → extractor/explanation.json} +0 -0
- /data/lib/active_genie/{data_extractor/generalist.prompt.md → extractor/explanation.prompt.md} +0 -0
- /data/lib/active_genie/{data_extractor/from_informal.json → extractor/litote.json} +0 -0
- /data/lib/active_genie/{factory → lister}/feud.json +0 -0
- /data/lib/active_genie/{factory → lister}/feud.prompt.md +0 -0
- /data/lib/active_genie/{scoring/generalist.prompt.md → scorer/jury_bench.md} +0 -0
@@ -1,95 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'player'
|
4
|
-
|
5
|
-
module ActiveGenie
|
6
|
-
module Ranking
|
7
|
-
class PlayersCollection
|
8
|
-
def initialize(param_players)
|
9
|
-
@players = build(param_players)
|
10
|
-
end
|
11
|
-
attr_reader :players
|
12
|
-
|
13
|
-
def coefficient_of_variation
|
14
|
-
mean = score_mean
|
15
|
-
|
16
|
-
return nil if mean.zero?
|
17
|
-
|
18
|
-
variance = all_scores.map { |num| (num - mean)**2 }.sum / all_scores.size
|
19
|
-
standard_deviation = Math.sqrt(variance)
|
20
|
-
|
21
|
-
(standard_deviation / mean) * 100
|
22
|
-
end
|
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
|
-
|
34
|
-
def calc_relegation_tier
|
35
|
-
eligible[(tier_size * -1)..]
|
36
|
-
end
|
37
|
-
|
38
|
-
def calc_defender_tier
|
39
|
-
eligible[(tier_size * -2)...(tier_size * -1)]
|
40
|
-
end
|
41
|
-
|
42
|
-
def eligible
|
43
|
-
sorted.reject(&:eliminated)
|
44
|
-
end
|
45
|
-
|
46
|
-
def eligible_size
|
47
|
-
@players.reject(&:eliminated).size
|
48
|
-
end
|
49
|
-
|
50
|
-
def elo_eligible?
|
51
|
-
eligible.size > 15
|
52
|
-
end
|
53
|
-
|
54
|
-
def sorted
|
55
|
-
@players.sort_by { |p| -p.sort_value }
|
56
|
-
end
|
57
|
-
|
58
|
-
def to_json(*_args)
|
59
|
-
@players.map(&:to_h).to_json
|
60
|
-
end
|
61
|
-
|
62
|
-
def method_missing(...)
|
63
|
-
@players.send(...)
|
64
|
-
end
|
65
|
-
|
66
|
-
def respond_to_missing?(method_name, include_private = false)
|
67
|
-
@players.respond_to?(method_name, include_private)
|
68
|
-
end
|
69
|
-
|
70
|
-
private
|
71
|
-
|
72
|
-
def build(param_players)
|
73
|
-
param_players.map { |p| Player.new(p) }
|
74
|
-
end
|
75
|
-
|
76
|
-
# Returns the number of players to battle in each round
|
77
|
-
# based on the eligible size, start fast and go slow until top 10
|
78
|
-
# Example:
|
79
|
-
# - 50 eligible, tier_size: 15
|
80
|
-
# - 35 eligible, tier_size: 11
|
81
|
-
# - 24 eligible, tier_size: 10
|
82
|
-
# - 14 eligible, tier_size: 4
|
83
|
-
# 4 rounds to reach top 10 with 50 players
|
84
|
-
def tier_size
|
85
|
-
size = (eligible_size / 3).ceil
|
86
|
-
|
87
|
-
if eligible_size < 10
|
88
|
-
(eligible_size / 2).ceil
|
89
|
-
else
|
90
|
-
size.clamp(10, eligible_size - 10)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
@@ -1,69 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ActiveGenie
|
4
|
-
module Ranking
|
5
|
-
class RankingScoring
|
6
|
-
def self.call(...)
|
7
|
-
new(...).call
|
8
|
-
end
|
9
|
-
|
10
|
-
def initialize(players, criteria, reviewers: [], config: nil)
|
11
|
-
@players = players
|
12
|
-
@criteria = criteria
|
13
|
-
@config = ActiveGenie.configuration.merge(config)
|
14
|
-
@reviewers = Array(reviewers).compact.uniq
|
15
|
-
end
|
16
|
-
|
17
|
-
def call
|
18
|
-
@config.log.additional_context = { ranking_scoring_id: }
|
19
|
-
@reviewers = generate_reviewers
|
20
|
-
|
21
|
-
players_without_score.each do |player|
|
22
|
-
player.score = generate_score(player)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
|
28
|
-
def players_without_score
|
29
|
-
@players_without_score ||= @players.select { |player| player.score.nil? }
|
30
|
-
end
|
31
|
-
|
32
|
-
def generate_score(player)
|
33
|
-
score, reasoning = ActiveGenie::Scoring.call(
|
34
|
-
player.content,
|
35
|
-
@criteria,
|
36
|
-
@reviewers,
|
37
|
-
config: @config
|
38
|
-
).values_at('final_score', 'final_reasoning')
|
39
|
-
|
40
|
-
@config.logger.call({ code: :new_score, player_id: player.id, score:, reasoning: })
|
41
|
-
|
42
|
-
score
|
43
|
-
end
|
44
|
-
|
45
|
-
def generate_reviewers
|
46
|
-
return @reviewers if @reviewers.size.positive?
|
47
|
-
|
48
|
-
reviewer1, reviewer2, reviewer3 = ActiveGenie::Scoring::RecommendedReviewers.call(
|
49
|
-
[@players.sample.content, @players.sample.content].join("\n\n"),
|
50
|
-
@criteria,
|
51
|
-
config: @config
|
52
|
-
).values_at('reviewer1', 'reviewer2', 'reviewer3')
|
53
|
-
|
54
|
-
@config.logger.call({ code: :new_reviewers, reviewers: [reviewer1, reviewer2, reviewer3] })
|
55
|
-
|
56
|
-
[reviewer1, reviewer2, reviewer3]
|
57
|
-
end
|
58
|
-
|
59
|
-
def ranking_scoring_id
|
60
|
-
@ranking_scoring_id ||= begin
|
61
|
-
player_ids = players_without_score.map(&:id).join(',')
|
62
|
-
ranking_unique_key = [player_ids, @criteria, @config.to_json].join('-')
|
63
|
-
|
64
|
-
"#{Digest::MD5.hexdigest(ranking_unique_key)}-scoring"
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
data/lib/active_genie/ranking.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'ranking/ranking'
|
4
|
-
|
5
|
-
module ActiveGenie
|
6
|
-
# See the [ranking README](lib/active_genie/ranking/README.md) for more information.
|
7
|
-
module Ranking
|
8
|
-
module_function
|
9
|
-
|
10
|
-
def call(...)
|
11
|
-
Ranking.call(...)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
@@ -1,119 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative '../clients/unified_client'
|
4
|
-
|
5
|
-
module ActiveGenie
|
6
|
-
module Scoring
|
7
|
-
# The Generalist class provides a foundation for scoring text content against specified criteria
|
8
|
-
# using AI-powered evaluation. It supports both single and multiple reviewer scenarios,
|
9
|
-
# with the ability to automatically recommend reviewers when none are specified.
|
10
|
-
#
|
11
|
-
# The scoring process evaluates text based on given criteria and returns detailed feedback
|
12
|
-
# including individual reviewer scores, reasoning, and a final aggregated score.
|
13
|
-
#
|
14
|
-
# @example Generalist usage with a single reviewer
|
15
|
-
# Generalist.call("Sample text", "Evaluate grammar and clarity", ["Grammar Expert"])
|
16
|
-
#
|
17
|
-
# @example Usage with automatic reviewer recommendation
|
18
|
-
# Generalist.call("Sample text", "Evaluate technical accuracy")
|
19
|
-
#
|
20
|
-
class Generalist
|
21
|
-
# @param text [String] The text content to be evaluated
|
22
|
-
# @param criteria [String] The evaluation criteria or rubric to assess against
|
23
|
-
# @param reviewers [Array<String>] Optional list of specific reviewers. If empty,
|
24
|
-
# reviewers will be automatically recommended based on the content and criteria
|
25
|
-
# @param config [Hash] Additional configuration config that modify the scoring behavior
|
26
|
-
# @return [Hash] The evaluation result containing the scores and reasoning
|
27
|
-
# @return [Number] :final_score The final score of the text based on the criteria and reviewers
|
28
|
-
# @return [String] :final_reasoning Detailed explanation of why the final score was reached
|
29
|
-
def self.call(...)
|
30
|
-
new(...).call
|
31
|
-
end
|
32
|
-
|
33
|
-
def initialize(text, criteria, reviewers = [], config: {})
|
34
|
-
@text = text
|
35
|
-
@criteria = criteria
|
36
|
-
@param_reviewers = Array(reviewers).compact.uniq
|
37
|
-
@config = ActiveGenie.configuration.merge(config)
|
38
|
-
end
|
39
|
-
|
40
|
-
def call
|
41
|
-
messages = [
|
42
|
-
{ role: 'system', content: PROMPT },
|
43
|
-
{ role: 'user', content: "Scoring criteria: #{@criteria}" },
|
44
|
-
{ role: 'user', content: "Text to score: #{@text}" }
|
45
|
-
]
|
46
|
-
|
47
|
-
result = ::ActiveGenie::Clients::UnifiedClient.function_calling(
|
48
|
-
messages,
|
49
|
-
build_function,
|
50
|
-
config: @config
|
51
|
-
)
|
52
|
-
|
53
|
-
result['final_score'] = 0 if result['final_score'].nil?
|
54
|
-
|
55
|
-
@config.logger.call({
|
56
|
-
code: :scoring,
|
57
|
-
text: @text[0..30],
|
58
|
-
criteria: @criteria[0..30],
|
59
|
-
reviewers: reviewers,
|
60
|
-
score: result['final_score'],
|
61
|
-
reasoning: result['final_reasoning']
|
62
|
-
})
|
63
|
-
|
64
|
-
result
|
65
|
-
end
|
66
|
-
|
67
|
-
PROMPT = File.read(File.join(__dir__, 'generalist.prompt.md'))
|
68
|
-
|
69
|
-
private
|
70
|
-
|
71
|
-
def build_function
|
72
|
-
properties = build_properties
|
73
|
-
|
74
|
-
function = JSON.parse(File.read(File.join(__dir__, 'generalist.json')), symbolize_names: true)
|
75
|
-
function[:parameters][:properties] = properties
|
76
|
-
function[:parameters][:required] = properties.keys
|
77
|
-
|
78
|
-
function
|
79
|
-
end
|
80
|
-
|
81
|
-
def build_properties
|
82
|
-
properties = {}
|
83
|
-
reviewers.each do |reviewer|
|
84
|
-
properties["#{reviewer}_reasoning"] = {
|
85
|
-
type: 'string',
|
86
|
-
description: "The reasoning of the scoring process by #{reviewer}."
|
87
|
-
}
|
88
|
-
properties["#{reviewer}_score"] = {
|
89
|
-
type: 'number',
|
90
|
-
description: "The score given by #{reviewer}.",
|
91
|
-
min: 0,
|
92
|
-
max: 100
|
93
|
-
}
|
94
|
-
end
|
95
|
-
|
96
|
-
properties[:final_score] = {
|
97
|
-
type: 'number',
|
98
|
-
description: 'The final score based on the previous reviewers'
|
99
|
-
}
|
100
|
-
properties[:final_reasoning] = {
|
101
|
-
type: 'string',
|
102
|
-
description: 'The final reasoning based on the previous reviewers'
|
103
|
-
}
|
104
|
-
|
105
|
-
properties
|
106
|
-
end
|
107
|
-
|
108
|
-
def reviewers
|
109
|
-
@reviewers ||= if @param_reviewers.any?
|
110
|
-
@param_reviewers
|
111
|
-
else
|
112
|
-
result = RecommendedReviewers.call(@text, @criteria, config: @config)
|
113
|
-
|
114
|
-
[result['reviewer1'], result['reviewer2'], result['reviewer3']]
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
@@ -1,87 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative '../clients/unified_client'
|
4
|
-
|
5
|
-
module ActiveGenie
|
6
|
-
module Scoring
|
7
|
-
# The RecommendedReviewers class intelligently suggests appropriate reviewer roles
|
8
|
-
# for evaluating text content based on specific criteria. It uses AI to analyze
|
9
|
-
# the content and criteria to identify the most suitable subject matter experts.
|
10
|
-
#
|
11
|
-
# The class ensures a balanced and comprehensive review process by recommending
|
12
|
-
# three distinct reviewer roles with complementary expertise and perspectives.
|
13
|
-
#
|
14
|
-
# @example Getting recommended reviewers for technical content
|
15
|
-
# RecommendedReviewers.call("Technical documentation about API design",
|
16
|
-
# "Evaluate technical accuracy and clarity")
|
17
|
-
# # => { reviewer1: "API Architect", reviewer2: "Technical Writer",
|
18
|
-
# # reviewer3: "Developer Advocate", reasoning: "..." }
|
19
|
-
#
|
20
|
-
class RecommendedReviewers
|
21
|
-
def self.call(...)
|
22
|
-
new(...).call
|
23
|
-
end
|
24
|
-
|
25
|
-
# Initializes a new reviewer recommendation instance
|
26
|
-
#
|
27
|
-
# @param text [String] The text content to analyze for reviewer recommendations
|
28
|
-
# @param criteria [String] The evaluation criteria that will guide reviewer selection
|
29
|
-
# @param config [Hash] Additional configuration config that modify the recommendation process
|
30
|
-
def initialize(text, criteria, config: {})
|
31
|
-
@text = text
|
32
|
-
@criteria = criteria
|
33
|
-
@config = ActiveGenie.configuration.merge(config)
|
34
|
-
end
|
35
|
-
|
36
|
-
def call
|
37
|
-
messages = [
|
38
|
-
{ role: 'system', content: PROMPT },
|
39
|
-
{ role: 'user', content: "Scoring criteria: #{@criteria}" },
|
40
|
-
{ role: 'user', content: "Text to score: #{@text}" }
|
41
|
-
]
|
42
|
-
|
43
|
-
function = {
|
44
|
-
name: 'identify_reviewers',
|
45
|
-
description: 'Discover reviewers based on the text and given criteria.',
|
46
|
-
parameters: {
|
47
|
-
type: 'object',
|
48
|
-
properties: {
|
49
|
-
reasoning: { type: 'string' },
|
50
|
-
reviewer1: { type: 'string' },
|
51
|
-
reviewer2: { type: 'string' },
|
52
|
-
reviewer3: { type: 'string' }
|
53
|
-
},
|
54
|
-
required: %w[reasoning reviewer1 reviewer2 reviewer3]
|
55
|
-
}
|
56
|
-
}
|
57
|
-
|
58
|
-
client.function_calling(
|
59
|
-
messages,
|
60
|
-
function,
|
61
|
-
config: @config
|
62
|
-
)
|
63
|
-
end
|
64
|
-
|
65
|
-
PROMPT = <<~PROMPT
|
66
|
-
Identify the top 3 suitable reviewer titles or roles based on the provided text and criteria. Selected reviewers must possess subject matter expertise, offer valuable insights, and ensure diverse yet aligned perspectives on the content.
|
67
|
-
|
68
|
-
# Instructions
|
69
|
-
1. **Analyze the Text and Criteria**: Examine the content and criteria to identify relevant reviewer titles or roles.
|
70
|
-
2. **Determine Subject Matter Expertise**: Select reviewers with substantial knowledge or experience in the subject area.
|
71
|
-
3. **Evaluate Insight Contribution**: Prioritize titles or roles capable of providing meaningful and actionable feedback on the content.
|
72
|
-
4. **Incorporate Perspective Diversity**: Ensure the selection includes reviewers with varied but complementary viewpoints while maintaining alignment with the criteria.
|
73
|
-
|
74
|
-
# Constraints
|
75
|
-
- Selected reviewers must align with the content’s subject matter and criteria.
|
76
|
-
- Include reasoning for how each choice supports a thorough and insightful review.
|
77
|
-
- Avoid redundant or overly similar titles/roles to maintain diversity.
|
78
|
-
PROMPT
|
79
|
-
|
80
|
-
private
|
81
|
-
|
82
|
-
def client
|
83
|
-
::ActiveGenie::Clients::UnifiedClient
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
data/lib/active_genie/scoring.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'scoring/generalist'
|
4
|
-
require_relative 'scoring/recommended_reviewers'
|
5
|
-
|
6
|
-
module ActiveGenie
|
7
|
-
# See the [Scoring README](lib/active_genie/scoring/README.md) for more information.
|
8
|
-
module Scoring
|
9
|
-
module_function
|
10
|
-
|
11
|
-
def call(...)
|
12
|
-
Generalist.call(...)
|
13
|
-
end
|
14
|
-
|
15
|
-
def generalist(...)
|
16
|
-
Generalist.call(...)
|
17
|
-
end
|
18
|
-
|
19
|
-
def recommended_reviewers(...)
|
20
|
-
RecommendedReviewers.call(...)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
/data/lib/active_genie/{data_extractor/generalist.prompt.md → extractor/explanation.prompt.md}
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|