active_genie 0.0.8 → 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|