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
|
@@ -1,90 +1,53 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'configs/comparator_config'
|
|
4
3
|
require_relative 'configs/extractor_config'
|
|
5
4
|
require_relative 'configs/lister_config'
|
|
6
5
|
require_relative 'configs/llm_config'
|
|
7
6
|
require_relative 'configs/log_config'
|
|
8
7
|
require_relative 'configs/providers_config'
|
|
9
8
|
require_relative 'configs/ranker_config'
|
|
10
|
-
require_relative 'configs/scorer_config'
|
|
11
9
|
|
|
12
10
|
module ActiveGenie
|
|
13
11
|
class Configuration
|
|
14
|
-
def
|
|
15
|
-
@
|
|
12
|
+
def initialize(initial_config = nil)
|
|
13
|
+
@initial_config = initial_config || {}
|
|
16
14
|
end
|
|
17
15
|
|
|
18
16
|
def providers
|
|
19
|
-
@providers ||= Config::ProvidersConfig.new
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def ranker
|
|
23
|
-
@ranker ||= Config::RankerConfig.new
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def scorer
|
|
27
|
-
@scorer ||= Config::ScorerConfig.new
|
|
17
|
+
@providers ||= Config::ProvidersConfig.new(**@initial_config.fetch(:providers, {}))
|
|
28
18
|
end
|
|
29
19
|
|
|
30
|
-
def
|
|
31
|
-
@
|
|
20
|
+
def llm
|
|
21
|
+
@llm ||= Config::LlmConfig.new(**@initial_config.fetch(:llm, {}))
|
|
32
22
|
end
|
|
33
23
|
|
|
34
|
-
def
|
|
35
|
-
@
|
|
24
|
+
def log
|
|
25
|
+
@log ||= Config::LogConfig.new(**@initial_config.fetch(:log, {}))
|
|
36
26
|
end
|
|
37
27
|
|
|
38
|
-
|
|
39
|
-
@lister ||= Config::ListerConfig.new
|
|
40
|
-
end
|
|
28
|
+
# Modules
|
|
41
29
|
|
|
42
|
-
def
|
|
43
|
-
@
|
|
30
|
+
def ranker
|
|
31
|
+
@ranker ||= Config::RankerConfig.new(**@initial_config.fetch(:ranker, {}))
|
|
44
32
|
end
|
|
45
33
|
|
|
46
|
-
def
|
|
47
|
-
@
|
|
34
|
+
def extractor
|
|
35
|
+
@extractor ||= Config::ExtractorConfig.new(**@initial_config.fetch(:extractor, {}))
|
|
48
36
|
end
|
|
49
37
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def merge(config_params = {})
|
|
53
|
-
return config_params if config_params.is_a?(Configuration)
|
|
54
|
-
|
|
55
|
-
new_configuration = dup
|
|
56
|
-
|
|
57
|
-
SUB_CONFIGS.each do |key|
|
|
58
|
-
config = new_configuration.send(key)
|
|
59
|
-
|
|
60
|
-
next unless config.respond_to?(:merge)
|
|
61
|
-
|
|
62
|
-
new_config = sub_config_merge(config, key, config_params)
|
|
63
|
-
|
|
64
|
-
new_configuration.send("#{key}=", new_config)
|
|
65
|
-
end
|
|
66
|
-
@logger = nil
|
|
67
|
-
|
|
68
|
-
new_configuration
|
|
38
|
+
def lister
|
|
39
|
+
@lister ||= Config::ListerConfig.new(**@initial_config.fetch(:lister, {}))
|
|
69
40
|
end
|
|
70
41
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
config.merge(config_params[key.to_s])
|
|
81
|
-
elsif config_params&.key?(key.to_sym)
|
|
82
|
-
config.merge(config_params[key.to_sym])
|
|
83
|
-
else
|
|
84
|
-
config.merge(config_params || {})
|
|
85
|
-
end
|
|
42
|
+
def to_h
|
|
43
|
+
{
|
|
44
|
+
providers: providers.to_h,
|
|
45
|
+
llm: llm.to_h,
|
|
46
|
+
log: log.to_h,
|
|
47
|
+
ranker: ranker.to_h,
|
|
48
|
+
extractor: extractor.to_h,
|
|
49
|
+
lister: lister.to_h
|
|
50
|
+
}
|
|
86
51
|
end
|
|
87
|
-
|
|
88
|
-
attr_writer(*SUB_CONFIGS, :logger)
|
|
89
52
|
end
|
|
90
53
|
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveGenie
|
|
4
|
+
class Result
|
|
5
|
+
attr_reader :data, :reasoning, :metadata
|
|
6
|
+
|
|
7
|
+
def initialize(data:, metadata:, reasoning: nil)
|
|
8
|
+
@data = data
|
|
9
|
+
@reasoning = reasoning
|
|
10
|
+
@metadata = metadata
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def explanation
|
|
14
|
+
@reasoning
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_h
|
|
18
|
+
{ data: @data, reasoning: @reasoning, metadata: @metadata }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def to_s
|
|
22
|
+
to_h.to_s
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def to_json(...)
|
|
26
|
+
to_h.to_json(...)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
module ActiveGenie
|
|
4
4
|
class ProviderServerError < StandardError
|
|
5
|
-
TEXT = <<~TEXT
|
|
6
|
-
Provider server error:
|
|
7
|
-
|
|
5
|
+
TEXT = <<~TEXT
|
|
6
|
+
Provider server error: %<code>s
|
|
7
|
+
%<body>s
|
|
8
8
|
|
|
9
9
|
Providers errors are common and can occur for various reasons, such as:
|
|
10
10
|
- Invalid API key
|
|
@@ -16,8 +16,11 @@ module ActiveGenie
|
|
|
16
16
|
TEXT
|
|
17
17
|
|
|
18
18
|
def initialize(response)
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
super(format(
|
|
20
|
+
TEXT,
|
|
21
|
+
code: response&.code.to_s,
|
|
22
|
+
body: response&.body.to_s.strip.empty? ? '(no body)' : response&.body.to_s
|
|
23
|
+
))
|
|
21
24
|
end
|
|
22
25
|
end
|
|
23
26
|
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveGenie
|
|
4
|
+
class WithoutAvailableProviderError < StandardError
|
|
5
|
+
TEXT = <<~TEXT
|
|
6
|
+
Missing at least one credentialed provider to proceed.
|
|
7
|
+
|
|
8
|
+
To configure ActiveGenie, you can either:
|
|
9
|
+
1. Set up global configuration:
|
|
10
|
+
```ruby
|
|
11
|
+
ActiveGenie.configure do |config|
|
|
12
|
+
config.providers.default = 'openai'
|
|
13
|
+
config.providers.openai.api_key = 'your_api_key'
|
|
14
|
+
# ... other configuration options
|
|
15
|
+
end
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
2. Or pass configuration directly to the method call:
|
|
19
|
+
```ruby
|
|
20
|
+
ActiveGenie::Extractor.call(
|
|
21
|
+
arg1,
|
|
22
|
+
arg2,
|
|
23
|
+
config: {
|
|
24
|
+
providers: {
|
|
25
|
+
default: 'openai',
|
|
26
|
+
openai: {
|
|
27
|
+
api_key: 'your_api_key'
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
)
|
|
32
|
+
```
|
|
33
|
+
TEXT
|
|
34
|
+
|
|
35
|
+
def initialize
|
|
36
|
+
super(TEXT)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Extract structured and typed data from user messages.
|
|
2
|
+
Identify relevant information within user messages and categorize it into predefined data fields with specific data types.
|
|
3
|
+
|
|
4
|
+
# Steps
|
|
5
|
+
1. **Identify Data Types**: Determine the types of data to collect, such as names, dates, email addresses, phone numbers, etc.
|
|
6
|
+
2. **Extract Information**: Use pattern recognition and language understanding to identify and extract the relevant pieces of data from the user message.
|
|
7
|
+
3. **Categorize Data**: Assign the extracted data to the appropriate predefined fields.
|
|
8
|
+
|
|
9
|
+
# Notes
|
|
10
|
+
- Handle missing or partial information gracefully.
|
|
11
|
+
- Manage multiple occurrences of similar data points by prioritizing the first one unless specified otherwise.
|
|
12
|
+
- Be flexible to handle variations in data format and language clues.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../providers/unified_provider'
|
|
4
|
+
|
|
5
|
+
module ActiveGenie
|
|
6
|
+
module Extractor
|
|
7
|
+
class Data < ActiveGenie::BaseModule
|
|
8
|
+
# Extracts structured data from text based on a predefined schema.
|
|
9
|
+
#
|
|
10
|
+
# @param text [String] The input text to analyze and extract data from
|
|
11
|
+
# @param data_to_extract [Hash] Schema defining the data structure to extract.
|
|
12
|
+
# Each key in the hash represents a field to extract, and its value defines the expected type and constraints.
|
|
13
|
+
# @param config [Hash] Additional config for the extraction process
|
|
14
|
+
#
|
|
15
|
+
# @return [Hash] The extracted data matching the schema structure. Each field will include
|
|
16
|
+
# both the extracted value.
|
|
17
|
+
#
|
|
18
|
+
# @example Extract a person's details
|
|
19
|
+
# schema = {
|
|
20
|
+
# name: { type: 'string', description: 'Full name of the person' },
|
|
21
|
+
# age: { type: 'integer', description: 'Age in years' }
|
|
22
|
+
# }
|
|
23
|
+
# text = "John Doe is 25 years old"
|
|
24
|
+
# Extractor.with_explanation(text, schema)
|
|
25
|
+
# # => { name: "John Doe", age: 25 }
|
|
26
|
+
def initialize(text, data_to_extract, config: {})
|
|
27
|
+
@text = text
|
|
28
|
+
@data_to_extract = data_to_extract
|
|
29
|
+
super(config:)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def call
|
|
33
|
+
messages = [
|
|
34
|
+
{ role: 'system', content: prompt },
|
|
35
|
+
{ role: 'user', content: @text }
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
function = JSON.parse(File.read(File.join(__dir__, 'data.json')), symbolize_names: true)
|
|
39
|
+
function[:parameters][:properties] = @data_to_extract
|
|
40
|
+
function[:parameters][:required] = @data_to_extract.keys
|
|
41
|
+
|
|
42
|
+
provider_response = ::ActiveGenie::Providers::UnifiedProvider.function_calling(
|
|
43
|
+
messages,
|
|
44
|
+
function,
|
|
45
|
+
config: config
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
response_formatted(provider_response)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def response_formatted(provider_response)
|
|
54
|
+
data = provider_response.compact
|
|
55
|
+
|
|
56
|
+
ActiveGenie::Result.new(
|
|
57
|
+
data:,
|
|
58
|
+
metadata: provider_response
|
|
59
|
+
)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def prompt
|
|
63
|
+
File.read(File.join(__dir__, 'data.prompt.md'))
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def module_config
|
|
67
|
+
{ llm: { recommended_model: 'deepseek-chat' } }
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative '../providers/unified_provider'
|
|
4
|
+
require_relative '../utils/deep_merge'
|
|
4
5
|
|
|
5
6
|
module ActiveGenie
|
|
6
7
|
module Extractor
|
|
7
|
-
class Explanation
|
|
8
|
-
def self.call(...)
|
|
9
|
-
new(...).call
|
|
10
|
-
end
|
|
11
|
-
|
|
8
|
+
class Explanation < ActiveGenie::BaseModule
|
|
12
9
|
# Extracts structured data from text based on a predefined schema.
|
|
13
10
|
#
|
|
14
11
|
# @param text [String] The input text to analyze and extract data from
|
|
@@ -31,7 +28,7 @@ module ActiveGenie
|
|
|
31
28
|
def initialize(text, data_to_extract, config: {})
|
|
32
29
|
@text = text
|
|
33
30
|
@data_to_extract = data_to_extract
|
|
34
|
-
|
|
31
|
+
super(config:)
|
|
35
32
|
end
|
|
36
33
|
|
|
37
34
|
def call
|
|
@@ -46,16 +43,18 @@ module ActiveGenie
|
|
|
46
43
|
function[:parameters][:properties] = properties
|
|
47
44
|
function[:parameters][:required] = properties.keys
|
|
48
45
|
|
|
49
|
-
|
|
46
|
+
provider_response = ::ActiveGenie::Providers::UnifiedProvider.function_calling(
|
|
47
|
+
messages,
|
|
48
|
+
function,
|
|
49
|
+
config: config
|
|
50
|
+
)
|
|
50
51
|
|
|
51
|
-
|
|
52
|
+
response_formatted(provider_response)
|
|
52
53
|
end
|
|
53
54
|
|
|
54
55
|
private
|
|
55
56
|
|
|
56
57
|
def data_to_extract_with_explanation
|
|
57
|
-
return @data_to_extract unless config.extractor.with_explanation
|
|
58
|
-
|
|
59
58
|
with_explanation = {}
|
|
60
59
|
|
|
61
60
|
@data_to_extract.each do |key, value|
|
|
@@ -80,38 +79,16 @@ module ActiveGenie
|
|
|
80
79
|
with_explanation
|
|
81
80
|
end
|
|
82
81
|
|
|
83
|
-
def
|
|
84
|
-
|
|
85
|
-
messages,
|
|
86
|
-
function,
|
|
87
|
-
config: config
|
|
88
|
-
)
|
|
82
|
+
def response_formatted(provider_response)
|
|
83
|
+
data = provider_response.slice(*@data_to_extract.keys.map(&:to_s)).transform_keys(&:to_sym)
|
|
89
84
|
|
|
90
|
-
|
|
91
|
-
{
|
|
92
|
-
code: :extractor,
|
|
93
|
-
text: @text[0..30],
|
|
94
|
-
data_to_extract: function[:parameters][:properties],
|
|
95
|
-
extracted_data: response
|
|
96
|
-
}
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
response
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def simplify_response(response)
|
|
103
|
-
return response if config.extractor.verbose
|
|
104
|
-
|
|
105
|
-
simplified_response = {}
|
|
106
|
-
|
|
107
|
-
@data_to_extract.each_key do |key|
|
|
108
|
-
next unless response.key?(key.to_s)
|
|
109
|
-
next if response.key?("#{key}_accuracy") && response["#{key}_accuracy"] < min_accuracy
|
|
85
|
+
first_reasoning_key = provider_response["#{provider_response.keys.first}_explanation"]
|
|
110
86
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
87
|
+
ActiveGenie::Result.new(
|
|
88
|
+
data:,
|
|
89
|
+
reasoning: first_reasoning_key,
|
|
90
|
+
metadata: provider_response
|
|
91
|
+
)
|
|
115
92
|
end
|
|
116
93
|
|
|
117
94
|
def min_accuracy
|
|
@@ -122,13 +99,8 @@ module ActiveGenie
|
|
|
122
99
|
File.read(File.join(__dir__, 'explanation.prompt.md'))
|
|
123
100
|
end
|
|
124
101
|
|
|
125
|
-
def
|
|
126
|
-
|
|
127
|
-
c = ActiveGenie.configuration.merge(@initial_config)
|
|
128
|
-
c.llm.recommended_model = 'deepseek-chat' unless c.llm.recommended_model
|
|
129
|
-
|
|
130
|
-
c
|
|
131
|
-
end
|
|
102
|
+
def module_config
|
|
103
|
+
{ llm: { recommended_model: 'deepseek-chat' } }
|
|
132
104
|
end
|
|
133
105
|
end
|
|
134
106
|
end
|
|
@@ -4,11 +4,7 @@ require_relative 'explanation'
|
|
|
4
4
|
|
|
5
5
|
module ActiveGenie
|
|
6
6
|
module Extractor
|
|
7
|
-
class Litote
|
|
8
|
-
def self.call(...)
|
|
9
|
-
new(...).call
|
|
10
|
-
end
|
|
11
|
-
|
|
7
|
+
class Litote < ActiveGenie::BaseModule
|
|
12
8
|
# Extracts data from informal text while also detecting litotes and their meanings.
|
|
13
9
|
# This method extends the basic extraction by analyzing rhetorical devices.
|
|
14
10
|
#
|
|
@@ -31,13 +27,16 @@ module ActiveGenie
|
|
|
31
27
|
def initialize(text, data_to_extract, config: {})
|
|
32
28
|
@text = text
|
|
33
29
|
@data_to_extract = data_to_extract
|
|
34
|
-
|
|
30
|
+
super(config:)
|
|
35
31
|
end
|
|
36
32
|
|
|
37
33
|
def call
|
|
38
34
|
response = Explanation.call(@text, extract_with_litote, config:)
|
|
39
35
|
|
|
40
|
-
|
|
36
|
+
if response.data[:message_litote]
|
|
37
|
+
response = Explanation.call(response.data[:litote_rephrased], @data_to_extract,
|
|
38
|
+
config:)
|
|
39
|
+
end
|
|
41
40
|
|
|
42
41
|
response
|
|
43
42
|
end
|
|
@@ -50,13 +49,8 @@ module ActiveGenie
|
|
|
50
49
|
@data_to_extract.merge(parameters)
|
|
51
50
|
end
|
|
52
51
|
|
|
53
|
-
def
|
|
54
|
-
|
|
55
|
-
c = ActiveGenie.configuration.merge(@initial_config)
|
|
56
|
-
c.llm.recommended_model = 'deepseek-chat' unless c.llm.recommended_model
|
|
57
|
-
|
|
58
|
-
c
|
|
59
|
-
end
|
|
52
|
+
def module_config
|
|
53
|
+
{ llm: { recommended_model: 'deepseek-chat' } }
|
|
60
54
|
end
|
|
61
55
|
end
|
|
62
56
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'extractor/explanation'
|
|
4
|
+
require_relative 'extractor/data'
|
|
4
5
|
require_relative 'extractor/litote'
|
|
5
6
|
|
|
6
7
|
module ActiveGenie
|
|
@@ -15,6 +16,10 @@ module ActiveGenie
|
|
|
15
16
|
Explanation.call(...)
|
|
16
17
|
end
|
|
17
18
|
|
|
19
|
+
def data(...)
|
|
20
|
+
Data.call(...)
|
|
21
|
+
end
|
|
22
|
+
|
|
18
23
|
def with_litote(...)
|
|
19
24
|
Litote.call(...)
|
|
20
25
|
end
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
"type": "string",
|
|
9
9
|
"description": "The theme for the feud."
|
|
10
10
|
},
|
|
11
|
+
"why_these_items": {
|
|
12
|
+
"type": "string",
|
|
13
|
+
"description": "Explanation of why these items were chosen."
|
|
14
|
+
},
|
|
11
15
|
"items": {
|
|
12
16
|
"type": "array",
|
|
13
17
|
"description": "The list of items for the feud.",
|
|
@@ -16,6 +20,6 @@
|
|
|
16
20
|
}
|
|
17
21
|
}
|
|
18
22
|
},
|
|
19
|
-
"required": ["theme", "items"]
|
|
23
|
+
"required": ["theme", "why_these_items", "items"]
|
|
20
24
|
}
|
|
21
25
|
}
|
|
@@ -9,17 +9,13 @@ module ActiveGenie
|
|
|
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")
|
|
11
11
|
#
|
|
12
|
-
class Feud
|
|
13
|
-
def self.call(...)
|
|
14
|
-
new(...).call
|
|
15
|
-
end
|
|
16
|
-
|
|
12
|
+
class Feud < ActiveGenie::BaseModule
|
|
17
13
|
# @param theme [String] The theme for the feud
|
|
18
14
|
# @param config [Hash] Additional configuration options
|
|
19
15
|
# @return [Array of strings] List of items
|
|
20
16
|
def initialize(theme, config: {})
|
|
21
17
|
@theme = theme
|
|
22
|
-
|
|
18
|
+
super(config:)
|
|
23
19
|
end
|
|
24
20
|
|
|
25
21
|
# @return [Array of strings] The list of items
|
|
@@ -30,14 +26,17 @@ module ActiveGenie
|
|
|
30
26
|
{ role: 'user', content: "theme: #{@theme}" }
|
|
31
27
|
]
|
|
32
28
|
|
|
33
|
-
|
|
29
|
+
provider_response = ::ActiveGenie::Providers::UnifiedProvider.function_calling(
|
|
34
30
|
messages,
|
|
35
31
|
FUNCTION,
|
|
36
32
|
config:
|
|
37
33
|
)
|
|
38
34
|
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
ActiveGenie::Result.new(
|
|
36
|
+
data: provider_response['items'] || [],
|
|
37
|
+
reasoning: provider_response['why_these_items'],
|
|
38
|
+
metadata: provider_response
|
|
39
|
+
)
|
|
41
40
|
end
|
|
42
41
|
|
|
43
42
|
PROMPT = File.read(File.join(__dir__, 'feud.prompt.md'))
|
|
@@ -49,21 +48,8 @@ module ActiveGenie
|
|
|
49
48
|
config.lister.number_of_items
|
|
50
49
|
end
|
|
51
50
|
|
|
52
|
-
def
|
|
53
|
-
|
|
54
|
-
code: :feud,
|
|
55
|
-
theme: @theme[0..30],
|
|
56
|
-
items: response['items'].map { |item| item[0..30] }
|
|
57
|
-
)
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def config
|
|
61
|
-
@config ||= begin
|
|
62
|
-
c = ActiveGenie.configuration.merge(@initial_config)
|
|
63
|
-
c.llm.recommended_model = 'deepseek-chat' unless c.llm.recommended_model
|
|
64
|
-
|
|
65
|
-
c
|
|
66
|
-
end
|
|
51
|
+
def module_config
|
|
52
|
+
{ llm: { recommended_model: 'claude-haiku-4-5' } }
|
|
67
53
|
end
|
|
68
54
|
end
|
|
69
55
|
end
|
|
@@ -16,11 +16,7 @@ module ActiveGenie
|
|
|
16
16
|
# "Evaluate technical accuracy and clarity")
|
|
17
17
|
# # => [ "API Architect", "Technical Writer", "Developer Advocate" ]
|
|
18
18
|
#
|
|
19
|
-
class Juries
|
|
20
|
-
def self.call(...)
|
|
21
|
-
new(...).call
|
|
22
|
-
end
|
|
23
|
-
|
|
19
|
+
class Juries < ActiveGenie::BaseModule
|
|
24
20
|
# Initializes a new jury recommendation instance
|
|
25
21
|
#
|
|
26
22
|
# @param text [String] The text content to analyze for jury recommendations
|
|
@@ -29,7 +25,7 @@ module ActiveGenie
|
|
|
29
25
|
def initialize(text, criteria, config: {})
|
|
30
26
|
@text = text
|
|
31
27
|
@criteria = criteria
|
|
32
|
-
|
|
28
|
+
super(config:)
|
|
33
29
|
end
|
|
34
30
|
|
|
35
31
|
def call
|
|
@@ -45,7 +41,10 @@ module ActiveGenie
|
|
|
45
41
|
parameters: {
|
|
46
42
|
type: 'object',
|
|
47
43
|
properties: {
|
|
48
|
-
|
|
44
|
+
why_these_juries: {
|
|
45
|
+
type: 'string',
|
|
46
|
+
description: 'A brief explanation of why these juries were chosen.'
|
|
47
|
+
},
|
|
49
48
|
juries: {
|
|
50
49
|
type: 'array',
|
|
51
50
|
description: 'The list of best juries',
|
|
@@ -58,32 +57,27 @@ module ActiveGenie
|
|
|
58
57
|
}
|
|
59
58
|
}
|
|
60
59
|
|
|
61
|
-
|
|
60
|
+
provider_response = ::ActiveGenie::Providers::UnifiedProvider.function_calling(
|
|
62
61
|
messages,
|
|
63
62
|
function,
|
|
64
63
|
config:
|
|
65
64
|
)
|
|
66
65
|
|
|
67
|
-
|
|
66
|
+
ActiveGenie::Result.new(
|
|
67
|
+
data: provider_response['juries'] || [],
|
|
68
|
+
reasoning: provider_response['why_these_juries'],
|
|
69
|
+
metadata: provider_response
|
|
70
|
+
)
|
|
68
71
|
end
|
|
69
72
|
|
|
70
73
|
private
|
|
71
74
|
|
|
72
|
-
def client
|
|
73
|
-
::ActiveGenie::Providers::UnifiedProvider
|
|
74
|
-
end
|
|
75
|
-
|
|
76
75
|
def prompt
|
|
77
76
|
@prompt ||= File.read(File.join(__dir__, 'juries.prompt.md'))
|
|
78
77
|
end
|
|
79
78
|
|
|
80
|
-
def
|
|
81
|
-
|
|
82
|
-
c = ActiveGenie.configuration.merge(@initial_config)
|
|
83
|
-
c.llm.recommended_model = 'deepseek-chat' unless c.llm.recommended_model
|
|
84
|
-
|
|
85
|
-
c
|
|
86
|
-
end
|
|
79
|
+
def module_config
|
|
80
|
+
{ llm: { recommended_model: 'deepseek-chat' } }
|
|
87
81
|
end
|
|
88
82
|
end
|
|
89
83
|
end
|