active_genie 0.0.8 → 0.0.10
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 +34 -33
- data/VERSION +1 -1
- data/lib/active_genie/battle/README.md +2 -2
- data/lib/active_genie/battle/basic.rb +24 -19
- data/lib/active_genie/battle.rb +1 -1
- data/lib/active_genie/clients/openai_client.rb +77 -0
- data/lib/active_genie/clients/unified_client.rb +19 -0
- data/lib/active_genie/configuration/log_config.rb +14 -0
- data/lib/active_genie/configuration/openai_config.rb +56 -0
- data/lib/active_genie/configuration/providers_config.rb +37 -0
- data/lib/active_genie/configuration.rb +18 -23
- data/lib/active_genie/data_extractor/README.md +4 -4
- data/lib/active_genie/data_extractor/basic.rb +19 -9
- data/lib/active_genie/data_extractor/from_informal.rb +18 -7
- data/lib/active_genie/data_extractor.rb +1 -1
- data/lib/active_genie/league/README.md +43 -0
- data/lib/active_genie/{leaderboard → league}/elo_ranking.rb +41 -8
- data/lib/active_genie/league/free_for_all.rb +62 -0
- data/lib/active_genie/league/league.rb +120 -0
- data/lib/active_genie/{leaderboard → league}/player.rb +17 -10
- data/lib/active_genie/league.rb +12 -0
- data/lib/active_genie/logger.rb +45 -0
- data/lib/active_genie/scoring/README.md +4 -8
- data/lib/active_genie/scoring/basic.rb +19 -10
- data/lib/active_genie/scoring/recommended_reviews.rb +7 -9
- data/lib/active_genie/scoring.rb +1 -1
- data/lib/active_genie.rb +9 -17
- data/lib/tasks/install.rake +3 -3
- data/lib/tasks/templates/active_genie.rb +17 -0
- metadata +85 -80
- data/lib/active_genie/clients/openai.rb +0 -61
- data/lib/active_genie/clients/router.rb +0 -41
- data/lib/active_genie/leaderboard/leaderboard.rb +0 -72
- data/lib/active_genie/leaderboard/league.rb +0 -48
- data/lib/active_genie/leaderboard.rb +0 -11
- data/lib/active_genie/utils/math.rb +0 -15
- data/lib/tasks/templates/active_genie.yml +0 -7
- /data/lib/active_genie/{leaderboard → league}/players_collection.rb +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9d0424a39ba21d821cb2419730387e1b026c35b5e2e5dff9f6d615f3ec54e6a3
|
4
|
+
data.tar.gz: 17b460ccd1a689d0f8709af2b84f3cde65aa0075b76104ee1b4bb8b3b0ffc182
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ad98b2d5d063d0d1c4009e1a9f92d6d326ed948cbdee71317c94b6a4a0ee57042609c1f405ca7ce00d4beb321bc1e98ad722646349076929bcdc0a28da8b6b8b
|
7
|
+
data.tar.gz: d2cc39b77757619c5235041f12d5182778b4237444f3c2246982ebcf54c0542af0da194783cea57fbe4fbde0985ef635802c648796b4f5c4d7a5d4f42c6519c7
|
data/README.md
CHANGED
@@ -10,9 +10,7 @@ ActiveGenie is a Ruby gem that provides a polished, production-ready interface f
|
|
10
10
|
|
11
11
|
- 🎯 **Data Extraction**: Extract structured data from unstructured text with type validation
|
12
12
|
- 📊 **Smart Scoring**: Multi-reviewer evaluation system with automatic expert selection
|
13
|
-
- 💭 **
|
14
|
-
- 🔒 **Safe & Secure**: Built-in validation and sanitization
|
15
|
-
- 🛠️ **Configurable**: Supports multiple GenAI providers and models
|
13
|
+
- 💭 **Leaderboard**: Consistent rank items based on custom criteria, using multiple tecniques of ranking
|
16
14
|
|
17
15
|
## Installation
|
18
16
|
|
@@ -32,19 +30,13 @@ echo "ActiveGenie.load_tasks" >> Rakefile
|
|
32
30
|
rails g active_genie:install
|
33
31
|
```
|
34
32
|
|
35
|
-
4.
|
36
|
-
```
|
37
|
-
|
38
|
-
api_key
|
39
|
-
|
40
|
-
|
41
|
-
claude-3-5-sonnet:
|
42
|
-
api_key: <%= ENV['ANTHROPIC_API_KEY'] %>
|
43
|
-
provider: "anthropic"
|
33
|
+
4. Configure your credentials in `config/initializers/active_genie.rb`:
|
34
|
+
```ruby
|
35
|
+
ActiveGenie.configure do |config|
|
36
|
+
config.openai.api_key = ENV['OPENAI_API_KEY']
|
37
|
+
end
|
44
38
|
```
|
45
39
|
|
46
|
-
> The first key will be used as default in all modules, in this example `GPT-4o-mini`
|
47
|
-
|
48
40
|
## Quick Start
|
49
41
|
|
50
42
|
### Data Extractor
|
@@ -94,7 +86,7 @@ Text evaluation system that provides detailed scoring and feedback using multipl
|
|
94
86
|
text = "The code implements a binary search algorithm with O(log n) complexity"
|
95
87
|
criteria = "Evaluate technical accuracy and clarity"
|
96
88
|
|
97
|
-
result = ActiveGenie::Scoring
|
89
|
+
result = ActiveGenie::Scoring.basic(text, criteria)
|
98
90
|
# => {
|
99
91
|
# algorithm_expert_score: 95,
|
100
92
|
# algorithm_expert_reasoning: "Accurately describes binary search and its complexity",
|
@@ -122,7 +114,7 @@ player_a = "Implementation uses dependency injection for better testability"
|
|
122
114
|
player_b = "Code has high test coverage but tightly coupled components"
|
123
115
|
criteria = "Evaluate code quality and maintainability"
|
124
116
|
|
125
|
-
result = ActiveGenie::Battle
|
117
|
+
result = ActiveGenie::Battle.call(player_a, player_b, criteria)
|
126
118
|
# => {
|
127
119
|
# winner_player: "Implementation uses dependency injection for better testability",
|
128
120
|
# reasoning: "Player A's implementation demonstrates better maintainability through dependency injection,
|
@@ -140,6 +132,29 @@ Features:
|
|
140
132
|
|
141
133
|
See the [Battle README](lib/active_genie/battle/README.md) for advanced usage, custom reviewers, and detailed interface documentation.
|
142
134
|
|
135
|
+
### League
|
136
|
+
The League module provides competitive ranking through multi-stage evaluation:
|
137
|
+
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
require 'active_genie'
|
141
|
+
|
142
|
+
players = ['REST API', 'GraphQL API', 'SOAP API', 'gRPC API', 'Websocket API']
|
143
|
+
criteria = "Best one to be used into a high changing environment"
|
144
|
+
|
145
|
+
result = ActiveGenie::League.call(players, criteria)
|
146
|
+
# => {
|
147
|
+
# winner_player: "gRPC API",
|
148
|
+
# reasoning: "gRPC API is the best one to be used into a high changing environment",
|
149
|
+
# }
|
150
|
+
```
|
151
|
+
|
152
|
+
- **Multi-phase ranking system** combining expert scoring and ELO algorithms
|
153
|
+
- **Automatic elimination** of inconsistent performers using statistical analysis
|
154
|
+
- **Dynamic ranking adjustments** based on simulated pairwise battles, from bottom to top
|
155
|
+
|
156
|
+
See the [League README](lib/active_genie/league/README.md) for implementation details, configuration, and advanced ranking strategies.
|
157
|
+
|
143
158
|
### Summarizer (WIP)
|
144
159
|
The summarizer is a tool that can be used to summarize a given text. It uses a set of rules to summarize the text out of the box. Uses the best practices of prompt engineering and engineering to make the summarization as accurate as possible.
|
145
160
|
|
@@ -184,22 +199,9 @@ sentiment = ActiveGenie::SentimentAnalyzer.call(text)
|
|
184
199
|
puts sentiment # => "positive"
|
185
200
|
```
|
186
201
|
|
187
|
-
|
188
|
-
The Elo ranking is a tool that can be used to rank a set of items. It uses a set of rules to rank the items out of the box. Uses the best practices of prompt engineering and engineering to make the ranking as accurate as possible.
|
189
|
-
|
190
|
-
```ruby
|
191
|
-
require 'active_genie'
|
202
|
+
## Configuration
|
192
203
|
|
193
|
-
|
194
|
-
criterias = 'items that look rounded'
|
195
|
-
ranked_items = ActiveGenie::EloRanking.call(items, criterias, rounds: 10)
|
196
|
-
puts ranked_items # => [{ name: "Circle", score: 1500 }, { name: "Square", score: 800 }, { name: "Triangle", score: 800 }]
|
197
|
-
```
|
198
|
-
|
199
|
-
|
200
|
-
## Configuration Options
|
201
|
-
|
202
|
-
| Option | Description | Default |
|
204
|
+
| Config | Description | Default |
|
203
205
|
|--------|-------------|---------|
|
204
206
|
| `provider` | LLM provider (openai, anthropic, etc) | `nil` |
|
205
207
|
| `model` | Model to use | `nil` |
|
@@ -207,7 +209,7 @@ puts ranked_items # => [{ name: "Circle", score: 1500 }, { name: "Square", score
|
|
207
209
|
| `timeout` | Request timeout in seconds | `5` |
|
208
210
|
| `max_retries` | Maximum retry attempts | `3` |
|
209
211
|
|
210
|
-
> **Note:** Each module can append its own set of configuration
|
212
|
+
> **Note:** Each module can append its own set of configuration, see the individual module documentation for details.
|
211
213
|
|
212
214
|
## Contributing
|
213
215
|
|
@@ -219,4 +221,3 @@ puts ranked_items # => [{ name: "Circle", score: 1500 }, { name: "Square", score
|
|
219
221
|
## License
|
220
222
|
|
221
223
|
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
222
|
-
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.10
|
@@ -27,11 +27,11 @@ result = ActiveGenie::Battle::Basic.call(player_a, player_b, criteria)
|
|
27
27
|
```
|
28
28
|
|
29
29
|
## Interface
|
30
|
-
### Basic.call(player_a, player_b, criteria,
|
30
|
+
### Basic.call(player_a, player_b, criteria, config: {})
|
31
31
|
- `player_a` [String, Hash] - The content or submission from the first player
|
32
32
|
- `player_b` [String, Hash] - The content or submission from the second player
|
33
33
|
- `criteria` [String] - The evaluation criteria or rules to assess against
|
34
|
-
- `
|
34
|
+
- `config` [Hash] - Additional configuration config that modify the battle evaluation behavior
|
35
35
|
|
36
36
|
Returns a Hash containing:
|
37
37
|
- `winner_player` [String, Hash] - The winning player's content (either player_a or player_b)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '../clients/
|
3
|
+
require_relative '../clients/unified_client'
|
4
4
|
|
5
5
|
module ActiveGenie::Battle
|
6
6
|
# The Basic class provides a foundation for evaluating battles between two players
|
@@ -14,23 +14,23 @@ module ActiveGenie::Battle
|
|
14
14
|
# Basic.call("Player A content", "Player B content", "Evaluate keyword usage and pattern matching")
|
15
15
|
#
|
16
16
|
class Basic
|
17
|
-
def self.call(player_a, player_b, criteria,
|
18
|
-
new(player_a, player_b, criteria,
|
17
|
+
def self.call(player_a, player_b, criteria, config: {})
|
18
|
+
new(player_a, player_b, criteria, config:).call
|
19
19
|
end
|
20
20
|
|
21
21
|
# @param player_a [String] The content or submission from the first player
|
22
22
|
# @param player_b [String] The content or submission from the second player
|
23
23
|
# @param criteria [String] The evaluation criteria or rules to assess against
|
24
|
-
# @param
|
24
|
+
# @param config [Hash] Additional configuration config that modify the battle evaluation behavior
|
25
25
|
# @return [Hash] The evaluation result containing the winner and reasoning
|
26
26
|
# @return [String] :winner The @param player_a or player_b
|
27
27
|
# @return [String] :reasoning Detailed explanation of why the winner was chosen
|
28
28
|
# @return [String] :what_could_be_changed_to_avoid_draw A suggestion on how to avoid a draw
|
29
|
-
def initialize(player_a, player_b, criteria,
|
29
|
+
def initialize(player_a, player_b, criteria, config: {})
|
30
30
|
@player_a = player_a
|
31
31
|
@player_b = player_b
|
32
32
|
@criteria = criteria
|
33
|
-
@
|
33
|
+
@config = config
|
34
34
|
@response = nil
|
35
35
|
end
|
36
36
|
|
@@ -42,7 +42,7 @@ module ActiveGenie::Battle
|
|
42
42
|
{ role: 'user', content: "player_b: #{player_content(@player_b)}" },
|
43
43
|
]
|
44
44
|
|
45
|
-
@response = ::ActiveGenie::Clients::
|
45
|
+
@response = ::ActiveGenie::Clients::UnifiedClient.function_calling(messages, FUNCTION, config:)
|
46
46
|
|
47
47
|
response_formatted
|
48
48
|
end
|
@@ -56,18 +56,12 @@ module ActiveGenie::Battle
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def response_formatted
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
else
|
66
|
-
@response['winner'] = nil
|
67
|
-
@response['loser'] = nil
|
68
|
-
end
|
69
|
-
|
70
|
-
@response
|
59
|
+
winner = case @response['winner']
|
60
|
+
when 'player_a' then @player_a
|
61
|
+
when 'player_b' then @player_b
|
62
|
+
end
|
63
|
+
|
64
|
+
@response.merge!('winner' => winner, 'loser' => winner ? (winner == @player_a ? @player_b : @player_a) : nil)
|
71
65
|
end
|
72
66
|
|
73
67
|
PROMPT = <<~PROMPT
|
@@ -121,5 +115,16 @@ module ActiveGenie::Battle
|
|
121
115
|
}
|
122
116
|
}
|
123
117
|
}
|
118
|
+
|
119
|
+
def config
|
120
|
+
{
|
121
|
+
all_providers: { model_tier: 'lower_tier' },
|
122
|
+
log: {
|
123
|
+
**(@config.dig(:log) || {}),
|
124
|
+
trace: self.class.name,
|
125
|
+
},
|
126
|
+
**@config
|
127
|
+
}
|
128
|
+
end
|
124
129
|
end
|
125
130
|
end
|
data/lib/active_genie/battle.rb
CHANGED
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
module ActiveGenie::Clients
|
5
|
+
class OpenaiClient
|
6
|
+
def initialize(config)
|
7
|
+
@app_config = config
|
8
|
+
end
|
9
|
+
|
10
|
+
def function_calling(messages, function, config: {})
|
11
|
+
model = config[:model]
|
12
|
+
model = @app_config.tier_to_model(config.dig(:all_providers, :model_tier)) if model.nil? && config.dig(:all_providers, :model_tier)
|
13
|
+
model = @app_config.lower_tier_model if model.nil?
|
14
|
+
|
15
|
+
payload = {
|
16
|
+
messages:,
|
17
|
+
response_format: {
|
18
|
+
type: 'json_schema',
|
19
|
+
json_schema: function
|
20
|
+
},
|
21
|
+
model:,
|
22
|
+
}
|
23
|
+
|
24
|
+
api_key = config[:api_key] || @app_config.api_key
|
25
|
+
headers = DEFAULT_HEADERS.merge(
|
26
|
+
'Authorization': "Bearer #{api_key}"
|
27
|
+
).compact
|
28
|
+
|
29
|
+
response = request(payload, headers, config:)
|
30
|
+
|
31
|
+
parsed_response = JSON.parse(response.dig('choices', 0, 'message', 'content'))
|
32
|
+
parsed_response.dig('properties') || parsed_response
|
33
|
+
rescue JSON::ParserError
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def request(payload, headers, config:)
|
40
|
+
start_time = Time.now
|
41
|
+
response = Net::HTTP.post(
|
42
|
+
URI("#{@app_config.api_url}/chat/completions"),
|
43
|
+
payload.to_json,
|
44
|
+
headers
|
45
|
+
)
|
46
|
+
|
47
|
+
raise OpenaiError, response.body unless response.is_a?(Net::HTTPSuccess)
|
48
|
+
return nil if response.body.empty?
|
49
|
+
|
50
|
+
parsed_body = JSON.parse(response.body)
|
51
|
+
log_response(start_time, parsed_body, config:)
|
52
|
+
|
53
|
+
parsed_body
|
54
|
+
end
|
55
|
+
|
56
|
+
DEFAULT_HEADERS = {
|
57
|
+
'Content-Type': 'application/json',
|
58
|
+
}
|
59
|
+
|
60
|
+
def log_response(start_time, response, config:)
|
61
|
+
ActiveGenie::Logger.trace(
|
62
|
+
{
|
63
|
+
**config.dig(:log),
|
64
|
+
category: :llm,
|
65
|
+
trace: "#{config.dig(:log, :trace)}/#{self.class.name}",
|
66
|
+
total_tokens: response.dig('usage', 'total_tokens'),
|
67
|
+
model: response.dig('model'),
|
68
|
+
request_duration: Time.now - start_time,
|
69
|
+
openai: response
|
70
|
+
}
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
# TODO: add some more rich error handling
|
75
|
+
class OpenaiError < StandardError; end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ActiveGenie::Clients
|
2
|
+
class UnifiedClient
|
3
|
+
class << self
|
4
|
+
def function_calling(messages, function, config: {})
|
5
|
+
provider_name = config[:provider]&.downcase&.strip&.to_sym
|
6
|
+
provider = ActiveGenie.configuration.providers.all[provider_name] || ActiveGenie.configuration.providers.default
|
7
|
+
|
8
|
+
raise InvalidProviderError if provider.nil? || provider.client.nil?
|
9
|
+
|
10
|
+
provider.client.function_calling(messages, function, config:)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
# TODO: improve error message
|
16
|
+
class InvalidProviderError < StandardError; end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require_relative '../clients/openai_client'
|
2
|
+
|
3
|
+
module ActiveGenie::Configuration
|
4
|
+
class OpenaiConfig
|
5
|
+
attr_writer :api_key, :organization, :api_url, :client,
|
6
|
+
:lower_tier_model, :middle_tier_model, :upper_tier_model
|
7
|
+
|
8
|
+
def api_key
|
9
|
+
@api_key || ENV['OPENAI_API_KEY']
|
10
|
+
end
|
11
|
+
|
12
|
+
def organization
|
13
|
+
@organization || ENV['OPENAI_ORGANIZATION']
|
14
|
+
end
|
15
|
+
|
16
|
+
def lower_tier_model
|
17
|
+
@lower_tier_model || 'gpt-4o-mini'
|
18
|
+
end
|
19
|
+
|
20
|
+
def middle_tier_model
|
21
|
+
@middle_tier_model || 'gpt-4o'
|
22
|
+
end
|
23
|
+
|
24
|
+
def upper_tier_model
|
25
|
+
@upper_tier_model || 'o1-preview'
|
26
|
+
end
|
27
|
+
|
28
|
+
def tier_to_model(tier)
|
29
|
+
{
|
30
|
+
lower_tier: lower_tier_model,
|
31
|
+
middle_tier: middle_tier_model,
|
32
|
+
upper_tier: upper_tier_model
|
33
|
+
}[tier&.to_sym]
|
34
|
+
end
|
35
|
+
|
36
|
+
def api_url
|
37
|
+
@api_url || 'https://api.openai.com/v1'
|
38
|
+
end
|
39
|
+
|
40
|
+
def client
|
41
|
+
@client ||= ::ActiveGenie::Clients::OpenaiClient.new(self)
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_h(config = {})
|
45
|
+
{
|
46
|
+
api_key:,
|
47
|
+
organization:,
|
48
|
+
api_url:,
|
49
|
+
lower_tier_model:,
|
50
|
+
middle_tier_model:,
|
51
|
+
upper_tier_model:,
|
52
|
+
**config
|
53
|
+
}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module ActiveGenie::Configuration
|
2
|
+
class ProvidersConfig
|
3
|
+
def initialize
|
4
|
+
@all = {}
|
5
|
+
@default = nil
|
6
|
+
end
|
7
|
+
|
8
|
+
def register(name, provider_class)
|
9
|
+
@all ||= {}
|
10
|
+
@all[name] = provider_class.new
|
11
|
+
define_singleton_method(name) do
|
12
|
+
instance_variable_get("@#{name}") || instance_variable_set("@#{name}", @all[name])
|
13
|
+
end
|
14
|
+
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
def default
|
19
|
+
@default || @all.values.first
|
20
|
+
end
|
21
|
+
|
22
|
+
def all
|
23
|
+
@all
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_h(config = {})
|
27
|
+
hash_all = {}
|
28
|
+
@all.each do |name, provider|
|
29
|
+
hash_all[name] = provider.to_h(config.dig(name) || {})
|
30
|
+
end
|
31
|
+
hash_all
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
attr_writer :default
|
36
|
+
end
|
37
|
+
end
|
@@ -1,33 +1,28 @@
|
|
1
|
-
|
1
|
+
require_relative 'configuration/providers_config'
|
2
|
+
require_relative 'configuration/openai_config'
|
3
|
+
require_relative 'configuration/log_config'
|
2
4
|
|
3
5
|
module ActiveGenie
|
4
|
-
|
5
|
-
|
6
|
+
module Configuration
|
7
|
+
module_function
|
6
8
|
|
7
|
-
def
|
8
|
-
@
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
return @values if @values
|
13
|
-
|
14
|
-
@values = load_values.transform_keys(&:to_sym)
|
15
|
-
@values.each do |key, _value|
|
16
|
-
@values[key][:model] = key
|
17
|
-
@values[key] = @values[key].transform_keys(&:to_sym)
|
9
|
+
def providers
|
10
|
+
@providers ||= begin
|
11
|
+
p = ProvidersConfig.new
|
12
|
+
p.register(:openai, ActiveGenie::Configuration::OpenaiConfig)
|
13
|
+
p
|
18
14
|
end
|
19
15
|
end
|
20
16
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
return {} unless File.exist?(@path_to_config)
|
17
|
+
def log
|
18
|
+
@log ||= LogConfig.new
|
19
|
+
end
|
25
20
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
21
|
+
def to_h(configs = {})
|
22
|
+
{
|
23
|
+
providers: providers.to_h(configs.dig(:providers) || {}),
|
24
|
+
log: log.to_h(configs.dig(:log) || {})
|
25
|
+
}
|
31
26
|
end
|
32
27
|
end
|
33
28
|
end
|
@@ -95,7 +95,7 @@ result = ActiveGenie::DataExtractor.from_informal(text, schema)
|
|
95
95
|
|
96
96
|
## Interface
|
97
97
|
|
98
|
-
### `.call(text, data_to_extract,
|
98
|
+
### `.call(text, data_to_extract, config = {})`
|
99
99
|
Extracts structured data from text based on a predefined schema.
|
100
100
|
|
101
101
|
#### Parameters
|
@@ -103,9 +103,9 @@ Extracts structured data from text based on a predefined schema.
|
|
103
103
|
| --- | --- | --- | --- | --- |
|
104
104
|
| `text` | `String` | The text to analyze and extract data from | Yes | "John Doe is 25 years old" |
|
105
105
|
| `data_to_extract` | `Hash` | Schema defining the data structure to extract | Yes | `{ name: { type: 'string' } }` |
|
106
|
-
| `
|
106
|
+
| `config` | `Hash` | Additional extraction configuration | No | `{ model: "gpt-4" }` |
|
107
107
|
|
108
|
-
####
|
108
|
+
#### config
|
109
109
|
| Name | Type | Description |
|
110
110
|
| --- | --- | --- |
|
111
111
|
| `model` | `String` | The model to use for the extraction |
|
@@ -117,7 +117,7 @@ Extracts structured data from text based on a predefined schema.
|
|
117
117
|
- Explanation field for each extracted value
|
118
118
|
- Additional analysis fields when using `from_informal`
|
119
119
|
|
120
|
-
### `.from_informal(text, data_to_extract,
|
120
|
+
### `.from_informal(text, data_to_extract, config = {})`
|
121
121
|
Extends basic extraction with rhetorical analysis, particularly for litotes.
|
122
122
|
|
123
123
|
#### Additional Return Fields
|
@@ -1,9 +1,10 @@
|
|
1
|
-
|
1
|
+
|
2
|
+
require_relative '../clients/unified_client'
|
2
3
|
|
3
4
|
module ActiveGenie::DataExtractor
|
4
5
|
class Basic
|
5
|
-
def self.call(text, data_to_extract,
|
6
|
-
new(text, data_to_extract,
|
6
|
+
def self.call(text, data_to_extract, config: {})
|
7
|
+
new(text, data_to_extract, config:).call
|
7
8
|
end
|
8
9
|
|
9
10
|
# Extracts structured data from text based on a predefined schema.
|
@@ -11,9 +12,7 @@ module ActiveGenie::DataExtractor
|
|
11
12
|
# @param text [String] The input text to analyze and extract data from
|
12
13
|
# @param data_to_extract [Hash] Schema defining the data structure to extract.
|
13
14
|
# Each key in the hash represents a field to extract, and its value defines the expected type and constraints.
|
14
|
-
# @param
|
15
|
-
# @option options [String] :model The model to use for the extraction
|
16
|
-
# @option options [String] :api_key The API key to use for the extraction
|
15
|
+
# @param config [Hash] Additional config for the extraction process
|
17
16
|
#
|
18
17
|
# @return [Hash] The extracted data matching the schema structure. Each field will include
|
19
18
|
# both the extracted value and an explanation of how it was derived.
|
@@ -27,10 +26,10 @@ module ActiveGenie::DataExtractor
|
|
27
26
|
# DataExtractor.call(text, schema)
|
28
27
|
# # => { name: "John Doe", name_explanation: "Found directly in text",
|
29
28
|
# # age: 25, age_explanation: "Explicitly stated as 25 years old" }
|
30
|
-
def initialize(text, data_to_extract,
|
29
|
+
def initialize(text, data_to_extract, config: {})
|
31
30
|
@text = text
|
32
31
|
@data_to_extract = data_to_extract
|
33
|
-
@
|
32
|
+
@config = ActiveGenie::Configuration.to_h(config)
|
34
33
|
end
|
35
34
|
|
36
35
|
def call
|
@@ -47,7 +46,7 @@ module ActiveGenie::DataExtractor
|
|
47
46
|
}
|
48
47
|
}
|
49
48
|
|
50
|
-
::ActiveGenie::Clients::
|
49
|
+
::ActiveGenie::Clients::UnifiedClient.function_calling(messages, function, config:)
|
51
50
|
end
|
52
51
|
|
53
52
|
private
|
@@ -84,5 +83,16 @@ module ActiveGenie::DataExtractor
|
|
84
83
|
|
85
84
|
with_explaination
|
86
85
|
end
|
86
|
+
|
87
|
+
def config
|
88
|
+
{
|
89
|
+
all_providers: { model_tier: 'lower_tier' },
|
90
|
+
log: {
|
91
|
+
**(@config.dig(:log) || {}),
|
92
|
+
trace: self.class.name,
|
93
|
+
},
|
94
|
+
**@config
|
95
|
+
}
|
96
|
+
end
|
87
97
|
end
|
88
98
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module ActiveGenie::DataExtractor
|
2
2
|
class FromInformal
|
3
|
-
def self.call(text, data_to_extract,
|
4
|
-
new(text, data_to_extract,
|
3
|
+
def self.call(text, data_to_extract, config: {})
|
4
|
+
new(text, data_to_extract, config:).call()
|
5
5
|
end
|
6
6
|
|
7
7
|
# Extracts data from informal text while also detecting litotes and their meanings.
|
@@ -9,7 +9,7 @@ module ActiveGenie::DataExtractor
|
|
9
9
|
#
|
10
10
|
# @param text [String] The informal text to analyze
|
11
11
|
# @param data_to_extract [Hash] Schema defining the data structure to extract
|
12
|
-
# @param
|
12
|
+
# @param config [Hash] Additional config for the extraction process
|
13
13
|
#
|
14
14
|
# @return [Hash] The extracted data including litote analysis. In addition to the
|
15
15
|
# schema-defined fields, includes:
|
@@ -23,17 +23,17 @@ module ActiveGenie::DataExtractor
|
|
23
23
|
# # => { mood: "positive", mood_explanation: "Speaker views weather favorably",
|
24
24
|
# # message_litote: true,
|
25
25
|
# # litote_rephrased: "The weather is good today" }
|
26
|
-
def initialize(text, data_to_extract,
|
26
|
+
def initialize(text, data_to_extract, config: {})
|
27
27
|
@text = text
|
28
28
|
@data_to_extract = data_to_extract
|
29
|
-
@
|
29
|
+
@config = ActiveGenie::Configuration.to_h(config)
|
30
30
|
end
|
31
31
|
|
32
32
|
def call
|
33
|
-
response = Basic.call(@text, data_to_extract_with_litote,
|
33
|
+
response = Basic.call(@text, data_to_extract_with_litote, config:)
|
34
34
|
|
35
35
|
if response['message_litote']
|
36
|
-
response = Basic.call(response['litote_rephrased'], @data_to_extract,
|
36
|
+
response = Basic.call(response['litote_rephrased'], @data_to_extract, config:)
|
37
37
|
end
|
38
38
|
|
39
39
|
response
|
@@ -54,5 +54,16 @@ module ActiveGenie::DataExtractor
|
|
54
54
|
}
|
55
55
|
}
|
56
56
|
end
|
57
|
+
|
58
|
+
def config
|
59
|
+
{
|
60
|
+
all_providers: { model_tier: 'lower_tier' },
|
61
|
+
**@config,
|
62
|
+
log: {
|
63
|
+
**@config.dig(:log),
|
64
|
+
trace: self.class.name,
|
65
|
+
},
|
66
|
+
}
|
67
|
+
end
|
57
68
|
end
|
58
69
|
end
|
@@ -2,7 +2,7 @@ require_relative 'data_extractor/basic'
|
|
2
2
|
require_relative 'data_extractor/from_informal'
|
3
3
|
|
4
4
|
module ActiveGenie
|
5
|
-
#
|
5
|
+
# See the [DataExtractor README](lib/active_genie/data_extractor/README.md) for more information.
|
6
6
|
module DataExtractor
|
7
7
|
module_function
|
8
8
|
|