active_genie 0.30.0 → 0.30.1
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/VERSION +1 -1
- data/lib/active_genie/configs/llm_config.rb +2 -1
- data/lib/active_genie/configs/providers_config.rb +1 -1
- data/lib/active_genie/extractor/litote.rb +3 -3
- data/lib/active_genie/lister/feud.rb +2 -2
- data/lib/active_genie/lister/juries.rb +2 -2
- data/lib/active_genie/providers/base_provider.rb +12 -5
- data/lib/active_genie/providers/unified_provider.rb +2 -2
- data/lib/active_genie/ranker/elo.rb +3 -2
- data/lib/active_genie/ranker/free_for_all.rb +3 -3
- data/lib/active_genie/ranker/scoring.rb +5 -3
- data/lib/active_genie/ranker/tournament.rb +7 -8
- data/lib/active_genie/scorer/jury_bench.rb +2 -2
- data/lib/active_genie/utils/fiber_by_batch.rb +21 -0
- metadata +19 -4
- /data/lib/active_genie/scorer/{jury_bench.md → jury_bench.prompt.md} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5673a34f1cd8c2b2a9a884a73ded1bf12e28f4d29ab5f351ecb3374bc9ca0617
|
4
|
+
data.tar.gz: 7ca0bc8d8bdc8b6d3618bd5f22bd13f80a9ac4399fa0282e6b11a461beb1cc1d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f174076b72df558ef82d3834043ae3299ab1aa41d235d6b547cbee45d21804295b87729a92dda51910d37a076f954fb8b4e2460443c1814f73221b5663255d96
|
7
|
+
data.tar.gz: 503840469d6a67206d67d3f5b27f73803cb736cd4c826e7ea4b4575f1f702b7875fdeb979a875c29e88d5b06d9d18b49c94ae4f957f13563289428bdc1d83cd0
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.30.
|
1
|
+
0.30.1
|
@@ -4,7 +4,7 @@ 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, :provider
|
7
|
+
:model_tier, :read_timeout, :open_timeout, :provider, :max_fibers
|
8
8
|
attr_reader :provider_name
|
9
9
|
|
10
10
|
def initialize
|
@@ -18,6 +18,7 @@ module ActiveGenie
|
|
18
18
|
@model_tier = 'lower_tier'
|
19
19
|
@read_timeout = nil
|
20
20
|
@open_timeout = nil
|
21
|
+
@max_fibers = 10
|
21
22
|
end
|
22
23
|
|
23
24
|
def provider_name=(provider_name)
|
@@ -35,10 +35,10 @@ module ActiveGenie
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def call
|
38
|
-
response =
|
38
|
+
response = Explanation.call(@text, extract_with_litote, config: @config)
|
39
39
|
|
40
40
|
if response[:message_litote]
|
41
|
-
response =
|
41
|
+
response = Explanation.call(response[:litote_rephrased], @data_to_extract, config: @config)
|
42
42
|
end
|
43
43
|
|
44
44
|
response
|
@@ -47,7 +47,7 @@ module ActiveGenie
|
|
47
47
|
private
|
48
48
|
|
49
49
|
def extract_with_litote
|
50
|
-
parameters = JSON.parse(File.read(File.join(__dir__, '
|
50
|
+
parameters = JSON.parse(File.read(File.join(__dir__, 'litote.json')), symbolize_names: true)
|
51
51
|
|
52
52
|
@data_to_extract.merge(parameters)
|
53
53
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '../
|
3
|
+
require_relative '../providers/unified_provider'
|
4
4
|
|
5
5
|
module ActiveGenie
|
6
6
|
module Lister
|
@@ -30,7 +30,7 @@ module ActiveGenie
|
|
30
30
|
{ role: 'user', content: "theme: #{@theme}" }
|
31
31
|
]
|
32
32
|
|
33
|
-
response = ::ActiveGenie::
|
33
|
+
response = ::ActiveGenie::Providers::UnifiedProvider.function_calling(
|
34
34
|
messages,
|
35
35
|
FUNCTION,
|
36
36
|
config: @config
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '../
|
3
|
+
require_relative '../providers/unified_provider'
|
4
4
|
|
5
5
|
module ActiveGenie
|
6
6
|
module Lister
|
@@ -71,7 +71,7 @@ module ActiveGenie
|
|
71
71
|
private
|
72
72
|
|
73
73
|
def client
|
74
|
-
::ActiveGenie::
|
74
|
+
::ActiveGenie::Providers::UnifiedProvider
|
75
75
|
end
|
76
76
|
|
77
77
|
def prompt
|
@@ -1,9 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'net/http'
|
4
|
+
|
3
5
|
module ActiveGenie
|
4
6
|
module Providers
|
5
7
|
class BaseProvider
|
6
|
-
class
|
8
|
+
class ProviderUnknownError < StandardError; end
|
9
|
+
class ProviderServerError < StandardError; end
|
7
10
|
|
8
11
|
DEFAULT_HEADERS = {
|
9
12
|
'Content-Type': 'application/json',
|
@@ -81,7 +84,7 @@ module ActiveGenie
|
|
81
84
|
|
82
85
|
response = http_request(request, uri)
|
83
86
|
|
84
|
-
raise
|
87
|
+
raise ProviderUnknownError, "Unexpected response: #{response.code} - #{response.body}" unless response.is_a?(Net::HTTPSuccess)
|
85
88
|
|
86
89
|
parsed_response = parse_response(response)
|
87
90
|
|
@@ -143,7 +146,7 @@ module ActiveGenie
|
|
143
146
|
begin
|
144
147
|
JSON.parse(response.body)
|
145
148
|
rescue JSON::ParserError => e
|
146
|
-
raise
|
149
|
+
raise ProviderUnknownError, "Failed to parse JSON response: #{e.message}"
|
147
150
|
end
|
148
151
|
end
|
149
152
|
|
@@ -169,8 +172,12 @@ module ActiveGenie
|
|
169
172
|
retries = 0
|
170
173
|
|
171
174
|
begin
|
172
|
-
yield
|
173
|
-
|
175
|
+
response = yield
|
176
|
+
|
177
|
+
raise ProviderServerError, "Provider server error: #{response.code} - #{response.body}" if !response.is_a?(Net::HTTPSuccess) && response.code.to_i >= 500
|
178
|
+
|
179
|
+
response
|
180
|
+
rescue Net::OpenTimeout, Net::ReadTimeout, Errno::ECONNREFUSED, ProviderServerError => e
|
174
181
|
raise if retries > max_retries
|
175
182
|
|
176
183
|
sleep_time = retry_delay * (2**retries)
|
@@ -10,7 +10,7 @@ module ActiveGenie
|
|
10
10
|
module Providers
|
11
11
|
class UnifiedProvider
|
12
12
|
class << self
|
13
|
-
|
13
|
+
PROVIDER_NAME_TO_PROVIDER = {
|
14
14
|
openai: OpenaiProvider,
|
15
15
|
anthropic: AnthropicProvider,
|
16
16
|
google: GoogleProvider,
|
@@ -19,7 +19,7 @@ module ActiveGenie
|
|
19
19
|
|
20
20
|
def function_calling(messages, function, config: {})
|
21
21
|
provider_name = config.llm.provider_name || config.providers.default
|
22
|
-
provider =
|
22
|
+
provider = PROVIDER_NAME_TO_PROVIDER[provider_name.to_sym]
|
23
23
|
|
24
24
|
raise ActiveGenie::InvalidProviderError, provider_name if provider.nil?
|
25
25
|
|
@@ -23,9 +23,9 @@ module ActiveGenie
|
|
23
23
|
@config.log.add_observer(observers: ->(log) { log_observer(log) })
|
24
24
|
@config.log.additional_context = { elo_id: }
|
25
25
|
|
26
|
-
matches
|
27
|
-
# TODO: debate can take a while, can be parallelized
|
26
|
+
ActiveGenie::FiberByBatch.call(matches, config: @config) do |player_a, player_b|
|
28
27
|
winner, loser = debate(player_a, player_b)
|
28
|
+
|
29
29
|
update_players_elo(winner, loser)
|
30
30
|
end
|
31
31
|
|
@@ -105,6 +105,7 @@ module ActiveGenie
|
|
105
105
|
players_in: players_in.map(&:id),
|
106
106
|
debates_count: matches.size,
|
107
107
|
total_tokens: @total_tokens,
|
108
|
+
players_in_round: players_in.map(&:id),
|
108
109
|
previous_highest_elo: @previous_highest_elo,
|
109
110
|
highest_elo:,
|
110
111
|
highest_elo_diff: highest_elo - @previous_highest_elo,
|
@@ -8,7 +8,7 @@ module ActiveGenie
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def initialize(players, criteria, config: nil)
|
11
|
-
@players = Players.new(players)
|
11
|
+
@players = Entities::Players.new(players)
|
12
12
|
@criteria = criteria
|
13
13
|
@config = config || ActiveGenie.configuration
|
14
14
|
@start_time = Time.now
|
@@ -19,7 +19,7 @@ module ActiveGenie
|
|
19
19
|
@config.log.add_observer(observers: ->(log) { log_observer(log) })
|
20
20
|
@config.log.additional_context = { free_for_all_id: }
|
21
21
|
|
22
|
-
matches
|
22
|
+
ActiveGenie::FiberByBatch.call(matches, config: @config) do |player_a, player_b|
|
23
23
|
winner, loser = debate(player_a, player_b)
|
24
24
|
|
25
25
|
update_players_score(winner, loser)
|
@@ -39,7 +39,7 @@ module ActiveGenie
|
|
39
39
|
def debate(player_a, player_b)
|
40
40
|
log_context = { player_a_id: player_a.id, player_b_id: player_b.id }
|
41
41
|
|
42
|
-
result = ActiveGenie::Comparator.
|
42
|
+
result = ActiveGenie::Comparator.by_debate(
|
43
43
|
player_a.content,
|
44
44
|
player_b.content,
|
45
45
|
@criteria,
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative '../utils/fiber_by_batch'
|
4
|
+
|
3
5
|
module ActiveGenie
|
4
6
|
module Ranker
|
5
7
|
class Scoring
|
@@ -8,7 +10,7 @@ module ActiveGenie
|
|
8
10
|
end
|
9
11
|
|
10
12
|
def initialize(players, criteria, juries: [], config: nil)
|
11
|
-
@players = Players.new(players)
|
13
|
+
@players = Entities::Players.new(players)
|
12
14
|
@criteria = criteria
|
13
15
|
@config = ActiveGenie.configuration.merge(config)
|
14
16
|
@juries = Array(juries).compact.uniq
|
@@ -17,7 +19,7 @@ module ActiveGenie
|
|
17
19
|
def call
|
18
20
|
@config.log.additional_context = { ranker_scoring_id: }
|
19
21
|
|
20
|
-
players_without_score
|
22
|
+
ActiveGenie::FiberByBatch.call(players_without_score, config: @config) do |player|
|
21
23
|
player.score = generate_score(player)
|
22
24
|
end
|
23
25
|
end
|
@@ -29,7 +31,7 @@ module ActiveGenie
|
|
29
31
|
end
|
30
32
|
|
31
33
|
def generate_score(player)
|
32
|
-
score, reasoning = ActiveGenie::Scorer.
|
34
|
+
score, reasoning = ActiveGenie::Scorer.by_jury_bench(
|
33
35
|
player.content,
|
34
36
|
@criteria,
|
35
37
|
@juries,
|
@@ -35,12 +35,11 @@ module ActiveGenie
|
|
35
35
|
new(...).call
|
36
36
|
end
|
37
37
|
|
38
|
-
def initialize(players, criteria,
|
39
|
-
@players = Players.new(players)
|
38
|
+
def initialize(players, criteria, juries: [], config: {})
|
39
|
+
@players = Entities::Players.new(players)
|
40
40
|
@criteria = criteria
|
41
|
-
@
|
41
|
+
@juries = Array(juries).compact.uniq
|
42
42
|
@config = ActiveGenie.configuration.merge(config)
|
43
|
-
@players = nil
|
44
43
|
end
|
45
44
|
|
46
45
|
def call
|
@@ -51,7 +50,7 @@ module ActiveGenie
|
|
51
50
|
|
52
51
|
while @players.elo_eligible?
|
53
52
|
elo_report = run_elo_round!
|
54
|
-
|
53
|
+
eliminate_lower_tier_players!
|
55
54
|
rebalance_players!(elo_report)
|
56
55
|
end
|
57
56
|
|
@@ -69,7 +68,7 @@ module ActiveGenie
|
|
69
68
|
Scoring.call(
|
70
69
|
@players,
|
71
70
|
@criteria,
|
72
|
-
|
71
|
+
juries: @juries,
|
73
72
|
config: @config
|
74
73
|
)
|
75
74
|
end
|
@@ -88,8 +87,8 @@ module ActiveGenie
|
|
88
87
|
)
|
89
88
|
end
|
90
89
|
|
91
|
-
def
|
92
|
-
@players.
|
90
|
+
def eliminate_lower_tier_players!
|
91
|
+
@players.calc_lower_tier.each { |player| player.eliminated = ELIMINATION_RELEGATION }
|
93
92
|
end
|
94
93
|
|
95
94
|
def rebalance_players!(elo_report)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '../
|
3
|
+
require_relative '../providers/unified_provider'
|
4
4
|
|
5
5
|
module ActiveGenie
|
6
6
|
module Scorer
|
@@ -44,7 +44,7 @@ module ActiveGenie
|
|
44
44
|
{ role: 'user', content: "Text to score: #{@text}" }
|
45
45
|
]
|
46
46
|
|
47
|
-
result = ::ActiveGenie::
|
47
|
+
result = ::ActiveGenie::Providers::UnifiedProvider.function_calling(
|
48
48
|
messages,
|
49
49
|
build_function,
|
50
50
|
config: @config
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'async'
|
4
|
+
|
5
|
+
module ActiveGenie
|
6
|
+
module FiberByBatch
|
7
|
+
module_function
|
8
|
+
|
9
|
+
def call(items, config:, &block)
|
10
|
+
items.each_slice(config.llm.max_fibers).to_a.each do |batch|
|
11
|
+
Async do
|
12
|
+
tasks = batch.map do |item|
|
13
|
+
Async { block.call(item) }
|
14
|
+
end
|
15
|
+
|
16
|
+
tasks.each(&:wait)
|
17
|
+
end.wait
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_genie
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.30.
|
4
|
+
version: 0.30.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Radamés Roriz
|
@@ -9,9 +9,23 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
date: 2025-08-15 00:00:00.000000000 Z
|
12
|
-
dependencies:
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: async
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
13
27
|
description: |
|
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
|
28
|
+
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 extractor, comparator, scorer, and ranker, so you can focus on your app’s logic, not the shifting AI landscape.
|
15
29
|
Behind the scenes, a custom benchmarking system keeps everything consistent across LLM vendors and versions, release after release.
|
16
30
|
email:
|
17
31
|
- radames@roriz.dev
|
@@ -73,8 +87,9 @@ files:
|
|
73
87
|
- lib/active_genie/ranker/scoring.rb
|
74
88
|
- lib/active_genie/ranker/tournament.rb
|
75
89
|
- lib/active_genie/scorer.rb
|
76
|
-
- lib/active_genie/scorer/jury_bench.md
|
90
|
+
- lib/active_genie/scorer/jury_bench.prompt.md
|
77
91
|
- lib/active_genie/scorer/jury_bench.rb
|
92
|
+
- lib/active_genie/utils/fiber_by_batch.rb
|
78
93
|
- lib/tasks/benchmark.rake
|
79
94
|
- lib/tasks/install.rake
|
80
95
|
- lib/tasks/templates/active_genie.rb
|
File without changes
|