active_genie 0.30.3 → 0.30.8
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 +7 -5
- data/VERSION +1 -1
- data/lib/active_genie/comparator/debate.rb +13 -36
- data/lib/active_genie/comparator/fight.rb +3 -3
- data/lib/active_genie/comparator.rb +0 -2
- data/lib/active_genie/configs/base_config.rb +25 -0
- data/lib/active_genie/configs/extractor_config.rb +6 -14
- data/lib/active_genie/configs/lister_config.rb +6 -10
- data/lib/active_genie/configs/llm_config.rb +12 -26
- data/lib/active_genie/configs/log_config.rb +19 -16
- data/lib/active_genie/configs/providers/anthropic_config.rb +16 -1
- data/lib/active_genie/configs/providers/deepseek_config.rb +8 -2
- data/lib/active_genie/configs/providers/google_config.rb +8 -2
- data/lib/active_genie/configs/providers/openai_config.rb +8 -2
- data/lib/active_genie/configs/providers/provider_base.rb +22 -31
- data/lib/active_genie/configs/providers_config.rb +35 -16
- data/lib/active_genie/configs/ranker_config.rb +6 -12
- data/lib/active_genie/configuration.rb +23 -60
- data/lib/active_genie/{ranker/entities → entities}/player.rb +4 -0
- data/lib/active_genie/{ranker/entities → entities}/players.rb +1 -1
- data/lib/active_genie/entities/result.rb +29 -0
- data/lib/active_genie/errors/provider_server_error.rb +8 -5
- data/lib/active_genie/errors/without_available_provider_error.rb +39 -0
- data/lib/active_genie/extractor/data.json +9 -0
- data/lib/active_genie/extractor/data.prompt.md +12 -0
- data/lib/active_genie/extractor/data.rb +71 -0
- data/lib/active_genie/extractor/explanation.rb +19 -47
- data/lib/active_genie/extractor/litote.rb +8 -14
- data/lib/active_genie/extractor.rb +5 -0
- data/lib/active_genie/lister/feud.json +5 -1
- data/lib/active_genie/lister/feud.rb +10 -24
- data/lib/active_genie/lister/juries.rb +14 -20
- data/lib/active_genie/logger.rb +16 -28
- data/lib/active_genie/providers/anthropic_provider.rb +11 -5
- data/lib/active_genie/providers/base_provider.rb +15 -17
- data/lib/active_genie/providers/deepseek_provider.rb +17 -9
- data/lib/active_genie/providers/google_provider.rb +10 -4
- data/lib/active_genie/providers/openai_provider.rb +6 -4
- data/lib/active_genie/providers/unified_provider.rb +47 -17
- data/lib/active_genie/ranker/elo.rb +41 -36
- data/lib/active_genie/ranker/free_for_all.rb +45 -28
- data/lib/active_genie/ranker/scoring.rb +20 -11
- data/lib/active_genie/ranker/tournament.rb +23 -35
- data/lib/active_genie/scorer/jury_bench.rb +15 -29
- data/lib/active_genie/utils/base_module.rb +34 -0
- data/lib/active_genie/utils/call_wrapper.rb +20 -0
- data/lib/active_genie/utils/deep_merge.rb +12 -0
- data/lib/active_genie/utils/fiber_by_batch.rb +2 -2
- data/lib/active_genie/utils/text_case.rb +18 -0
- data/lib/active_genie.rb +16 -18
- data/lib/tasks/test.rake +61 -3
- metadata +19 -8
- data/lib/active_genie/configs/comparator_config.rb +0 -10
- data/lib/active_genie/configs/scorer_config.rb +0 -10
data/lib/active_genie/logger.rb
CHANGED
|
@@ -5,32 +5,24 @@ require 'fileutils'
|
|
|
5
5
|
|
|
6
6
|
module ActiveGenie
|
|
7
7
|
class Logger
|
|
8
|
-
def
|
|
9
|
-
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def call(data)
|
|
13
|
-
log = data.merge(@log_config.additional_context)
|
|
8
|
+
def call(data, config:)
|
|
9
|
+
log = data.merge(config.log.additional_context || {})
|
|
14
10
|
.merge(
|
|
15
11
|
timestamp: Time.now,
|
|
16
12
|
process_id: Process.pid
|
|
17
13
|
)
|
|
18
14
|
|
|
19
|
-
persist!(log)
|
|
20
|
-
output_call(log)
|
|
21
|
-
call_observers(log)
|
|
15
|
+
persist!(log, config:)
|
|
16
|
+
output_call(log, config:)
|
|
17
|
+
call_observers(log, config:)
|
|
22
18
|
|
|
23
19
|
log
|
|
24
20
|
end
|
|
25
21
|
|
|
26
|
-
def merge(log_config = nil)
|
|
27
|
-
new(log_config:)
|
|
28
|
-
end
|
|
29
|
-
|
|
30
22
|
private
|
|
31
23
|
|
|
32
|
-
def call_observers(log)
|
|
33
|
-
Array(
|
|
24
|
+
def call_observers(log, config:)
|
|
25
|
+
Array(config.log.observers).each do |observer|
|
|
34
26
|
next unless observer[:scope].all? { |key, value| log[key.to_sym] == value }
|
|
35
27
|
|
|
36
28
|
observer[:observer]&.call(log)
|
|
@@ -39,9 +31,9 @@ module ActiveGenie
|
|
|
39
31
|
end
|
|
40
32
|
end
|
|
41
33
|
|
|
42
|
-
def output_call(log)
|
|
43
|
-
if
|
|
44
|
-
|
|
34
|
+
def output_call(log, config:)
|
|
35
|
+
if config.log.output
|
|
36
|
+
config.log.output&.call(log)
|
|
45
37
|
else
|
|
46
38
|
$stdout.puts log
|
|
47
39
|
end
|
|
@@ -49,20 +41,16 @@ module ActiveGenie
|
|
|
49
41
|
call(code: :output_error, error: e.message)
|
|
50
42
|
end
|
|
51
43
|
|
|
52
|
-
def persist!(log)
|
|
53
|
-
file_path =
|
|
44
|
+
def persist!(log, config:)
|
|
45
|
+
file_path = if log.key?(:fine_tune) && log[:fine_tune]
|
|
46
|
+
config.log.fine_tune_file_path
|
|
47
|
+
else
|
|
48
|
+
config.log.file_path
|
|
49
|
+
end
|
|
54
50
|
folder_path = File.dirname(file_path)
|
|
55
51
|
|
|
56
52
|
FileUtils.mkdir_p(folder_path)
|
|
57
53
|
File.write(file_path, "#{JSON.generate(log)}\n", mode: 'a')
|
|
58
54
|
end
|
|
59
|
-
|
|
60
|
-
def log_to_file_path(log)
|
|
61
|
-
if log.key?(:fine_tune) && log[:fine_tune]
|
|
62
|
-
@log_config.fine_tune_file_path
|
|
63
|
-
else
|
|
64
|
-
@log_config.file_path
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
55
|
end
|
|
68
56
|
end
|
|
@@ -30,7 +30,11 @@ module ActiveGenie
|
|
|
30
30
|
temperature: @config.llm.temperature || 0
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
response = retry_with_backoff do
|
|
34
|
+
request(payload)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
response.dig('content', 0, 'input')
|
|
34
38
|
end
|
|
35
39
|
|
|
36
40
|
ANTHROPIC_ENDPOINT = '/v1/messages'
|
|
@@ -55,7 +59,7 @@ module ActiveGenie
|
|
|
55
59
|
def request(payload)
|
|
56
60
|
response = post(url, payload, headers:)
|
|
57
61
|
|
|
58
|
-
|
|
62
|
+
ActiveGenie.logger.call(
|
|
59
63
|
{
|
|
60
64
|
code: :llm_usage,
|
|
61
65
|
input_tokens: response.dig('usage', 'input_tokens'),
|
|
@@ -65,15 +69,17 @@ module ActiveGenie
|
|
|
65
69
|
'output_tokens'),
|
|
66
70
|
model: payload[:model],
|
|
67
71
|
usage: response['usage']
|
|
68
|
-
}
|
|
72
|
+
},
|
|
73
|
+
config: @config
|
|
69
74
|
)
|
|
70
|
-
|
|
75
|
+
ActiveGenie.logger.call(
|
|
71
76
|
{
|
|
72
77
|
code: :function_calling,
|
|
73
78
|
fine_tune: true,
|
|
74
79
|
payload:,
|
|
75
80
|
parsed_response: response.dig('content', 0, 'input')
|
|
76
|
-
}
|
|
81
|
+
},
|
|
82
|
+
config: @config
|
|
77
83
|
)
|
|
78
84
|
|
|
79
85
|
response
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'net/http'
|
|
4
|
+
require_relative '../errors/provider_server_error'
|
|
4
5
|
|
|
5
6
|
module ActiveGenie
|
|
6
7
|
module Providers
|
|
@@ -103,9 +104,7 @@ module ActiveGenie
|
|
|
103
104
|
http.read_timeout = @config.llm.read_timeout || DEFAULT_TIMEOUT
|
|
104
105
|
http.open_timeout = @config.llm.open_timeout || DEFAULT_OPEN_TIMEOUT
|
|
105
106
|
|
|
106
|
-
|
|
107
|
-
http.request(request)
|
|
108
|
-
end
|
|
107
|
+
http.request(request)
|
|
109
108
|
end
|
|
110
109
|
|
|
111
110
|
# Apply headers to the request
|
|
@@ -153,7 +152,7 @@ module ActiveGenie
|
|
|
153
152
|
#
|
|
154
153
|
# @param details [Hash] Request and response details
|
|
155
154
|
def log_request_details(uri:, request:, response:, start_time:, parsed_response:)
|
|
156
|
-
|
|
155
|
+
ActiveGenie.logger.call(
|
|
157
156
|
{
|
|
158
157
|
code: :http_request,
|
|
159
158
|
uri: uri.to_s,
|
|
@@ -161,7 +160,7 @@ module ActiveGenie
|
|
|
161
160
|
status: response.code,
|
|
162
161
|
duration: Time.now - start_time,
|
|
163
162
|
response_size: parsed_response.to_s.bytesize
|
|
164
|
-
}
|
|
163
|
+
}, config: @config
|
|
165
164
|
)
|
|
166
165
|
end
|
|
167
166
|
|
|
@@ -171,23 +170,22 @@ module ActiveGenie
|
|
|
171
170
|
retries = 0
|
|
172
171
|
|
|
173
172
|
begin
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
response
|
|
179
|
-
rescue Net::OpenTimeout, Net::ReadTimeout, Errno::ECONNREFUSED, ProviderServerError => e
|
|
173
|
+
yield
|
|
174
|
+
rescue Net::OpenTimeout, Net::ReadTimeout, Errno::ECONNREFUSED, ActiveGenie::ProviderServerError,
|
|
175
|
+
JSON::ParserError => e
|
|
180
176
|
raise if retries > max_retries
|
|
181
177
|
|
|
182
178
|
sleep_time = retry_delay * (2**retries)
|
|
183
179
|
retries += 1
|
|
184
180
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
181
|
+
ActiveGenie.logger.call(
|
|
182
|
+
{
|
|
183
|
+
code: :retry_attempt,
|
|
184
|
+
attempt: retries,
|
|
185
|
+
max_retries:,
|
|
186
|
+
next_retry_in_seconds: sleep_time,
|
|
187
|
+
error: e.message
|
|
188
|
+
}, config: @config
|
|
191
189
|
)
|
|
192
190
|
|
|
193
191
|
sleep(sleep_time)
|
|
@@ -25,14 +25,14 @@ module ActiveGenie
|
|
|
25
25
|
model:
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
response =
|
|
29
|
-
|
|
30
|
-
if response.nil? || response.keys.empty?
|
|
31
|
-
raise InvalidResponseError,
|
|
32
|
-
"Invalid response: #{response}"
|
|
28
|
+
response = retry_with_backoff do
|
|
29
|
+
request(payload)
|
|
33
30
|
end
|
|
34
31
|
|
|
35
|
-
|
|
32
|
+
raise InvalidResponseError, "Invalid response: #{response}" if response.keys.empty?
|
|
33
|
+
raise InvalidResponseError, 'Invalid response: empty' if response.nil?
|
|
34
|
+
|
|
35
|
+
ActiveGenie.logger.call({ code: :function_calling, fine_tune: true, payload:, response: }, config: @config)
|
|
36
36
|
|
|
37
37
|
response
|
|
38
38
|
end
|
|
@@ -44,7 +44,7 @@ module ActiveGenie
|
|
|
44
44
|
|
|
45
45
|
return nil if response.nil?
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
ActiveGenie.logger.call(
|
|
48
48
|
{
|
|
49
49
|
code: :llm_usage,
|
|
50
50
|
input_tokens: response.dig('usage', 'prompt_tokens'),
|
|
@@ -52,11 +52,19 @@ module ActiveGenie
|
|
|
52
52
|
total_tokens: response.dig('usage', 'total_tokens'),
|
|
53
53
|
model:,
|
|
54
54
|
usage: response['usage']
|
|
55
|
-
}
|
|
55
|
+
}, config: @config
|
|
56
56
|
)
|
|
57
57
|
|
|
58
|
-
parsed_response = JSON.parse(response
|
|
58
|
+
parsed_response = JSON.parse(get_response_body(response))
|
|
59
59
|
parsed_response['message'] || parsed_response
|
|
60
|
+
rescue JSON::ParserError
|
|
61
|
+
raise InvalidResponseError, "Invalid response: #{get_response_body(response)}"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def get_response_body(response)
|
|
65
|
+
response.dig('choices', 0, 'message', 'tool_calls', 0, 'function', 'arguments')
|
|
66
|
+
.gsub(', " "', '')
|
|
67
|
+
.strip
|
|
60
68
|
end
|
|
61
69
|
|
|
62
70
|
def function_to_tool(function)
|
|
@@ -29,12 +29,17 @@ module ActiveGenie
|
|
|
29
29
|
}
|
|
30
30
|
params = { key: provider_config.api_key }
|
|
31
31
|
|
|
32
|
-
response =
|
|
32
|
+
response = retry_with_backoff do
|
|
33
|
+
request(payload, params)
|
|
34
|
+
end
|
|
33
35
|
|
|
34
36
|
json_string = response&.dig('candidates', 0, 'content', 'parts', 0, 'text')
|
|
35
37
|
return nil if json_string.nil? || json_string.empty?
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
ActiveGenie.logger.call(
|
|
40
|
+
{ code: :function_calling, fine_tune: true, payload:, parsed_response: json_string },
|
|
41
|
+
config: @config
|
|
42
|
+
)
|
|
38
43
|
|
|
39
44
|
normalize_response(json_string)
|
|
40
45
|
end
|
|
@@ -50,7 +55,7 @@ module ActiveGenie
|
|
|
50
55
|
def request(payload, params)
|
|
51
56
|
response = post(url, payload, headers: DEFAULT_HEADERS, params:)
|
|
52
57
|
|
|
53
|
-
|
|
58
|
+
ActiveGenie.logger.call(
|
|
54
59
|
{
|
|
55
60
|
code: :llm_usage,
|
|
56
61
|
input_tokens: response['usageMetadata']['promptTokenCount'] || 0,
|
|
@@ -58,7 +63,8 @@ module ActiveGenie
|
|
|
58
63
|
total_tokens: response['usageMetadata']['totalTokenCount'] || (prompt_tokens + candidates_tokens),
|
|
59
64
|
model:,
|
|
60
65
|
usage: response['usageMetadata'] || {}
|
|
61
|
-
}
|
|
66
|
+
},
|
|
67
|
+
config: @config
|
|
62
68
|
)
|
|
63
69
|
|
|
64
70
|
response
|
|
@@ -25,11 +25,13 @@ module ActiveGenie
|
|
|
25
25
|
model:
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
response =
|
|
28
|
+
response = retry_with_backoff do
|
|
29
|
+
request(payload)
|
|
30
|
+
end
|
|
29
31
|
|
|
30
32
|
raise InvalidResponseError, "Invalid response: #{response}" if response.nil? || response.keys.empty?
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
ActiveGenie.logger.call({ code: :function_calling, fine_tune: true, payload:, response: }, config: @config)
|
|
33
35
|
|
|
34
36
|
response
|
|
35
37
|
end
|
|
@@ -41,7 +43,7 @@ module ActiveGenie
|
|
|
41
43
|
|
|
42
44
|
return nil if response.nil?
|
|
43
45
|
|
|
44
|
-
|
|
46
|
+
ActiveGenie.logger.call(
|
|
45
47
|
{
|
|
46
48
|
code: :llm_usage,
|
|
47
49
|
input_tokens: response.dig('usage', 'prompt_tokens'),
|
|
@@ -49,7 +51,7 @@ module ActiveGenie
|
|
|
49
51
|
total_tokens: response.dig('usage', 'total_tokens'),
|
|
50
52
|
model:,
|
|
51
53
|
usage: response['usage']
|
|
52
|
-
}
|
|
54
|
+
}, config: @config
|
|
53
55
|
)
|
|
54
56
|
|
|
55
57
|
parsed_response = JSON.parse(response.dig('choices', 0, 'message', 'tool_calls', 0, 'function', 'arguments'))
|
|
@@ -6,6 +6,7 @@ require_relative 'google_provider'
|
|
|
6
6
|
require_relative 'deepseek_provider'
|
|
7
7
|
require_relative '../errors/invalid_provider_error'
|
|
8
8
|
require_relative '../errors/invalid_model_error'
|
|
9
|
+
require_relative '../errors/without_available_provider_error'
|
|
9
10
|
|
|
10
11
|
module ActiveGenie
|
|
11
12
|
module Providers
|
|
@@ -19,8 +20,13 @@ module ActiveGenie
|
|
|
19
20
|
}.freeze
|
|
20
21
|
|
|
21
22
|
def function_calling(messages, function, config: {})
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
model, provider_name = model_and_provider_by(config)
|
|
24
|
+
|
|
25
|
+
provider = PROVIDER_NAME_TO_PROVIDER[provider_name&.to_sym]
|
|
26
|
+
|
|
27
|
+
raise ActiveGenie::WithoutAvailableProviderError if provider.nil?
|
|
28
|
+
|
|
29
|
+
config.llm.model = model
|
|
24
30
|
|
|
25
31
|
response = provider.new(config).function_calling(messages, function)
|
|
26
32
|
|
|
@@ -29,30 +35,54 @@ module ActiveGenie
|
|
|
29
35
|
|
|
30
36
|
private
|
|
31
37
|
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
# rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
|
39
|
+
def model_and_provider_by(config)
|
|
40
|
+
model, provider_name = explicit_choice(config)
|
|
41
|
+
model, provider_name = global_default(config) if model.nil? && provider_name.nil?
|
|
42
|
+
model, provider_name = module_recommendation(config) if model.nil? && provider_name.nil?
|
|
34
43
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
provider_name
|
|
38
|
-
end
|
|
44
|
+
model, provider_name = infer_from_partial(config, model, provider_name) if model.nil? || provider_name.nil?
|
|
45
|
+
model, provider_name = any_available(config) if model.nil? || provider_name.nil?
|
|
39
46
|
|
|
40
|
-
|
|
47
|
+
[model, provider_name]
|
|
48
|
+
end
|
|
49
|
+
# rubocop:enable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
|
41
50
|
|
|
42
|
-
|
|
51
|
+
def explicit_choice(config)
|
|
52
|
+
model = config.llm.model
|
|
53
|
+
provider_name = config.providers.default
|
|
43
54
|
|
|
44
|
-
|
|
55
|
+
[model, provider_name]
|
|
45
56
|
end
|
|
46
57
|
|
|
47
|
-
def
|
|
48
|
-
|
|
49
|
-
raise ActiveGenie::InvalidModelError, 'nil' unless config.llm.recommended_model
|
|
58
|
+
def global_default(config)
|
|
59
|
+
provider_name = config.providers.default
|
|
50
60
|
|
|
51
|
-
|
|
61
|
+
[nil, provider_name]
|
|
62
|
+
end
|
|
52
63
|
|
|
53
|
-
|
|
64
|
+
def module_recommendation(config)
|
|
65
|
+
model = config.llm.recommended_model
|
|
66
|
+
provider_name = config.providers.provider_name_by_model(model) if model
|
|
67
|
+
|
|
68
|
+
return nil if model.nil? || provider_name.nil?
|
|
69
|
+
|
|
70
|
+
[model, provider_name]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def infer_from_partial(config, model, provider_name)
|
|
74
|
+
provider_name ||= config.providers.provider_name_by_model(model) if model
|
|
75
|
+
model ||= config.providers.valid[provider_name.to_sym]&.default_model if provider_name
|
|
76
|
+
|
|
77
|
+
[model, provider_name]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def any_available(config)
|
|
81
|
+
provider = config.providers.valid.first
|
|
82
|
+
provider_name = provider&.first
|
|
83
|
+
model = provider&.last&.default_model
|
|
54
84
|
|
|
55
|
-
|
|
85
|
+
[model, provider_name]
|
|
56
86
|
end
|
|
57
87
|
|
|
58
88
|
def normalize_response(response)
|
|
@@ -12,7 +12,7 @@ module ActiveGenie
|
|
|
12
12
|
@higher_tier = players.calc_higher_tier
|
|
13
13
|
@lower_tier = players.calc_lower_tier
|
|
14
14
|
@criteria = criteria
|
|
15
|
-
@
|
|
15
|
+
@initial_config = config
|
|
16
16
|
@tmp_highers = []
|
|
17
17
|
@total_tokens = 0
|
|
18
18
|
@previous_elo = @players.to_h { |player| [player.id, player.elo] }
|
|
@@ -20,16 +20,16 @@ module ActiveGenie
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def call
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
config.log.add_observer(observers: ->(log) { log_observer(log) })
|
|
24
|
+
config.log.additional_context = { elo_id: }
|
|
25
25
|
|
|
26
|
-
ActiveGenie::FiberByBatch.call(matches, config:
|
|
26
|
+
ActiveGenie::FiberByBatch.call(matches, config:) do |player_a, player_b|
|
|
27
27
|
winner, loser = debate(player_a, player_b)
|
|
28
28
|
|
|
29
29
|
update_players_elo(winner, loser)
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
elo_result
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
DEBATE_PER_PLAYER = 3
|
|
@@ -38,39 +38,37 @@ module ActiveGenie
|
|
|
38
38
|
private
|
|
39
39
|
|
|
40
40
|
def matches
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
@higher_tier.each_with_object([]) do |attack_player, matches|
|
|
41
|
+
@matches ||= @lower_tier.each_with_object([]) do |lower_player, matches|
|
|
44
42
|
DEBATE_PER_PLAYER.times do
|
|
45
43
|
higher_player = next_higher_player
|
|
46
44
|
|
|
47
|
-
next if
|
|
45
|
+
next if matches.include?([lower_player, higher_player])
|
|
48
46
|
|
|
49
|
-
|
|
50
|
-
matches << [attack_player, higher_player]
|
|
47
|
+
matches << [lower_player, higher_player]
|
|
51
48
|
end
|
|
52
49
|
end
|
|
53
50
|
end
|
|
54
51
|
|
|
55
52
|
def next_higher_player
|
|
56
|
-
@tmp_highers = @higher_tier.
|
|
53
|
+
@tmp_highers = @higher_tier.dup if @tmp_highers.empty?
|
|
57
54
|
|
|
58
|
-
@tmp_highers.pop
|
|
55
|
+
@tmp_highers.count % 2 ? @tmp_highers.shift : @tmp_highers.pop
|
|
59
56
|
end
|
|
60
57
|
|
|
61
58
|
def debate(player_a, player_b)
|
|
59
|
+
debate_config = ActiveGenie::DeepMerge.call(
|
|
60
|
+
config.to_h,
|
|
61
|
+
{ log: { additional_context: { player_a_id: player_a.id, player_b_id: player_b.id } } }
|
|
62
|
+
)
|
|
63
|
+
|
|
62
64
|
result = ActiveGenie::Comparator.by_debate(
|
|
63
65
|
player_a.content,
|
|
64
66
|
player_b.content,
|
|
65
67
|
@criteria,
|
|
66
|
-
config:
|
|
68
|
+
config: ActiveGenie.new_configuration(debate_config)
|
|
67
69
|
)
|
|
68
70
|
|
|
69
|
-
winner, loser =
|
|
70
|
-
when 'player_a' then [player_a, player_b]
|
|
71
|
-
when 'player_b' then [player_b, player_a]
|
|
72
|
-
when 'draw' then [nil, nil]
|
|
73
|
-
end
|
|
71
|
+
winner, loser = result.data == player_a.content ? [player_a, player_b] : [player_b, player_a]
|
|
74
72
|
|
|
75
73
|
[winner, loser]
|
|
76
74
|
end
|
|
@@ -94,27 +92,30 @@ module ActiveGenie
|
|
|
94
92
|
higher_tier_ids = @higher_tier.map(&:id).join(',')
|
|
95
93
|
lower_tier_ids = @lower_tier.map(&:id).join(',')
|
|
96
94
|
|
|
97
|
-
ranker_unique_key = [higher_tier_ids, lower_tier_ids, @criteria
|
|
95
|
+
ranker_unique_key = [higher_tier_ids, lower_tier_ids, @criteria].join('-')
|
|
98
96
|
Digest::MD5.hexdigest(ranker_unique_key)
|
|
99
97
|
end
|
|
100
98
|
end
|
|
101
99
|
|
|
102
|
-
def
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
100
|
+
def elo_result
|
|
101
|
+
result = ActiveGenie::Result.new(
|
|
102
|
+
data: @players.sorted.map(&:content),
|
|
103
|
+
metadata: {
|
|
104
|
+
elo_id:,
|
|
105
|
+
players: @players,
|
|
106
|
+
players_in_round: players_in.map(&:id),
|
|
107
|
+
debates_count: matches.size,
|
|
108
|
+
total_tokens: @total_tokens,
|
|
109
|
+
previous_highest_elo: @previous_highest_elo,
|
|
110
|
+
highest_elo:,
|
|
111
|
+
highest_elo_diff: highest_elo - @previous_highest_elo,
|
|
112
|
+
players_elo_diff:
|
|
113
|
+
}
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
ActiveGenie.logger.call({ elo_id:, code: :elo_report, **result.metadata }, config:)
|
|
117
|
+
|
|
118
|
+
result
|
|
118
119
|
end
|
|
119
120
|
|
|
120
121
|
def players_in
|
|
@@ -135,6 +136,10 @@ module ActiveGenie
|
|
|
135
136
|
def log_observer(log)
|
|
136
137
|
@total_tokens += log[:total_tokens] if log[:code] == :llm_usage
|
|
137
138
|
end
|
|
139
|
+
|
|
140
|
+
def config
|
|
141
|
+
@config ||= ActiveGenie.new_configuration(@initial_config)
|
|
142
|
+
end
|
|
138
143
|
end
|
|
139
144
|
end
|
|
140
145
|
end
|
|
@@ -10,22 +10,19 @@ module ActiveGenie
|
|
|
10
10
|
def initialize(players, criteria, config: nil)
|
|
11
11
|
@players = Entities::Players.new(players)
|
|
12
12
|
@criteria = criteria
|
|
13
|
-
@
|
|
13
|
+
@initial_config = config
|
|
14
14
|
@start_time = Time.now
|
|
15
15
|
@total_tokens = 0
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def call
|
|
19
|
-
|
|
20
|
-
@config.log.additional_context = { free_for_all_id: }
|
|
21
|
-
|
|
22
|
-
ActiveGenie::FiberByBatch.call(matches, config: @config) do |player_a, player_b|
|
|
19
|
+
ActiveGenie::FiberByBatch.call(matches, config:) do |player_a, player_b|
|
|
23
20
|
winner, loser = debate(player_a, player_b)
|
|
24
21
|
|
|
25
22
|
update_players_score(winner, loser)
|
|
26
23
|
end
|
|
27
24
|
|
|
28
|
-
|
|
25
|
+
build_result
|
|
29
26
|
end
|
|
30
27
|
|
|
31
28
|
private
|
|
@@ -37,29 +34,32 @@ module ActiveGenie
|
|
|
37
34
|
end
|
|
38
35
|
|
|
39
36
|
def debate(player_a, player_b)
|
|
40
|
-
|
|
37
|
+
debate_config = ActiveGenie::DeepMerge.call(
|
|
38
|
+
config.to_h,
|
|
39
|
+
{ log: { additional_context: { player_a_id: player_a.id, player_b_id: player_b.id } } }
|
|
40
|
+
)
|
|
41
41
|
|
|
42
42
|
result = ActiveGenie::Comparator.by_debate(
|
|
43
43
|
player_a.content,
|
|
44
44
|
player_b.content,
|
|
45
45
|
@criteria,
|
|
46
|
-
config:
|
|
46
|
+
config: ActiveGenie.new_configuration(debate_config)
|
|
47
47
|
)
|
|
48
48
|
|
|
49
|
-
winner, loser = case result
|
|
50
|
-
when
|
|
51
|
-
|
|
52
|
-
when 'draw' then [nil, nil]
|
|
49
|
+
winner, loser = case result.data
|
|
50
|
+
when player_a.to_s then [player_a, player_b]
|
|
51
|
+
else [player_b, player_a]
|
|
53
52
|
end
|
|
54
53
|
|
|
55
|
-
|
|
54
|
+
ActiveGenie.logger.call(
|
|
56
55
|
{
|
|
57
|
-
|
|
56
|
+
player_a_id: player_a.id,
|
|
57
|
+
player_b_id: player_b.id,
|
|
58
58
|
code: :free_for_all,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
reasoning: result
|
|
62
|
-
}
|
|
59
|
+
winner: winner.to_s[0..20],
|
|
60
|
+
loser: loser.to_s[0..20],
|
|
61
|
+
reasoning: result.reasoning
|
|
62
|
+
}, config:
|
|
63
63
|
)
|
|
64
64
|
|
|
65
65
|
[winner, loser]
|
|
@@ -80,27 +80,44 @@ module ActiveGenie
|
|
|
80
80
|
def free_for_all_id
|
|
81
81
|
@free_for_all_id ||= begin
|
|
82
82
|
eligible_ids = @players.eligible.map(&:id).join(',')
|
|
83
|
-
ranking_unique_key = [eligible_ids, @criteria
|
|
83
|
+
ranking_unique_key = [eligible_ids, @criteria].join('-')
|
|
84
84
|
Digest::MD5.hexdigest(ranking_unique_key)
|
|
85
85
|
end
|
|
86
86
|
end
|
|
87
87
|
|
|
88
|
-
def
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
88
|
+
def build_result
|
|
89
|
+
result = ActiveGenie::Result.new(
|
|
90
|
+
data: @players.sorted.map(&:content),
|
|
91
|
+
metadata: {
|
|
92
|
+
free_for_all_id:,
|
|
93
|
+
players: @players,
|
|
94
|
+
debates_count: matches.size,
|
|
95
|
+
duration_seconds: Time.now - @start_time,
|
|
96
|
+
total_tokens: @total_tokens
|
|
97
|
+
}
|
|
98
|
+
)
|
|
95
99
|
|
|
96
|
-
|
|
100
|
+
ActiveGenie.logger.call({ code: :free_for_all_report, **result.metadata }, config:)
|
|
97
101
|
|
|
98
|
-
|
|
102
|
+
result
|
|
99
103
|
end
|
|
100
104
|
|
|
101
105
|
def log_observer(log)
|
|
102
106
|
@total_tokens += log[:total_tokens] if log[:code] == :llm_usage
|
|
103
107
|
end
|
|
108
|
+
|
|
109
|
+
def config
|
|
110
|
+
@config ||= begin
|
|
111
|
+
c = ActiveGenie.new_configuration(
|
|
112
|
+
ActiveGenie::DeepMerge.call(
|
|
113
|
+
@initial_config.to_h,
|
|
114
|
+
{ log: { context: { free_for_all_id: } } }
|
|
115
|
+
)
|
|
116
|
+
)
|
|
117
|
+
c.log.add_observer(observers: ->(log) { log_observer(log) })
|
|
118
|
+
c
|
|
119
|
+
end
|
|
120
|
+
end
|
|
104
121
|
end
|
|
105
122
|
end
|
|
106
123
|
end
|