active_genie 0.27.1 → 0.28.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 752b254545d62f5e618ca56935dca6e89dce29e0353af995f96f276ac2852662
4
- data.tar.gz: 7df0c3373970c5da1cf12cea004839a17e8489bf52b2e3d14dedcd61c04f91cc
3
+ metadata.gz: a2cb129cd0e5a4f5f0f1daf6ed3cb26a4dab4d10cb56bbfcc419656c2797347a
4
+ data.tar.gz: df1fe343d28aeb72f8fe30dc53bdecd860e8bd44ae009a086c7ad502da77bfd8
5
5
  SHA512:
6
- metadata.gz: dd4d86d0bd8a8be3c44355d997883db01b424a070ab51b555e1f1b8a10c3c0fbc46f8afc4c5b8764b4462b9e1e5e711be38ad25a77a21e26b3ea55668fd61ccf
7
- data.tar.gz: bb69781f7f05a339833e553ab7cb1dc805044cda7fb2615712c7fea3f5ce9f71dce82b38edd1b70aa6fca36d6ef6538c573b34058e80498a0a5a65bf6fd58821
6
+ metadata.gz: 1449245c23bcc2c5f3c4266712c67b202e4e2c6724ee9f283bd81d76b2daedc4bc06ee524ed8d959d13f31062b393b5f7cfb7f46721888efb8d89bbd4d8a12b3
7
+ data.tar.gz: 649a20ee71c55d8486d8264b641974c3222baa477133d6924ba039f2ef24f9185d2b60b2f37f7d2ff498b87aaed09b78815feb3bbc52c92613eb810de2c59c2c
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  [![Gem Version](https://badge.fury.io/rb/active_genie.svg?icon=si%3Arubygems)](https://badge.fury.io/rb/active_genie)
5
5
  [![Ruby](https://github.com/roriz/active_genie/actions/workflows/benchmark.yml/badge.svg)](https://github.com/roriz/active_genie/actions/workflows/benchmark.yml)
6
6
 
7
- ActiveGenie is a developer-first library for GenAI workflows, designed to help you compare, rank, and score LLM outputs with consistency and model-agnostic flexibility. Think of it as the Lodash for GenAI: built for real value, consistent results, and freedom from vendor lock-in. It solves the biggest pain in GenAI today: getting predictable, trustworthy answers across use cases, models, and providers.
7
+ ActiveGenie is a developer-first library for GenAI workflows, designed to help you extract, compare, score, and rank with consistency and model-agnostic. Think of it as the Lodash for GenAI: built for real value, consistent results, and freedom from vendor lock-in. It solves the biggest pain in GenAI today: getting predictable, trustworthy answers across use cases, models, and providers.
8
8
 
9
9
  Behind the scenes, a custom benchmarking system keeps everything consistent across LLM vendors and versions, release after release.
10
10
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.27.1
1
+ 0.28.2
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "fight_evaluation",
3
+ "description": "Evaluate a fight between player_a and player_b using predefined criteria and identify the winner.",
4
+ "parameters": {
5
+ "type": "object",
6
+ "properties": {
7
+ "player_a_introduction": {
8
+ "type": "string",
9
+ "description": "player_a introduces their name and fighting style. Max of 100 words."
10
+ },
11
+ "player_b_introduction": {
12
+ "type": "string",
13
+ "description": "player_b introduces their name and fighting style. Max of 100 words."
14
+ },
15
+ "player_a_turn_1": {
16
+ "type": "string",
17
+ "description": "player_a makes their first turn. Max of 100 words."
18
+ },
19
+ "player_b_turn_1": {
20
+ "type": "string",
21
+ "description": "player_b makes their first turn. Max of 100 words."
22
+ },
23
+ "player_a_turn_2": {
24
+ "type": "string",
25
+ "description": "player_a makes their second turn. Max of 100 words."
26
+ },
27
+ "player_b_turn_2": {
28
+ "type": "string",
29
+ "description": "player_b makes their second turn. Max of 100 words."
30
+ },
31
+ "player_a_turn_3": {
32
+ "type": "string",
33
+ "description": "player_a makes their third turn. Max of 100 words."
34
+ },
35
+ "player_b_turn_3": {
36
+ "type": "string",
37
+ "description": "player_b makes their third turn. Max of 100 words."
38
+ },
39
+ "player_a_turn_4": {
40
+ "type": "string",
41
+ "description": "player_a makes their fourth turn. Max of 100 words."
42
+ },
43
+ "player_b_turn_4": {
44
+ "type": "string",
45
+ "description": "player_b makes their fourth turn. Max of 100 words."
46
+ },
47
+ "impartial_judge_winner_reasoning": {
48
+ "type": "string",
49
+ "description": "The detailed reasoning about why the impartial judge chose the winner. Max of 100 words."
50
+ },
51
+ "impartial_judge_winner": {
52
+ "type": "string",
53
+ "description": "Who is the winner based on the impartial judge reasoning?",
54
+ "enum": ["player_a", "player_b"]
55
+ }
56
+ },
57
+ "required": ["player_a_introduction", "player_b_introduction", "player_a_turn_1", "player_b_turn_1",
58
+ "player_a_turn_2", "player_b_turn_2", "player_a_turn_3", "player_b_turn_3", "player_a_turn_4",
59
+ "player_b_turn_4", "impartial_judge_winner_reasoning", "impartial_judge_winner"]
60
+ }
61
+ }
@@ -0,0 +1,41 @@
1
+ Create a dynamic, back-and-forth verbal match between two fighters, given their names and preferred fighting styles. In this match, fighters do not physically battle, instead, they engage in a spoken confrontation, each describing and boasting about their fighting style, how their techniques would outmaneuver, counter, or overwhelm the other's approach. Ensure that each verbal exchange details:
2
+ - The technique or strategy the speaker would use.
3
+ - How that move is superior to the opponent’s last stated tactic.
4
+ - How the opponent might respond, and what reply the speaker would have.
5
+
6
+ Continue this turn-based verbal sparring until each fighter has made at least two exchanges, and escalate the creativity and competitiveness of their boasts with each round.
7
+
8
+ Explicit steps:
9
+ - Begin by introducing both fighters and naming their styles.
10
+ - Alternate dialogue, with each fighter explaining their tactical move, then the intended counter or rebuttal, going at least two cycles each.
11
+ - Ensure reasoning (the logic or explanation behind each technique and counter) always precedes the conclusion or actual "move" or taunt.
12
+ - Close with a final statement or taunt from each fighter, maintaining the competitive tone.
13
+ - Make the dialogue lively and imaginative, using language consistent with fighters boasting or challenging each other.
14
+
15
+ Output Format:
16
+ - Structure as a script, with each turn labeled by the fighter’s name.
17
+ - Each line should start with the reasoning (the advantage or thinking), then the boast or attack, in the order: reasoning first, conclusion second.
18
+ - At least two cycles per fighter, alternating turns; roughly 6-8 lines, but can be longer for complexity.
19
+ - No code blocks, use markdown for formatting.
20
+
21
+ Example:
22
+
23
+ Example Input:
24
+ Fighter One: Master Crane, Style: Crane Kung Fu
25
+ Fighter Two: Iron Ox, Style: Ox Bull Charge
26
+
27
+ Example Output:
28
+ Master Crane: My Crane Kung Fu relies on lightness and precision, striking where your heavy blows simply can't reach. Your Ox Bull Charge is powerful, but too direct, I'd dance aside and tap your pressure points before you even turn.
29
+ Iron Ox: While speed is impressive, strength dominates in a real fight. My Ox Bull Charge would absorb your nimble attacks, and my sheer mass pins you before you could even flutter away!
30
+ Master Crane: Adaptability ensures survival. As you lumber forward, I use your momentum to redirect you, your own strength becomes your undoing as you stumble into my calculated traps.
31
+ Iron Ox: Anticipating trickery, I’d anchor myself and force your traps to fail. Your frailty means that, once caught, no escape, one bear hug from me, and there’s no dancing away.
32
+ Master Crane: Patience and timing can unravel even the strongest grip. Just as you squeeze, a swift strike to your nerve centers will loosen your hold before you know it.
33
+ Iron Ox: Endurance outlasts finesse; those quick hits may sting, but in the end, the last one standing is the one who never falls, and I never fall!
34
+
35
+ (Real examples can be longer, more diverse in style banter, and use specific technique names or metaphors!)
36
+
37
+ Important Considerations:
38
+ - Always have the reasoning/strategy before the actual attack or boast in each utterance.
39
+ - Maintain the turn order and competitive dialogue style.
40
+
41
+ *Reminder: The main objective is to script an imaginative, turn-based verbal contest between two stylized fighters, with detailed reasoning and taunts about their style advantages, as instructed above.*
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../clients/unified_client'
4
+ require_relative 'generalist'
5
+
6
+ module ActiveGenie
7
+ module Battle
8
+ # The Fight class are battle specialized in a fight between two fighters, like martial arts, heroes, characters.
9
+ # The fight evaluation process simulate a fight using words, techniques, strategies, and reasoning.
10
+ #
11
+ # @example Fight usage with two fighters and criteria
12
+ # Fight.call("Naruto", "Sasuke", "How can win without using jutsu?")
13
+ #
14
+ class Fight < Generalist
15
+ # @return [BattleResponse] The evaluation result containing the winner and reasoning
16
+ def call
17
+ messages = [
18
+ { role: 'system', content: PROMPT },
19
+ { role: 'user', content: "criteria: #{@criteria}" },
20
+ { role: 'user', content: "player_a: #{@player_a}" },
21
+ { role: 'user', content: "player_b: #{@player_b}" }
22
+ ]
23
+
24
+ response = ::ActiveGenie::Clients::UnifiedClient.function_calling(
25
+ messages,
26
+ FUNCTION,
27
+ config: @config
28
+ )
29
+
30
+ response_formatted(response)
31
+ end
32
+
33
+ PROMPT = File.read(File.join(__dir__, 'fight.prompt.md'))
34
+ FUNCTION = JSON.parse(File.read(File.join(__dir__, 'fight.json')), symbolize_names: true)
35
+ end
36
+ end
37
+ end
@@ -15,6 +15,8 @@ module ActiveGenie
15
15
  # Generalist.call("Player A content", "Player B content", "Evaluate keyword usage and pattern matching")
16
16
  #
17
17
  class Generalist
18
+ BattleResponse = Struct.new(:winner, :loser, :reasoning, :raw, keyword_init: true)
19
+
18
20
  def self.call(...)
19
21
  new(...).call
20
22
  end
@@ -23,7 +25,7 @@ module ActiveGenie
23
25
  # @param player_b [String] The content or submission from the second player
24
26
  # @param criteria [String] The evaluation criteria or rules to assess against
25
27
  # @param config [Hash] Additional configuration options that modify the battle evaluation behavior
26
- # @return [Hash] The evaluation result containing the winner and reasoning
28
+ # @return [BattleResponse] The evaluation result containing the winner and reasoning
27
29
  # @return [String] :winner The winner, either player_a or player_b
28
30
  # @return [String] :reasoning Detailed explanation of why the winner was chosen
29
31
  # @return [String] :what_could_be_changed_to_avoid_draw A suggestion on how to avoid a draw
@@ -34,6 +36,7 @@ module ActiveGenie
34
36
  @config = ActiveGenie.configuration.merge(config)
35
37
  end
36
38
 
39
+ # @return [BattleResponse] The evaluation result containing the winner and reasoning
37
40
  def call
38
41
  messages = [
39
42
  { role: 'system', content: PROMPT },
@@ -48,15 +51,6 @@ module ActiveGenie
48
51
  config: @config
49
52
  )
50
53
 
51
- @config.logger.call({
52
- code: :battle,
53
- player_a: @player_a[0..30],
54
- player_b: @player_b[0..30],
55
- criteria: @criteria[0..30],
56
- winner: response['impartial_judge_winner'],
57
- reasoning: response['impartial_judge_winner_reasoning']
58
- })
59
-
60
54
  response_formatted(response)
61
55
  end
62
56
 
@@ -66,13 +60,26 @@ module ActiveGenie
66
60
  private
67
61
 
68
62
  def response_formatted(response)
69
- winner = response['impartial_judge_winner']
70
- loser = case winner
71
- when 'player_a' then 'player_b'
72
- when 'player_b' then 'player_a'
73
- end
63
+ winner, loser = case response['impartial_judge_winner']
64
+ when 'player_a' then [@player_a, @player_b]
65
+ when 'player_b' then [@player_b, @player_a]
66
+ end
67
+ reasoning = response['impartial_judge_winner_reasoning']
68
+
69
+ battle_response = BattleResponse.new(winner:, loser:, reasoning:, raw: response)
70
+ log_battle(battle_response)
74
71
 
75
- { 'winner' => winner, 'loser' => loser, 'reasoning' => response['impartial_judge_winner_reasoning'] }
72
+ battle_response
73
+ end
74
+
75
+ def log_battle(battle_response)
76
+ @config.logger.call(
77
+ code: :battle,
78
+ player_a: @player_a[0..30],
79
+ player_b: @player_b[0..30],
80
+ criteria: @criteria[0..30],
81
+ **battle_response.to_h
82
+ )
76
83
  end
77
84
  end
78
85
  end
@@ -1,18 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'battle/generalist'
4
+ require_relative 'battle/fight'
4
5
 
5
6
  module ActiveGenie
6
7
  # See the [Battle README](lib/active_genie/battle/README.md) for more information.
7
8
  module Battle
8
9
  module_function
9
10
 
11
+ def generalist(...)
12
+ Generalist.call(...)
13
+ end
14
+
10
15
  def call(...)
11
16
  Generalist.call(...)
12
17
  end
13
18
 
14
- def generalist(...)
19
+ def compare(...)
15
20
  Generalist.call(...)
16
21
  end
22
+
23
+ def fight(...)
24
+ Fight.call(...)
25
+ end
17
26
  end
18
27
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_genie
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.27.1
4
+ version: 0.28.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Radamés Roriz
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-06-30 00:00:00.000000000 Z
11
+ date: 2025-07-12 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  ActiveGenie is a Ruby gem that helps developers build reliable, future-proof GenAI features without worrying about changing models, prompts, or providers. Like Lodash for GenAI, it offers simple, reusable modules for tasks like data extraction, scoring, and ranking, so you can focus on your app’s logic, not the shifting AI landscape.
@@ -24,7 +24,9 @@ files:
24
24
  - VERSION
25
25
  - lib/active_genie.rb
26
26
  - lib/active_genie/battle.rb
27
- - lib/active_genie/battle/README.md
27
+ - lib/active_genie/battle/fight.json
28
+ - lib/active_genie/battle/fight.prompt.md
29
+ - lib/active_genie/battle/fight.rb
28
30
  - lib/active_genie/battle/generalist.json
29
31
  - lib/active_genie/battle/generalist.prompt.md
30
32
  - lib/active_genie/battle/generalist.rb
@@ -1,39 +0,0 @@
1
- # Battle
2
- AI-powered battle evaluation system that determines winners between two players based on specified criteria.
3
-
4
- ## Features
5
- - Content comparison - Evaluate submissions from two players against defined criteria
6
- - Objective analysis - AI-powered assessment of how well each player meets requirements
7
- - Detailed reasoning - Comprehensive explanation of why a winner was chosen
8
- - Draw avoidance - Suggestions on how to modify content to avoid draws
9
- - Flexible input - Support for both string content and structured data with content field
10
-
11
- ## Basic Usage
12
- Evaluate a battle between two players with simple text content:
13
-
14
- ```ruby
15
- player_a = "Implementation uses dependency injection for better testability"
16
- player_b = "Code has high test coverage but tightly coupled components"
17
- criteria = "Evaluate code quality and maintainability"
18
-
19
- result = ActiveGenie::Battle.call(player_a, player_b, criteria)
20
- # => {
21
- # winner_player: "Implementation uses dependency injection for better testability",
22
- # reasoning: "Player A's implementation demonstrates better maintainability through dependency injection,
23
- # which allows for easier testing and component replacement. While Player B has good test coverage,
24
- # the tight coupling makes the code harder to maintain and modify.",
25
- # what_could_be_changed_to_avoid_draw: "Focus on specific architectural patterns and design principles"
26
- # }
27
- ```
28
-
29
- ## Interface
30
- ### .call(player_a, player_b, criteria, config: {})
31
- - `player_a` [String, Hash] - The content or submission from the first player
32
- - `player_b` [String, Hash] - The content or submission from the second player
33
- - `criteria` [String] - The evaluation criteria or rules to assess against
34
- - `config` [Hash] - Additional configuration config that modify the battle evaluation behavior
35
-
36
- Returns a Hash containing:
37
- - `winner_player` [String, Hash] - The winning player's content (either player_a or player_b)
38
- - `reasoning` [String] - Detailed explanation of why the winner was chosen
39
- - `what_could_be_changed_to_avoid_draw` [String] - A suggestion on how to avoid a draw