active_genie 0.29.1 → 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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/VERSION +1 -1
  4. data/lib/active_genie/{battle/generalist.json → comparator/debate.json} +2 -2
  5. data/lib/active_genie/{battle/generalist.prompt.md → comparator/debate.prompt.md} +1 -1
  6. data/lib/active_genie/{battle/generalist.rb → comparator/debate.rb} +20 -21
  7. data/lib/active_genie/{battle → comparator}/fight.rb +7 -7
  8. data/lib/active_genie/comparator.rb +24 -0
  9. data/lib/active_genie/{config/scoring_config.rb → configs/comparator_config.rb} +1 -1
  10. data/lib/active_genie/{config/data_extractor_config.rb → configs/extractor_config.rb} +1 -1
  11. data/lib/active_genie/{config/factory_config.rb → configs/lister_config.rb} +1 -1
  12. data/lib/active_genie/{config → configs}/llm_config.rb +6 -6
  13. data/lib/active_genie/{config/ranking_config.rb → configs/ranker_config.rb} +1 -1
  14. data/lib/active_genie/{config/battle_config.rb → configs/scorer_config.rb} +1 -1
  15. data/lib/active_genie/configuration.rb +19 -19
  16. data/lib/active_genie/errors/invalid_provider_error.rb +1 -1
  17. data/lib/active_genie/{data_extractor/generalist.rb → extractor/explanation.rb} +11 -11
  18. data/lib/active_genie/{data_extractor/from_informal.rb → extractor/litote.rb} +7 -7
  19. data/lib/active_genie/extractor.rb +22 -0
  20. data/lib/active_genie/{factory → lister}/feud.rb +6 -6
  21. data/lib/active_genie/lister/juries.prompt.md +20 -0
  22. data/lib/active_genie/lister/juries.rb +82 -0
  23. data/lib/active_genie/{factory.rb → lister.rb} +7 -6
  24. data/lib/active_genie/{clients/providers/anthropic_client.rb → providers/anthropic_provider.rb} +4 -4
  25. data/lib/active_genie/{clients/providers/base_client.rb → providers/base_provider.rb} +6 -6
  26. data/lib/active_genie/{clients/providers/deepseek_client.rb → providers/deepseek_provider.rb} +3 -3
  27. data/lib/active_genie/{clients/providers/google_client.rb → providers/google_provider.rb} +6 -6
  28. data/lib/active_genie/{clients/providers/openai_client.rb → providers/openai_provider.rb} +3 -3
  29. data/lib/active_genie/providers/unified_provider.rb +44 -0
  30. data/lib/active_genie/{ranking/elo_round.rb → ranker/elo.rb} +35 -35
  31. data/lib/active_genie/ranker/entities/player.rb +124 -0
  32. data/lib/active_genie/ranker/entities/players.rb +102 -0
  33. data/lib/active_genie/{ranking → ranker}/free_for_all.rb +8 -8
  34. data/lib/active_genie/ranker/scoring.rb +66 -0
  35. data/lib/active_genie/{ranking/ranking.rb → ranker/tournament.rb} +17 -25
  36. data/lib/active_genie/ranker.rb +32 -0
  37. data/lib/active_genie/scorer/jury_bench.rb +121 -0
  38. data/lib/active_genie/scorer.rb +17 -0
  39. data/lib/active_genie.rb +9 -9
  40. data/lib/tasks/test.rake +4 -0
  41. metadata +50 -50
  42. data/lib/active_genie/battle.rb +0 -31
  43. data/lib/active_genie/clients/unified_client.rb +0 -50
  44. data/lib/active_genie/data_extractor.rb +0 -23
  45. data/lib/active_genie/ranking/player.rb +0 -122
  46. data/lib/active_genie/ranking/players_collection.rb +0 -95
  47. data/lib/active_genie/ranking/ranking_scoring.rb +0 -69
  48. data/lib/active_genie/ranking.rb +0 -14
  49. data/lib/active_genie/scoring/generalist.json +0 -9
  50. data/lib/active_genie/scoring/generalist.rb +0 -119
  51. data/lib/active_genie/scoring/recommended_reviewers.rb +0 -87
  52. data/lib/active_genie/scoring.rb +0 -23
  53. /data/lib/active_genie/{battle → comparator}/fight.json +0 -0
  54. /data/lib/active_genie/{battle → comparator}/fight.prompt.md +0 -0
  55. /data/lib/active_genie/{config → configs}/log_config.rb +0 -0
  56. /data/lib/active_genie/{config → configs}/providers/anthropic_config.rb +0 -0
  57. /data/lib/active_genie/{config → configs}/providers/deepseek_config.rb +0 -0
  58. /data/lib/active_genie/{config → configs}/providers/google_config.rb +0 -0
  59. /data/lib/active_genie/{config → configs}/providers/openai_config.rb +0 -0
  60. /data/lib/active_genie/{config → configs}/providers/provider_base.rb +0 -0
  61. /data/lib/active_genie/{config → configs}/providers_config.rb +0 -0
  62. /data/lib/active_genie/{data_extractor/generalist.json → extractor/explanation.json} +0 -0
  63. /data/lib/active_genie/{data_extractor/generalist.prompt.md → extractor/explanation.prompt.md} +0 -0
  64. /data/lib/active_genie/{data_extractor/from_informal.json → extractor/litote.json} +0 -0
  65. /data/lib/active_genie/{factory → lister}/feud.json +0 -0
  66. /data/lib/active_genie/{factory → lister}/feud.prompt.md +0 -0
  67. /data/lib/active_genie/{scoring/generalist.prompt.md → scorer/jury_bench.md} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d2f34c9574bf0d25b2411f3b65678fe3a83f243cf8f4d83f97964411ec737918
4
- data.tar.gz: 19359a3fe3033791197b77adff4aba84346df4c7d16e6fa8c14f4cf5c08a274c
3
+ metadata.gz: a3798199139490fc67b7bd1e154c74ff7c53411bd035cd9e2fd1d9b46e676a90
4
+ data.tar.gz: 5d9be13a369c0a6116257822f75365241ea7f628e0bdc11af085ecb267cef806
5
5
  SHA512:
6
- metadata.gz: 203d650dd794406004379de0a356681a42b008da14c9b3a2e902a54444114e9ebce78c14c5f1edfa4af961c4dcc8a6591ef07b4efef1016b62743a12ac021424
7
- data.tar.gz: 8a72b957973e192998e80d25e26b4acfa1c719886d181d2052c3a1aa463f187f194ff2468d888c27b7e4aacadd12043def0ad40e79e3bb98bd9c061c48dc415b
6
+ metadata.gz: 1929da5851c827ef6b693aa4f66b6118a2cda72c1b7996e602e0b0abd0537d3ac8de4fcd58d55c34fcb30ac798b237e0dea2f42e1f2431f6b61594e74807a2fd
7
+ data.tar.gz: 4d8a653613a1782f8542a531abf23d0171e5e30d36d42bee1d756963d645113b9ee151206ae81ba34a053b408df945d4f98e1ef35e23fc2fc47023776961dee8
data/README.md CHANGED
@@ -60,7 +60,7 @@ schema = {
60
60
  result = ActiveGenie::DataExtractor.call(
61
61
  text,
62
62
  schema,
63
- config: { provider: :openai, model: 'gpt-4.1-mini' } # optional
63
+ config: { provider_name: :openai, model: 'gpt-4.1-mini' } # optional
64
64
  )
65
65
  # => {
66
66
  # brand: "Nike",
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.29.1
1
+ 0.30.0
@@ -1,6 +1,6 @@
1
1
  {
2
- "name": "battle_evaluation",
3
- "description": "Evaluate a battle between player_a and player_b using predefined criteria and identify the winner.",
2
+ "name": "comparation_through_debate",
3
+ "description": "Evaluate a debate between player_a and player_b using predefined criteria and identify the winner.",
4
4
  "parameters": {
5
5
  "type": "object",
6
6
  "properties": {
@@ -1,4 +1,4 @@
1
- Based on two players, player_a and player_b, they will battle against each other based on criteria. Criteria are vital as they provide a clear metric to compare the players. Follow these criteria strictly.
1
+ Based on two players, player_a and player_b, they will debate against each other based on criteria. Criteria are vital as they provide a clear metric to compare the players. Follow these criteria strictly.
2
2
 
3
3
  # Steps
4
4
  1. player_a presents their strengths and how they meet the criteria. Max of 100 words.
@@ -1,22 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../clients/unified_client'
3
+ require_relative '../providers/unified_provider'
4
4
 
5
5
  module ActiveGenie
6
- module Battle
7
- # The Generalist class provides a foundation for evaluating battles between two players
6
+ module Comparator
7
+ # The Debate class provides a foundation for evaluating comparators between two players
8
8
  # using AI-powered evaluation. It determines a winner based on specified criteria,
9
9
  # analyzing how well each player meets the requirements.
10
10
  #
11
- # The battle evaluation process compares two players' content against given criteria
11
+ # The comparator evaluation process compares two players' content against given criteria
12
12
  # and returns detailed feedback including the winner and reasoning for the decision.
13
13
  #
14
- # @example Generalist usage with two players and criteria
15
- # Generalist.call("Player A content", "Player B content", "Evaluate keyword usage and pattern matching")
14
+ # @example Debate usage with two players and criteria
15
+ # Debate.call("Player A content", "Player B content", "Evaluate keyword usage and pattern matching")
16
16
  #
17
- class Generalist
18
- BattleResponse = Struct.new(:winner, :loser, :reasoning, :raw, keyword_init: true)
19
-
17
+ class Debate
20
18
  def self.call(...)
21
19
  new(...).call
22
20
  end
@@ -24,8 +22,8 @@ module ActiveGenie
24
22
  # @param player_a [String] The content or submission from the first player
25
23
  # @param player_b [String] The content or submission from the second player
26
24
  # @param criteria [String] The evaluation criteria or rules to assess against
27
- # @param config [Hash] Additional configuration options that modify the battle evaluation behavior
28
- # @return [BattleResponse] The evaluation result containing the winner and reasoning
25
+ # @param config [Hash] Additional configuration options that modify the comparator evaluation behavior
26
+ # @return [ComparatorResponse] The evaluation result containing the winner and reasoning
29
27
  # @return [String] :winner The winner, either player_a or player_b
30
28
  # @return [String] :reasoning Detailed explanation of why the winner was chosen
31
29
  # @return [String] :what_could_be_changed_to_avoid_draw A suggestion on how to avoid a draw
@@ -36,7 +34,7 @@ module ActiveGenie
36
34
  @config = ActiveGenie.configuration.merge(config)
37
35
  end
38
36
 
39
- # @return [BattleResponse] The evaluation result containing the winner and reasoning
37
+ # @return [ComparatorResponse] The evaluation result containing the winner and reasoning
40
38
  def call
41
39
  messages = [
42
40
  { role: 'system', content: PROMPT },
@@ -45,7 +43,7 @@ module ActiveGenie
45
43
  { role: 'user', content: "criteria: #{@criteria}" }
46
44
  ]
47
45
 
48
- response = ::ActiveGenie::Clients::UnifiedClient.function_calling(
46
+ response = ::ActiveGenie::Providers::UnifiedProvider.function_calling(
49
47
  messages,
50
48
  FUNCTION,
51
49
  config: @config
@@ -54,8 +52,8 @@ module ActiveGenie
54
52
  response_formatted(response)
55
53
  end
56
54
 
57
- PROMPT = File.read(File.join(__dir__, 'generalist.prompt.md'))
58
- FUNCTION = JSON.parse(File.read(File.join(__dir__, 'generalist.json')), symbolize_names: true)
55
+ PROMPT = File.read(File.join(__dir__, 'debate.prompt.md'))
56
+ FUNCTION = JSON.parse(File.read(File.join(__dir__, 'debate.json')), symbolize_names: true)
59
57
 
60
58
  private
61
59
 
@@ -66,19 +64,20 @@ module ActiveGenie
66
64
  end
67
65
  reasoning = response['impartial_judge_winner_reasoning']
68
66
 
69
- battle_response = BattleResponse.new(winner:, loser:, reasoning:, raw: response)
70
- log_battle(battle_response)
67
+ comparator_response = ActiveGenie::Comparator::ComparatorResponse.new(winner:, loser:, reasoning:,
68
+ raw: response)
69
+ log_comparator(comparator_response)
71
70
 
72
- battle_response
71
+ comparator_response
73
72
  end
74
73
 
75
- def log_battle(battle_response)
74
+ def log_comparator(comparator_response)
76
75
  @config.logger.call(
77
- code: :battle,
76
+ code: :comparator,
78
77
  player_a: @player_a[0..30],
79
78
  player_b: @player_b[0..30],
80
79
  criteria: @criteria[0..30],
81
- **battle_response.to_h
80
+ **comparator_response.to_h
82
81
  )
83
82
  end
84
83
  end
@@ -1,18 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../clients/unified_client'
4
- require_relative 'generalist'
3
+ require_relative '../providers/unified_provider'
4
+ require_relative 'debate'
5
5
 
6
6
  module ActiveGenie
7
- module Battle
8
- # The Fight class are battle specialized in a fight between two fighters, like martial arts, heroes, characters.
7
+ module Comparator
8
+ # The Fight class are comparation specialized in a fight between two fighters, like martial arts, heroes, characters.
9
9
  # The fight evaluation process simulate a fight using words, techniques, strategies, and reasoning.
10
10
  #
11
11
  # @example Fight usage with two fighters and criteria
12
12
  # Fight.call("Naruto", "Sasuke", "How can win without using jutsu?")
13
13
  #
14
- class Fight < Generalist
15
- # @return [BattleResponse] The evaluation result containing the winner and reasoning
14
+ class Fight < Debate
15
+ # @return [ComparatorResponse] The evaluation result containing the winner and reasoning
16
16
  def call
17
17
  messages = [
18
18
  { role: 'system', content: PROMPT },
@@ -21,7 +21,7 @@ module ActiveGenie
21
21
  { role: 'user', content: "criteria: #{@criteria}" }
22
22
  ]
23
23
 
24
- response = ::ActiveGenie::Clients::UnifiedClient.function_calling(
24
+ response = ::ActiveGenie::Providers::UnifiedProvider.function_calling(
25
25
  messages,
26
26
  FUNCTION,
27
27
  config: @config
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'comparator/debate'
4
+ require_relative 'comparator/fight'
5
+
6
+ module ActiveGenie
7
+ module Comparator
8
+ module_function
9
+
10
+ ComparatorResponse = Struct.new(:winner, :loser, :reasoning, :raw, keyword_init: true)
11
+
12
+ def call(...)
13
+ Debate.call(...)
14
+ end
15
+
16
+ def by_debate(...)
17
+ Debate.call(...)
18
+ end
19
+
20
+ def by_fight(...)
21
+ Fight.call(...)
22
+ end
23
+ end
24
+ end
@@ -3,7 +3,7 @@
3
3
  module ActiveGenie
4
4
  module Config
5
5
  # rubocop:disable Lint/EmptyClass
6
- class ScoringConfig
6
+ class ComparatorConfig
7
7
  end
8
8
  # rubocop:enable Lint/EmptyClass
9
9
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ActiveGenie
4
4
  module Config
5
- class DataExtractorConfig
5
+ class ExtractorConfig
6
6
  attr_accessor :with_explanation, :min_accuracy, :verbose
7
7
 
8
8
  def initialize
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ActiveGenie
4
4
  module Config
5
- class FactoryConfig
5
+ class ListerConfig
6
6
  attr_accessor :number_of_items
7
7
 
8
8
  def initialize
@@ -4,13 +4,13 @@ module ActiveGenie
4
4
  module Config
5
5
  class LlmConfig
6
6
  attr_accessor :model, :temperature, :max_tokens, :max_retries, :retry_delay,
7
- :model_tier, :read_timeout, :open_timeout, :client
8
- attr_reader :provider
7
+ :model_tier, :read_timeout, :open_timeout, :provider
8
+ attr_reader :provider_name
9
9
 
10
10
  def initialize
11
11
  @model = nil
12
+ @provider_name = nil
12
13
  @provider = nil
13
- @client = nil
14
14
  @temperature = 0
15
15
  @max_tokens = 4096
16
16
  @max_retries = nil
@@ -20,10 +20,10 @@ module ActiveGenie
20
20
  @open_timeout = nil
21
21
  end
22
22
 
23
- def provider=(provider)
24
- return if provider.nil? || provider.empty?
23
+ def provider_name=(provider_name)
24
+ return if provider_name.nil? || provider_name.empty?
25
25
 
26
- @provider = provider.to_s.downcase.strip.to_sym
26
+ @provider_name = provider_name.to_s.downcase.strip.to_sym
27
27
  end
28
28
 
29
29
  def merge(config_params = {})
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ActiveGenie
4
4
  module Config
5
- class RankingConfig
5
+ class RankerConfig
6
6
  attr_accessor :score_variation_threshold
7
7
 
8
8
  def initialize
@@ -3,7 +3,7 @@
3
3
  module ActiveGenie
4
4
  module Config
5
5
  # rubocop:disable Lint/EmptyClass
6
- class BattleConfig
6
+ class ScorerConfig
7
7
  end
8
8
  # rubocop:enable Lint/EmptyClass
9
9
  end
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'config/providers_config'
4
- require_relative 'config/log_config'
5
- require_relative 'config/ranking_config'
6
- require_relative 'config/scoring_config'
7
- require_relative 'config/data_extractor_config'
8
- require_relative 'config/battle_config'
9
- require_relative 'config/llm_config'
10
- require_relative 'config/factory_config'
3
+ require_relative 'configs/comparator_config'
4
+ require_relative 'configs/extractor_config'
5
+ require_relative 'configs/lister_config'
6
+ require_relative 'configs/llm_config'
7
+ require_relative 'configs/log_config'
8
+ require_relative 'configs/providers_config'
9
+ require_relative 'configs/ranker_config'
10
+ require_relative 'configs/scorer_config'
11
11
 
12
12
  module ActiveGenie
13
13
  class Configuration
@@ -19,24 +19,24 @@ module ActiveGenie
19
19
  @providers ||= Config::ProvidersConfig.new
20
20
  end
21
21
 
22
- def ranking
23
- @ranking ||= Config::RankingConfig.new
22
+ def ranker
23
+ @ranker ||= Config::RankerConfig.new
24
24
  end
25
25
 
26
- def scoring
27
- @scoring ||= Config::ScoringConfig.new
26
+ def scorer
27
+ @scorer ||= Config::ScorerConfig.new
28
28
  end
29
29
 
30
- def data_extractor
31
- @data_extractor ||= Config::DataExtractorConfig.new
30
+ def extractor
31
+ @extractor ||= Config::ExtractorConfig.new
32
32
  end
33
33
 
34
- def battle
35
- @battle ||= Config::BattleConfig.new
34
+ def comparator
35
+ @comparator ||= Config::ComparatorConfig.new
36
36
  end
37
37
 
38
- def factory
39
- @factory ||= Config::FactoryConfig.new
38
+ def lister
39
+ @lister ||= Config::ListerConfig.new
40
40
  end
41
41
 
42
42
  def llm
@@ -47,7 +47,7 @@ module ActiveGenie
47
47
  @logger ||= ActiveGenie::Logger.new(log_config: log)
48
48
  end
49
49
 
50
- SUB_CONFIGS = %w[log providers llm ranking scoring data_extractor battle factory].freeze
50
+ SUB_CONFIGS = %w[log providers llm ranker scorer extractor comparator lister].freeze
51
51
 
52
52
  def merge(config_params = {})
53
53
  return config_params if config_params.is_a?(Configuration)
@@ -17,7 +17,7 @@ module ActiveGenie
17
17
 
18
18
  2. Or pass configuration directly to the method call:
19
19
  ```ruby
20
- ActiveGenie::DataExtraction.call(
20
+ ActiveGenie::Extraction.call(
21
21
  arg1,
22
22
  arg2,
23
23
  config: {
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../clients/unified_client'
3
+ require_relative '../providers/unified_provider'
4
4
 
5
5
  module ActiveGenie
6
- module DataExtractor
7
- class Generalist
6
+ module Extractor
7
+ class Explanation
8
8
  def self.call(...)
9
9
  new(...).call
10
10
  end
@@ -25,7 +25,7 @@ module ActiveGenie
25
25
  # age: { type: 'integer', description: 'Age in years' }
26
26
  # }
27
27
  # text = "John Doe is 25 years old"
28
- # DataExtractor.call(text, schema)
28
+ # Extractor.with_explanation(text, schema)
29
29
  # # => { name: "John Doe", name_explanation: "Found directly in text",
30
30
  # # age: 25, age_explanation: "Explicitly stated as 25 years old" }
31
31
  def initialize(text, data_to_extract, config: {})
@@ -42,7 +42,7 @@ module ActiveGenie
42
42
 
43
43
  properties = data_to_extract_with_explanation
44
44
 
45
- function = JSON.parse(File.read(File.join(__dir__, 'generalist.json')), symbolize_names: true)
45
+ function = JSON.parse(File.read(File.join(__dir__, 'explanation.json')), symbolize_names: true)
46
46
  function[:parameters][:properties] = properties
47
47
  function[:parameters][:required] = properties.keys
48
48
 
@@ -54,7 +54,7 @@ module ActiveGenie
54
54
  private
55
55
 
56
56
  def data_to_extract_with_explanation
57
- return @data_to_extract unless @config.data_extractor.with_explanation
57
+ return @data_to_extract unless @config.extractor.with_explanation
58
58
 
59
59
  with_explanation = {}
60
60
 
@@ -81,7 +81,7 @@ module ActiveGenie
81
81
  end
82
82
 
83
83
  def function_calling(messages, function)
84
- response = ::ActiveGenie::Clients::UnifiedClient.function_calling(
84
+ response = ::ActiveGenie::Providers::UnifiedProvider.function_calling(
85
85
  messages,
86
86
  function,
87
87
  config: @config
@@ -89,7 +89,7 @@ module ActiveGenie
89
89
 
90
90
  @config.logger.call(
91
91
  {
92
- code: :data_extractor,
92
+ code: :extractor,
93
93
  text: @text[0..30],
94
94
  data_to_extract: function[:parameters][:properties],
95
95
  extracted_data: response
@@ -100,7 +100,7 @@ module ActiveGenie
100
100
  end
101
101
 
102
102
  def simplify_response(response)
103
- return response if @config.data_extractor.verbose
103
+ return response if @config.extractor.verbose
104
104
 
105
105
  simplified_response = {}
106
106
 
@@ -115,11 +115,11 @@ module ActiveGenie
115
115
  end
116
116
 
117
117
  def min_accuracy
118
- @config.data_extractor.min_accuracy # default 70
118
+ @config.extractor.min_accuracy # default 70
119
119
  end
120
120
 
121
121
  def prompt
122
- File.read(File.join(__dir__, 'generalist.prompt.md'))
122
+ File.read(File.join(__dir__, 'explanation.prompt.md'))
123
123
  end
124
124
  end
125
125
  end
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'generalist'
3
+ require_relative 'explanation'
4
4
 
5
5
  module ActiveGenie
6
- module DataExtractor
7
- class FromInformal
6
+ module Extractor
7
+ class Litote
8
8
  def self.call(...)
9
9
  new(...).call
10
10
  end
@@ -24,7 +24,7 @@ module ActiveGenie
24
24
  # @example Analyze text with litote
25
25
  # text = "The weather isn't bad today"
26
26
  # schema = { mood: { type: 'string', description: 'The mood of the message' } }
27
- # DataExtractor.from_informal(text, schema)
27
+ # Extractor.with_litote(text, schema)
28
28
  # # => { mood: "positive", mood_explanation: "Speaker views weather favorably",
29
29
  # # message_litote: true,
30
30
  # # litote_rephrased: "The weather is good today" }
@@ -35,7 +35,7 @@ module ActiveGenie
35
35
  end
36
36
 
37
37
  def call
38
- response = Generalist.call(@text, data_to_extract_with_litote, config: @config)
38
+ response = Generalist.call(@text, extract_with_litote, config: @config)
39
39
 
40
40
  if response[:message_litote]
41
41
  response = Generalist.call(response[:litote_rephrased], @data_to_extract, config: @config)
@@ -46,8 +46,8 @@ module ActiveGenie
46
46
 
47
47
  private
48
48
 
49
- def data_to_extract_with_litote
50
- parameters = JSON.parse(File.read(File.join(__dir__, 'from_informal.json')), symbolize_names: true)
49
+ def extract_with_litote
50
+ parameters = JSON.parse(File.read(File.join(__dir__, 'with_litote.json')), symbolize_names: true)
51
51
 
52
52
  @data_to_extract.merge(parameters)
53
53
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'extractor/explanation'
4
+ require_relative 'extractor/litote'
5
+
6
+ module ActiveGenie
7
+ module Extractor
8
+ module_function
9
+
10
+ def call(...)
11
+ Explanation.call(...)
12
+ end
13
+
14
+ def with_explanation(...)
15
+ Explanation.call(...)
16
+ end
17
+
18
+ def with_litote(...)
19
+ Litote.call(...)
20
+ end
21
+ end
22
+ end
@@ -3,8 +3,8 @@
3
3
  require_relative '../clients/unified_client'
4
4
 
5
5
  module ActiveGenie
6
- module Factory
7
- # The Factory::Feud class provides a foundation for generating a list of items for a given theme
6
+ module Lister
7
+ # The Lister::Feud class provides a foundation for generating a list of items for a given theme
8
8
  #
9
9
  # @example Feud usage with two players and criteria
10
10
  # Feud.call("Industries that are most likely to be affected by climate change")
@@ -15,14 +15,14 @@ module ActiveGenie
15
15
  end
16
16
 
17
17
  # @param theme [String] The theme for the feud
18
- # @param config [Hash] Additional configuration options that modify the battle evaluation behavior
18
+ # @param config [Hash] Additional configuration options
19
19
  # @return [Array of strings] List of items
20
20
  def initialize(theme, config: {})
21
21
  @theme = theme
22
22
  @config = ActiveGenie.configuration.merge(config)
23
23
  end
24
24
 
25
- # @return [Array] The list of items
25
+ # @return [Array of strings] The list of items
26
26
  def call
27
27
  messages = [
28
28
  { role: 'system', content: PROMPT },
@@ -37,7 +37,7 @@ module ActiveGenie
37
37
  )
38
38
 
39
39
  log_feud(response)
40
- response['items']
40
+ response['items'] || []
41
41
  end
42
42
 
43
43
  PROMPT = File.read(File.join(__dir__, 'feud.prompt.md'))
@@ -46,7 +46,7 @@ module ActiveGenie
46
46
  private
47
47
 
48
48
  def number_of_items
49
- @config.factory.number_of_items
49
+ @config.lister.number_of_items
50
50
  end
51
51
 
52
52
  def log_feud(response)
@@ -0,0 +1,20 @@
1
+ Emulate the game "Family Feud": For a given theme, reason about the general public's most common answers impersonating a survey of average people's opinions and generate an ordered, survey-style answer list.
2
+
3
+ - Before producing the answer list, internally consider: If a group of average people were surveyed on this theme, which answers would be mentioned most frequently?.
4
+
5
+ # Output Format
6
+
7
+ A numbered list in plain text
8
+
9
+ # Example
10
+
11
+ Example for the theme "Favorite Fast Foods":
12
+ 1. Pizza
13
+ 2. Hamburgers
14
+ 3. Hot Dogs
15
+ 4. French Fries
16
+
17
+ # Notes
18
+
19
+ - Always impersonate a "Family Feud" survey: order is critical most likely answers should come first.
20
+ - Judge each item by their general public reputation and cultural impact.
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../clients/unified_client'
4
+
5
+ module ActiveGenie
6
+ module Lister
7
+ # The Juries class intelligently suggests appropriate jury 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 jury roles with complementary expertise and perspectives.
13
+ #
14
+ # @example Getting jury for technical content
15
+ # Juries.call("Technical documentation about API design",
16
+ # "Evaluate technical accuracy and clarity")
17
+ # # => { jury1: "API Architect", jury2: "Technical Writer",
18
+ # # jury3: "Developer Advocate", reasoning: "..." }
19
+ #
20
+ class Juries
21
+ def self.call(...)
22
+ new(...).call
23
+ end
24
+
25
+ # Initializes a new jury recommendation instance
26
+ #
27
+ # @param text [String] The text content to analyze for jury recommendations
28
+ # @param criteria [String] The evaluation criteria that will guide jury 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: "<criteria> #{@criteria}</criteria>" },
40
+ { role: 'user', content: "<text-to-score>#{@text}</text-to-score>" }
41
+ ]
42
+
43
+ function = {
44
+ name: 'identify_jury',
45
+ description: 'Discover a list of juries based on the text and given criteria.',
46
+ parameters: {
47
+ type: 'object',
48
+ properties: {
49
+ reasoning: { type: 'string' },
50
+ juries: {
51
+ type: 'array',
52
+ description: 'The list of best juries',
53
+ items: {
54
+ type: 'string'
55
+ }
56
+ }
57
+ },
58
+ required: %w[reasoning juries]
59
+ }
60
+ }
61
+
62
+ result = client.function_calling(
63
+ messages,
64
+ function,
65
+ config: @config
66
+ )
67
+
68
+ result['juries'] || []
69
+ end
70
+
71
+ private
72
+
73
+ def client
74
+ ::ActiveGenie::Clients::UnifiedClient
75
+ end
76
+
77
+ def prompt
78
+ @prompt ||= File.read(File.join(__dir__, 'juries.prompt.md'))
79
+ end
80
+ end
81
+ end
82
+ end