boxcars 0.7.2 → 0.7.4
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/Gemfile.lock +64 -56
- data/boxcars.gemspec +1 -1
- data/lib/boxcars/boxcar/json_engine_boxcar.rb +1 -1
- data/lib/boxcars/boxcar.rb +1 -1
- data/lib/boxcars/engine/cerebras.rb +12 -85
- data/lib/boxcars/engine/google.rb +36 -0
- data/lib/boxcars/engine/intelligence_base.rb +103 -0
- data/lib/boxcars/engine.rb +10 -2
- data/lib/boxcars/version.rb +1 -1
- metadata +18 -18
- data/lib/boxcars/engine/intelligence.rb +0 -141
- data/perplexity_example.rb +0 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b7d7e68a8fe277fba9e416c7f582ee60135ef79f101da5f8da802a900330e807
|
4
|
+
data.tar.gz: 7f7e7f5243dc3a3f646dee5475456b9df640773e6411053b8f8514a5b9ef95be
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 91af273bb15b972e59d0c1c2f142e19fbfc226298ce650da55e7925054e3c9ea2076f2c7ca9e741b2280cea33a913be27bf7f65681d61f6fbfa02d8361726e00
|
7
|
+
data.tar.gz: e4f6d515b34bc9ba1b12b70f7cd0813930b825675b93d263e991858d8c774ffc2030ce27264aac3c458f921fede365651a8344c1a4f279852b0c73c30052e39b
|
data/Gemfile.lock
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
boxcars (0.7.
|
5
|
-
anthropic (~> 0.3)
|
4
|
+
boxcars (0.7.4)
|
6
5
|
google_search_results (~> 2.2)
|
7
6
|
gpt4all (~> 0.0.5)
|
8
7
|
hnswlib (~> 0.9)
|
9
8
|
intelligence (>= 0.8)
|
10
9
|
nokogiri (~> 1.18)
|
11
10
|
pgvector (~> 0.2)
|
11
|
+
ruby-anthropic (~> 0.4)
|
12
12
|
ruby-openai (>= 7.3)
|
13
13
|
|
14
14
|
GEM
|
@@ -34,11 +34,7 @@ GEM
|
|
34
34
|
tzinfo (~> 2.0, >= 2.0.5)
|
35
35
|
addressable (2.8.7)
|
36
36
|
public_suffix (>= 2.0.2, < 7.0)
|
37
|
-
|
38
|
-
event_stream_parser (>= 0.3.0, < 2.0.0)
|
39
|
-
faraday (>= 1)
|
40
|
-
faraday-multipart (>= 1)
|
41
|
-
ast (2.4.2)
|
37
|
+
ast (2.4.3)
|
42
38
|
async (1.32.1)
|
43
39
|
console (~> 1.10)
|
44
40
|
nio4r (~> 2.3)
|
@@ -51,20 +47,19 @@ GEM
|
|
51
47
|
protocol-http1 (~> 0.19.0)
|
52
48
|
protocol-http2 (~> 0.16.0)
|
53
49
|
traces (>= 0.10.0)
|
54
|
-
async-http-faraday (0.
|
50
|
+
async-http-faraday (0.21.0)
|
55
51
|
async-http (~> 0.42)
|
56
52
|
faraday
|
57
53
|
async-io (1.43.2)
|
58
54
|
async
|
59
|
-
async-pool (0.10.
|
55
|
+
async-pool (0.10.3)
|
60
56
|
async (>= 1.25)
|
61
|
-
traces
|
62
57
|
base64 (0.2.0)
|
63
58
|
benchmark (0.4.0)
|
64
59
|
bigdecimal (3.1.9)
|
65
60
|
concurrent-ruby (1.3.5)
|
66
61
|
connection_pool (2.5.0)
|
67
|
-
console (1.
|
62
|
+
console (1.30.2)
|
68
63
|
fiber-annotation
|
69
64
|
fiber-local (~> 1.1)
|
70
65
|
json
|
@@ -75,13 +70,13 @@ GEM
|
|
75
70
|
debug (1.10.0)
|
76
71
|
irb (~> 1.10)
|
77
72
|
reline (>= 0.3.8)
|
78
|
-
diff-lcs (1.
|
73
|
+
diff-lcs (1.6.1)
|
79
74
|
domain_name (0.6.20240107)
|
80
|
-
dotenv (3.1.
|
75
|
+
dotenv (3.1.8)
|
81
76
|
drb (2.2.1)
|
82
77
|
dynamicschema (1.0.0.beta04)
|
83
78
|
event_stream_parser (1.0.0)
|
84
|
-
faraday (2.
|
79
|
+
faraday (2.13.0)
|
85
80
|
faraday-net_http (>= 2.0, < 3.5)
|
86
81
|
json
|
87
82
|
logger
|
@@ -91,7 +86,7 @@ GEM
|
|
91
86
|
multipart-post (~> 2.0)
|
92
87
|
faraday-net_http (3.4.0)
|
93
88
|
net-http (>= 0.5.0)
|
94
|
-
faraday-retry (2.
|
89
|
+
faraday-retry (2.3.1)
|
95
90
|
faraday (~> 2.0)
|
96
91
|
fiber-annotation (0.2.0)
|
97
92
|
fiber-local (1.1.0)
|
@@ -116,7 +111,7 @@ GEM
|
|
116
111
|
http-accept (1.7.0)
|
117
112
|
http-cookie (1.0.8)
|
118
113
|
domain_name (~> 0.5)
|
119
|
-
i18n (1.14.
|
114
|
+
i18n (1.14.7)
|
120
115
|
concurrent-ruby (~> 1.0)
|
121
116
|
intelligence (0.8.0)
|
122
117
|
dynamicschema (~> 1.0.0.beta03)
|
@@ -124,50 +119,56 @@ GEM
|
|
124
119
|
json-repair (~> 0.2)
|
125
120
|
mime-types (~> 3.6)
|
126
121
|
io-console (0.8.0)
|
127
|
-
irb (1.
|
122
|
+
irb (1.15.2)
|
123
|
+
pp (>= 0.6.0)
|
128
124
|
rdoc (>= 4.0.0)
|
129
125
|
reline (>= 0.4.2)
|
130
|
-
json (2.
|
126
|
+
json (2.10.2)
|
131
127
|
json-repair (0.2.0)
|
132
|
-
language_server-protocol (3.17.0.
|
133
|
-
|
134
|
-
|
128
|
+
language_server-protocol (3.17.0.4)
|
129
|
+
lint_roller (1.1.0)
|
130
|
+
logger (1.7.0)
|
131
|
+
mime-types (3.6.2)
|
135
132
|
logger
|
136
133
|
mime-types-data (~> 3.2015)
|
137
|
-
mime-types-data (3.2025.
|
138
|
-
minitest (5.25.
|
134
|
+
mime-types-data (3.2025.0408)
|
135
|
+
minitest (5.25.5)
|
139
136
|
multi_json (1.15.0)
|
140
137
|
multipart-post (2.4.1)
|
141
138
|
net-http (0.6.0)
|
142
139
|
uri
|
143
140
|
netrc (0.11.0)
|
144
141
|
nio4r (2.7.4)
|
145
|
-
nokogiri (1.18.
|
142
|
+
nokogiri (1.18.7-aarch64-linux-gnu)
|
146
143
|
racc (~> 1.4)
|
147
|
-
nokogiri (1.18.
|
144
|
+
nokogiri (1.18.7-aarch64-linux-musl)
|
148
145
|
racc (~> 1.4)
|
149
|
-
nokogiri (1.18.
|
146
|
+
nokogiri (1.18.7-arm-linux-gnu)
|
150
147
|
racc (~> 1.4)
|
151
|
-
nokogiri (1.18.
|
148
|
+
nokogiri (1.18.7-arm-linux-musl)
|
152
149
|
racc (~> 1.4)
|
153
|
-
nokogiri (1.18.
|
150
|
+
nokogiri (1.18.7-arm64-darwin)
|
154
151
|
racc (~> 1.4)
|
155
|
-
nokogiri (1.18.
|
152
|
+
nokogiri (1.18.7-x86_64-darwin)
|
156
153
|
racc (~> 1.4)
|
157
|
-
nokogiri (1.18.
|
154
|
+
nokogiri (1.18.7-x86_64-linux-gnu)
|
158
155
|
racc (~> 1.4)
|
159
|
-
nokogiri (1.18.
|
156
|
+
nokogiri (1.18.7-x86_64-linux-musl)
|
160
157
|
racc (~> 1.4)
|
161
158
|
octokit (4.25.1)
|
162
159
|
faraday (>= 1, < 3)
|
163
160
|
sawyer (~> 0.9)
|
164
161
|
os (1.1.4)
|
165
162
|
parallel (1.26.3)
|
166
|
-
parser (3.3.
|
163
|
+
parser (3.3.8.0)
|
167
164
|
ast (~> 2.4.1)
|
168
165
|
racc
|
169
166
|
pg (1.5.9)
|
170
167
|
pgvector (0.2.2)
|
168
|
+
pp (0.6.2)
|
169
|
+
prettyprint
|
170
|
+
prettyprint (0.2.0)
|
171
|
+
prism (1.4.0)
|
171
172
|
protocol-hpack (1.5.1)
|
172
173
|
protocol-http (0.26.8)
|
173
174
|
protocol-http1 (0.19.1)
|
@@ -182,22 +183,22 @@ GEM
|
|
182
183
|
racc (1.8.1)
|
183
184
|
rainbow (3.1.1)
|
184
185
|
rake (13.2.1)
|
185
|
-
rdoc (6.
|
186
|
+
rdoc (6.13.1)
|
186
187
|
psych (>= 4.0.0)
|
187
188
|
regexp_parser (2.10.0)
|
188
|
-
reline (0.6.
|
189
|
+
reline (0.6.1)
|
189
190
|
io-console (~> 0.5)
|
190
191
|
rest-client (2.1.0)
|
191
192
|
http-accept (>= 1.7.0, < 2.0)
|
192
193
|
http-cookie (>= 1.0.2, < 2.0)
|
193
194
|
mime-types (>= 1.16, < 4.0)
|
194
195
|
netrc (~> 0.8)
|
195
|
-
rexml (3.4.
|
196
|
+
rexml (3.4.1)
|
196
197
|
rspec (3.13.0)
|
197
198
|
rspec-core (~> 3.13.0)
|
198
199
|
rspec-expectations (~> 3.13.0)
|
199
200
|
rspec-mocks (~> 3.13.0)
|
200
|
-
rspec-core (3.13.
|
201
|
+
rspec-core (3.13.3)
|
201
202
|
rspec-support (~> 3.13.0)
|
202
203
|
rspec-expectations (3.13.3)
|
203
204
|
diff-lcs (>= 1.2.0, < 2.0)
|
@@ -206,23 +207,30 @@ GEM
|
|
206
207
|
diff-lcs (>= 1.2.0, < 2.0)
|
207
208
|
rspec-support (~> 3.13.0)
|
208
209
|
rspec-support (3.13.2)
|
209
|
-
rubocop (1.
|
210
|
+
rubocop (1.75.2)
|
210
211
|
json (~> 2.3)
|
211
|
-
language_server-protocol (
|
212
|
+
language_server-protocol (~> 3.17.0.2)
|
213
|
+
lint_roller (~> 1.1.0)
|
212
214
|
parallel (~> 1.10)
|
213
215
|
parser (>= 3.3.0.2)
|
214
216
|
rainbow (>= 2.2.2, < 4.0)
|
215
217
|
regexp_parser (>= 2.9.3, < 3.0)
|
216
|
-
rubocop-ast (>= 1.
|
218
|
+
rubocop-ast (>= 1.44.0, < 2.0)
|
217
219
|
ruby-progressbar (~> 1.7)
|
218
220
|
unicode-display_width (>= 2.4.0, < 4.0)
|
219
|
-
rubocop-ast (1.
|
220
|
-
parser (>= 3.3.
|
221
|
+
rubocop-ast (1.44.1)
|
222
|
+
parser (>= 3.3.7.2)
|
223
|
+
prism (~> 1.4)
|
221
224
|
rubocop-rake (0.6.0)
|
222
225
|
rubocop (~> 1.0)
|
223
|
-
rubocop-rspec (3.
|
224
|
-
|
225
|
-
|
226
|
+
rubocop-rspec (3.5.0)
|
227
|
+
lint_roller (~> 1.1)
|
228
|
+
rubocop (~> 1.72, >= 1.72.1)
|
229
|
+
ruby-anthropic (0.4.2)
|
230
|
+
event_stream_parser (>= 0.3.0, < 2.0.0)
|
231
|
+
faraday (>= 1)
|
232
|
+
faraday-multipart (>= 1)
|
233
|
+
ruby-openai (8.1.0)
|
226
234
|
event_stream_parser (>= 0.3.0, < 2.0.0)
|
227
235
|
faraday (>= 1)
|
228
236
|
faraday-multipart (>= 1)
|
@@ -231,19 +239,19 @@ GEM
|
|
231
239
|
addressable (>= 2.3.5)
|
232
240
|
faraday (>= 0.17.3, < 3)
|
233
241
|
securerandom (0.4.1)
|
234
|
-
sqlite3 (2.
|
235
|
-
sqlite3 (2.
|
236
|
-
sqlite3 (2.
|
237
|
-
sqlite3 (2.
|
238
|
-
sqlite3 (2.
|
239
|
-
sqlite3 (2.
|
240
|
-
sqlite3 (2.
|
241
|
-
sqlite3 (2.
|
242
|
-
stringio (3.1.
|
242
|
+
sqlite3 (2.6.0-aarch64-linux-gnu)
|
243
|
+
sqlite3 (2.6.0-aarch64-linux-musl)
|
244
|
+
sqlite3 (2.6.0-arm-linux-gnu)
|
245
|
+
sqlite3 (2.6.0-arm-linux-musl)
|
246
|
+
sqlite3 (2.6.0-arm64-darwin)
|
247
|
+
sqlite3 (2.6.0-x86_64-darwin)
|
248
|
+
sqlite3 (2.6.0-x86_64-linux-gnu)
|
249
|
+
sqlite3 (2.6.0-x86_64-linux-musl)
|
250
|
+
stringio (3.1.6)
|
243
251
|
strings-ansi (0.2.0)
|
244
252
|
timeout (0.4.3)
|
245
|
-
timers (4.
|
246
|
-
traces (0.
|
253
|
+
timers (4.4.0)
|
254
|
+
traces (0.15.2)
|
247
255
|
tty-cursor (0.7.1)
|
248
256
|
tty-progressbar (0.18.3)
|
249
257
|
strings-ansi (~> 0.2)
|
@@ -254,7 +262,7 @@ GEM
|
|
254
262
|
tzinfo (2.0.6)
|
255
263
|
concurrent-ruby (~> 1.0)
|
256
264
|
unicode-display_width (2.6.0)
|
257
|
-
uri (1.0.
|
265
|
+
uri (1.0.3)
|
258
266
|
vcr (6.3.1)
|
259
267
|
base64
|
260
268
|
webmock (3.24.0)
|
data/boxcars.gemspec
CHANGED
@@ -31,13 +31,13 @@ Gem::Specification.new do |spec|
|
|
31
31
|
spec.require_paths = ["lib"]
|
32
32
|
|
33
33
|
# runtime dependencies
|
34
|
-
spec.add_dependency "anthropic", "~> 0.3"
|
35
34
|
spec.add_dependency "google_search_results", "~> 2.2"
|
36
35
|
spec.add_dependency "gpt4all", "~> 0.0.5"
|
37
36
|
spec.add_dependency "hnswlib", "~> 0.9"
|
38
37
|
spec.add_dependency "intelligence", ">= 0.8"
|
39
38
|
spec.add_dependency "nokogiri", "~> 1.18"
|
40
39
|
spec.add_dependency "pgvector", "~> 0.2"
|
40
|
+
spec.add_dependency "ruby-anthropic", "~> 0.4"
|
41
41
|
spec.add_dependency "ruby-openai", ">= 7.3"
|
42
42
|
|
43
43
|
# For more information and examples about making a new gem, checkout our
|
@@ -35,7 +35,7 @@ module Boxcars
|
|
35
35
|
%<wanted_data>s
|
36
36
|
}
|
37
37
|
SYSPR
|
38
|
-
stock_prompt += "\n\nImportant:\n#{important}\n"
|
38
|
+
stock_prompt += "\n\nImportant:\n#{important}\n" unless important.to_s.empty?
|
39
39
|
|
40
40
|
sprompt = format(stock_prompt, wanted_data: wanted_data, data_description: data_description)
|
41
41
|
ctemplate = [
|
data/lib/boxcars/boxcar.rb
CHANGED
@@ -166,7 +166,7 @@ module Boxcars
|
|
166
166
|
output = call(inputs: inputs)
|
167
167
|
rescue StandardError => e
|
168
168
|
Boxcars.error "Error in #{name} boxcar#call: #{e}\nbt:#{e.backtrace[0..5].join("\n ")}", :red
|
169
|
-
Boxcars.error("Response Body: #{e.response[:body]}", :red) if e.respond_to?(:response) && e.response.
|
169
|
+
Boxcars.error("Response Body: #{e.response[:body]}", :red) if e.respond_to?(:response) && !e.response.nil?
|
170
170
|
raise e
|
171
171
|
end
|
172
172
|
validate_outputs(outputs: output.keys)
|
@@ -1,11 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "intelligence"
|
4
3
|
module Boxcars
|
5
4
|
# A engine that uses Cerebras's API
|
6
|
-
class Cerebras <
|
7
|
-
attr_reader :prompts, :cerebras_params, :model_kwargs, :batch_size
|
8
|
-
|
5
|
+
class Cerebras < IntelligenceBase
|
9
6
|
# The default parameters to use when asking the engine
|
10
7
|
DEFAULT_PARAMS = {
|
11
8
|
model: "llama-3.3-70b",
|
@@ -18,92 +15,22 @@ module Boxcars
|
|
18
15
|
DEFAULT_DESCRIPTION = "useful for when you need to use Cerebras to process complex content. " \
|
19
16
|
"Supports text, images, and other content types"
|
20
17
|
|
18
|
+
# A Cerebras Engine is used by Boxcars to generate output from prompts
|
19
|
+
# @param name [String] The name of the Engine. Defaults to classname.
|
20
|
+
# @param description [String] A description of the Engine.
|
21
|
+
# @param prompts [Array<Prompt>] The prompts to use for the Engine.
|
22
|
+
# @param batch_size [Integer] The number of prompts to send to the Engine at a time.
|
23
|
+
# @param kwargs [Hash] Additional parameters to pass to the Engine.
|
21
24
|
def initialize(name: DEFAULT_NAME, description: DEFAULT_DESCRIPTION, prompts: [], batch_size: 20, **kwargs)
|
22
|
-
|
23
|
-
@prompts = prompts
|
24
|
-
@batch_size = batch_size
|
25
|
-
super(description: description, name: name)
|
26
|
-
end
|
27
|
-
|
28
|
-
# Get the Cerebras API client
|
29
|
-
def self.adapter(params:, api_key: nil)
|
30
|
-
api_key = Boxcars.configuration.cerebras_api_key(**params) if api_key.nil?
|
31
|
-
raise ArgumentError, "Cerebras API key not configured" unless api_key
|
32
|
-
|
33
|
-
Intelligence::Adapter[:cerebras].new(
|
34
|
-
{ key: api_key, chat_options: params }
|
35
|
-
)
|
36
|
-
end
|
37
|
-
|
38
|
-
# Process different content types
|
39
|
-
def process_content(content)
|
40
|
-
case content
|
41
|
-
when String
|
42
|
-
{ type: "text", text: content }
|
43
|
-
when Hash
|
44
|
-
validate_content(content)
|
45
|
-
when Array
|
46
|
-
content.map { |c| process_content(c) }
|
47
|
-
else
|
48
|
-
raise ArgumentError, "Unsupported content type: #{content.class}"
|
49
|
-
end
|
25
|
+
super(provider: :cerebras, description: description, name: name, prompts: prompts, batch_size: batch_size, **kwargs)
|
50
26
|
end
|
51
27
|
|
52
|
-
|
53
|
-
|
54
|
-
raise ArgumentError, "Content must have type and text fields" unless content[:type] && content[:text]
|
55
|
-
|
56
|
-
content
|
57
|
-
end
|
58
|
-
|
59
|
-
# Get an answer from the engine
|
60
|
-
def client(prompt:, inputs: {}, api_key: nil, **kwargs)
|
61
|
-
params = cerebras_params.merge(kwargs)
|
62
|
-
adapter = Cerebras.adapter(api_key: api_key, params: params)
|
63
|
-
raise Error, "Cerebras: No response from API" unless adapter
|
64
|
-
|
65
|
-
convo = prompt.as_intelligence_conversation(inputs: inputs)
|
66
|
-
raise Error, "Cerebras: No conversation" unless convo
|
67
|
-
|
68
|
-
# Make API call
|
69
|
-
request = Intelligence::ChatRequest.new(adapter: adapter)
|
70
|
-
response = request.chat(convo)
|
71
|
-
return JSON.parse(response.body) if response.success?
|
72
|
-
|
73
|
-
raise Error, "Cerebras: #{response.reason_phrase}"
|
74
|
-
rescue StandardError => e
|
75
|
-
Boxcars.error("Cerebras Error: #{e.message}", :red)
|
76
|
-
raise
|
77
|
-
end
|
78
|
-
|
79
|
-
# Run the engine with a question
|
80
|
-
def run(question, **kwargs)
|
81
|
-
prompt = Prompt.new(template: question)
|
82
|
-
response = client(prompt: prompt, **kwargs)
|
83
|
-
extract_answer(response)
|
84
|
-
end
|
85
|
-
|
86
|
-
private
|
87
|
-
|
88
|
-
def extract_answer(response)
|
89
|
-
# Handle different response formats
|
90
|
-
if response["choices"]
|
91
|
-
response["choices"].map { |c| c.dig("message", "content") || c["text"] }.join("\n").strip
|
92
|
-
else
|
93
|
-
response["output"] || response.to_s
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def check_response(response)
|
98
|
-
return if response.present? && response.key?("choices")
|
99
|
-
|
100
|
-
raise KeyError, "CEREBRAS_API_KEY not valid" if response&.reason_phrase == "Unauthorized"
|
101
|
-
|
102
|
-
raise ValueError, "Cerebras error: #{response&.reason_phrase&.present? ? response.reason_phrase : response}"
|
28
|
+
def default_model_params
|
29
|
+
DEFAULT_PARAMS
|
103
30
|
end
|
104
31
|
|
105
|
-
def
|
106
|
-
|
32
|
+
def lookup_provider_api_key(params:)
|
33
|
+
Boxcars.configuration.cerebras_api_key(**params)
|
107
34
|
end
|
108
35
|
end
|
109
36
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Boxcars
|
4
|
+
# A engine that uses Google's API
|
5
|
+
class Google < IntelligenceBase
|
6
|
+
# The default parameters to use when asking the engine
|
7
|
+
DEFAULT_PARAMS = {
|
8
|
+
model: "gemini-1.5-flash-latest",
|
9
|
+
temperature: 0.1
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
# the default name of the engine
|
13
|
+
DEFAULT_NAME = "Google Vertex AI engine"
|
14
|
+
# the default description of the engine
|
15
|
+
DEFAULT_DESCRIPTION = "useful for when you need to use Google Vertex AI to process complex content. " \
|
16
|
+
"Supports text, images, and other content types"
|
17
|
+
|
18
|
+
# A Google Engine is used by Boxcars to generate output from prompts
|
19
|
+
# @param name [String] The name of the Engine. Defaults to classname.
|
20
|
+
# @param description [String] A description of the Engine.
|
21
|
+
# @param prompts [Array<Prompt>] The prompts to use for the Engine.
|
22
|
+
# @param batch_size [Integer] The number of prompts to send to the Engine at a time.
|
23
|
+
# @param kwargs [Hash] Additional parameters to pass to the Engine.
|
24
|
+
def initialize(name: DEFAULT_NAME, description: DEFAULT_DESCRIPTION, prompts: [], batch_size: 20, **kwargs)
|
25
|
+
super(provider: :google, description: description, name: name, prompts: prompts, batch_size: batch_size, **kwargs)
|
26
|
+
end
|
27
|
+
|
28
|
+
def default_model_params
|
29
|
+
DEFAULT_PARAMS
|
30
|
+
end
|
31
|
+
|
32
|
+
def lookup_provider_api_key(params:)
|
33
|
+
Boxcars.configuration.gemini_api_key(**params)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'intelligence'
|
4
|
+
|
5
|
+
module Boxcars
|
6
|
+
# A Base class for all Intelligence Engines
|
7
|
+
class IntelligenceBase < Engine
|
8
|
+
attr_reader :provider, :all_params
|
9
|
+
|
10
|
+
# The base Intelligence Engine is used by other engines to generate output from prompts
|
11
|
+
# @param provider [String] The provider of the Engine implemented by the Intelligence gem.
|
12
|
+
# @param name [String] The name of the Engine. Defaults to classname.
|
13
|
+
# @param description [String] A description of the Engine.
|
14
|
+
# @param prompts [Array<Prompt>] The prompts to use for the Engine.
|
15
|
+
# @param batch_size [Integer] The number of prompts to send to the Engine at a time.
|
16
|
+
# @param kwargs [Hash] Additional parameters to pass to the Engine.
|
17
|
+
def initialize(provider:, description:, name:, prompts: [], batch_size: 20, **kwargs)
|
18
|
+
@provider = provider
|
19
|
+
@all_params = default_model_params.merge(kwargs)
|
20
|
+
super(description: description, name: name, prompts: prompts, batch_size: batch_size)
|
21
|
+
end
|
22
|
+
|
23
|
+
# can be overridden by provider subclass
|
24
|
+
def default_model_params
|
25
|
+
{}
|
26
|
+
end
|
27
|
+
|
28
|
+
def lookup_provider_api_key(params:)
|
29
|
+
raise NotImplementedError, "lookup_provider_api_key method must be implemented by subclass"
|
30
|
+
end
|
31
|
+
|
32
|
+
def adapter(params:, api_key:)
|
33
|
+
Intelligence::Adapter[provider].new(
|
34
|
+
{ key: api_key, chat_options: params }
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Process different content types
|
39
|
+
def process_content(content)
|
40
|
+
case content
|
41
|
+
when String
|
42
|
+
{ type: "text", text: content }
|
43
|
+
when Hash
|
44
|
+
validate_content(content)
|
45
|
+
when Array
|
46
|
+
content.map { |c| process_content(c) }
|
47
|
+
else
|
48
|
+
raise ArgumentError, "Unsupported content type: #{content.class}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Validate content structure
|
53
|
+
def validate_content(content)
|
54
|
+
raise ArgumentError, "Content must have type and text fields" unless content[:type] && content[:text]
|
55
|
+
|
56
|
+
content
|
57
|
+
end
|
58
|
+
|
59
|
+
# Get an answer from the engine
|
60
|
+
def client(prompt:, inputs: {}, api_key: nil, **kwargs)
|
61
|
+
params = all_params.merge(kwargs)
|
62
|
+
api_key ||= lookup_provider_api_key(params: params)
|
63
|
+
raise Error, "No API key found for #{provider}" unless api_key
|
64
|
+
|
65
|
+
adapter = adapter(api_key: api_key, params: params)
|
66
|
+
convo = prompt.as_intelligence_conversation(inputs: inputs)
|
67
|
+
request = Intelligence::ChatRequest.new(adapter: adapter)
|
68
|
+
response = request.chat(convo)
|
69
|
+
return JSON.parse(response.body) if response.success?
|
70
|
+
|
71
|
+
raise Error, (response&.reason_phrase || "No response from API #{provider}")
|
72
|
+
rescue StandardError => e
|
73
|
+
Boxcars.error("#{provider} Error: #{e.message}", :red)
|
74
|
+
raise
|
75
|
+
end
|
76
|
+
|
77
|
+
# Run the engine with a question
|
78
|
+
def run(question, **kwargs)
|
79
|
+
prompt = Prompt.new(template: question)
|
80
|
+
response = client(prompt: prompt, **kwargs)
|
81
|
+
extract_answer(response)
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def extract_answer(response)
|
87
|
+
# Handle different response formats
|
88
|
+
if response["choices"]
|
89
|
+
response["choices"].map { |c| c.dig("message", "content") || c["text"] }.join("\n").strip
|
90
|
+
elsif response["candidates"]
|
91
|
+
response["candidates"].map { |c| c.dig("content", "parts", 0, "text") }.join("\n").strip
|
92
|
+
else
|
93
|
+
response["output"] || response.to_s
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def check_response(response)
|
98
|
+
return if response.is_a?(Hash) && response.key?("choices")
|
99
|
+
|
100
|
+
raise Error, "Invalid response from #{provider}: #{response}"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
data/lib/boxcars/engine.rb
CHANGED
@@ -3,12 +3,18 @@
|
|
3
3
|
module Boxcars
|
4
4
|
# @abstract
|
5
5
|
class Engine
|
6
|
+
attr_reader :prompts, :batch_size
|
7
|
+
|
6
8
|
# An Engine is used by Boxcars to generate output from prompts
|
7
9
|
# @param name [String] The name of the Engine. Defaults to classname.
|
8
10
|
# @param description [String] A description of the Engine.
|
9
|
-
|
11
|
+
# @param prompts [Array<Prompt>] The prompts to use for the Engine.
|
12
|
+
# @param batch_size [Integer] The number of prompts to send to the Engine at a time.
|
13
|
+
def initialize(description: 'Engine', name: nil, prompts: [], batch_size: 20)
|
10
14
|
@name = name || self.class.name
|
11
15
|
@description = description
|
16
|
+
@prompts = prompts
|
17
|
+
@batch_size = batch_size
|
12
18
|
end
|
13
19
|
|
14
20
|
# Get an answer from the Engine.
|
@@ -37,7 +43,7 @@ module Boxcars
|
|
37
43
|
end
|
38
44
|
end
|
39
45
|
|
40
|
-
# Call out to
|
46
|
+
# Call out to LLM's endpoint with k unique prompts.
|
41
47
|
# @param prompts [Array<String>] The prompts to pass into the model.
|
42
48
|
# @param inputs [Array<String>] The inputs to subsitite into the prompt.
|
43
49
|
# @param stop [Array<String>] Optional list of stop words to use when generating.
|
@@ -80,4 +86,6 @@ require "boxcars/engine/openai"
|
|
80
86
|
require "boxcars/engine/perplexityai"
|
81
87
|
require "boxcars/engine/gpt4all_eng"
|
82
88
|
require "boxcars/engine/gemini_ai"
|
89
|
+
require "boxcars/engine/intelligence_base"
|
83
90
|
require "boxcars/engine/cerebras"
|
91
|
+
require "boxcars/engine/google"
|
data/lib/boxcars/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: boxcars
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Francis Sullivan
|
@@ -9,22 +9,8 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2025-
|
12
|
+
date: 2025-04-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
-
- !ruby/object:Gem::Dependency
|
15
|
-
name: anthropic
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
-
requirements:
|
18
|
-
- - "~>"
|
19
|
-
- !ruby/object:Gem::Version
|
20
|
-
version: '0.3'
|
21
|
-
type: :runtime
|
22
|
-
prerelease: false
|
23
|
-
version_requirements: !ruby/object:Gem::Requirement
|
24
|
-
requirements:
|
25
|
-
- - "~>"
|
26
|
-
- !ruby/object:Gem::Version
|
27
|
-
version: '0.3'
|
28
14
|
- !ruby/object:Gem::Dependency
|
29
15
|
name: google_search_results
|
30
16
|
requirement: !ruby/object:Gem::Requirement
|
@@ -109,6 +95,20 @@ dependencies:
|
|
109
95
|
- - "~>"
|
110
96
|
- !ruby/object:Gem::Version
|
111
97
|
version: '0.2'
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: ruby-anthropic
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - "~>"
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0.4'
|
105
|
+
type: :runtime
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - "~>"
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0.4'
|
112
112
|
- !ruby/object:Gem::Dependency
|
113
113
|
name: ruby-openai
|
114
114
|
requirement: !ruby/object:Gem::Requirement
|
@@ -169,9 +169,10 @@ files:
|
|
169
169
|
- lib/boxcars/engine/cohere.rb
|
170
170
|
- lib/boxcars/engine/engine_result.rb
|
171
171
|
- lib/boxcars/engine/gemini_ai.rb
|
172
|
+
- lib/boxcars/engine/google.rb
|
172
173
|
- lib/boxcars/engine/gpt4all_eng.rb
|
173
174
|
- lib/boxcars/engine/groq.rb
|
174
|
-
- lib/boxcars/engine/
|
175
|
+
- lib/boxcars/engine/intelligence_base.rb
|
175
176
|
- lib/boxcars/engine/ollama.rb
|
176
177
|
- lib/boxcars/engine/openai.rb
|
177
178
|
- lib/boxcars/engine/perplexityai.rb
|
@@ -205,7 +206,6 @@ files:
|
|
205
206
|
- lib/boxcars/vector_store/split_text.rb
|
206
207
|
- lib/boxcars/version.rb
|
207
208
|
- lib/boxcars/x_node.rb
|
208
|
-
- perplexity_example.rb
|
209
209
|
- run.json
|
210
210
|
homepage: https://github.com/BoxcarsAI/boxcars
|
211
211
|
licenses:
|
@@ -1,141 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Boxcars
|
4
|
-
# A engine that uses Intelligence's API
|
5
|
-
class Intelligence < Engine
|
6
|
-
attr_reader :prompts, :intelligence_params, :model_kwargs, :batch_size
|
7
|
-
|
8
|
-
# The default parameters to use when asking the engine
|
9
|
-
DEFAULT_PARAMS = {
|
10
|
-
model: "intelligence-1.0",
|
11
|
-
temperature: 0.1
|
12
|
-
}.freeze
|
13
|
-
|
14
|
-
# the default name of the engine
|
15
|
-
DEFAULT_NAME = "Intelligence engine"
|
16
|
-
# the default description of the engine
|
17
|
-
DEFAULT_DESCRIPTION = "useful for when you need to use Intelligence to process complex content. " \
|
18
|
-
"Supports text, images, and other content types"
|
19
|
-
|
20
|
-
def initialize(name: DEFAULT_NAME, description: DEFAULT_DESCRIPTION, prompts: [], batch_size: 20, **kwargs)
|
21
|
-
begin
|
22
|
-
require 'intelligence'
|
23
|
-
rescue LoadError => _e
|
24
|
-
raise LoadError,
|
25
|
-
"The intelligence gem is required. Please add 'gem \"intelligence\"' to your Gemfile and run bundle install"
|
26
|
-
end
|
27
|
-
|
28
|
-
@intelligence_params = DEFAULT_PARAMS.merge(kwargs)
|
29
|
-
@prompts = prompts
|
30
|
-
@batch_size = batch_size
|
31
|
-
super(description: description, name: name)
|
32
|
-
end
|
33
|
-
|
34
|
-
# Get the Intelligence API client
|
35
|
-
def self.intelligence_client(api_key: nil)
|
36
|
-
api_key ||= Boxcars.configuration.intelligence_api_key
|
37
|
-
raise ArgumentError, "Intelligence API key not configured" unless api_key
|
38
|
-
|
39
|
-
Client.new(api_key: api_key)
|
40
|
-
end
|
41
|
-
|
42
|
-
# Stream responses from the Intelligence API
|
43
|
-
def stream(prompt:, inputs: {}, api_key: nil, &block)
|
44
|
-
client = Intelligence.intelligence_client(api_key: api_key)
|
45
|
-
params = intelligence_params.merge(stream: true)
|
46
|
-
|
47
|
-
processed_prompt = if conversation_model?(params[:model])
|
48
|
-
prompt.as_messages(inputs)
|
49
|
-
else
|
50
|
-
{ prompt: prompt.as_prompt(inputs: inputs) }
|
51
|
-
end
|
52
|
-
|
53
|
-
processed_prompt[:content] = process_content(processed_prompt[:content]) if processed_prompt[:content]
|
54
|
-
|
55
|
-
client.stream(parameters: params.merge(processed_prompt), &block)
|
56
|
-
end
|
57
|
-
|
58
|
-
# Process different content types
|
59
|
-
def process_content(content)
|
60
|
-
case content
|
61
|
-
when String
|
62
|
-
{ type: "text", text: content }
|
63
|
-
when Hash
|
64
|
-
validate_content(content)
|
65
|
-
when Array
|
66
|
-
content.map { |c| process_content(c) }
|
67
|
-
else
|
68
|
-
raise ArgumentError, "Unsupported content type: #{content.class}"
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
# Validate content structure
|
73
|
-
def validate_content(content)
|
74
|
-
raise ArgumentError, "Content must have type and text fields" unless content[:type] && content[:text]
|
75
|
-
|
76
|
-
content
|
77
|
-
end
|
78
|
-
|
79
|
-
# Get an answer from the engine
|
80
|
-
def client(prompt:, inputs: {}, api_key: nil, **kwargs)
|
81
|
-
client = Intelligence.intelligence_client(api_key: api_key)
|
82
|
-
params = intelligence_params.merge(kwargs)
|
83
|
-
|
84
|
-
processed_prompt = if conversation_model?(params[:model])
|
85
|
-
prompt.as_messages(inputs)
|
86
|
-
else
|
87
|
-
{ prompt: prompt.as_prompt(inputs: inputs) }
|
88
|
-
end
|
89
|
-
|
90
|
-
# Add content processing
|
91
|
-
processed_prompt[:content] = process_content(processed_prompt[:content]) if processed_prompt[:content]
|
92
|
-
|
93
|
-
Boxcars.debug("Sending to Intelligence:\n#{processed_prompt}", :cyan) if Boxcars.configuration.log_prompts
|
94
|
-
|
95
|
-
# Make API call
|
96
|
-
response = client.generate(parameters: params.merge(processed_prompt))
|
97
|
-
check_response(response)
|
98
|
-
response
|
99
|
-
rescue StandardError => e
|
100
|
-
Boxcars.error("Intelligence Error: #{e.message}", :red)
|
101
|
-
raise
|
102
|
-
end
|
103
|
-
|
104
|
-
# Run the engine with a question
|
105
|
-
def run(question, **kwargs)
|
106
|
-
prompt = Prompt.new(template: question)
|
107
|
-
response = client(prompt: prompt, **kwargs)
|
108
|
-
extract_answer(response)
|
109
|
-
end
|
110
|
-
|
111
|
-
private
|
112
|
-
|
113
|
-
def extract_answer(response)
|
114
|
-
# Handle different response formats
|
115
|
-
if response["choices"]
|
116
|
-
response["choices"].map { |c| c.dig("message", "content") || c["text"] }.join("\n").strip
|
117
|
-
else
|
118
|
-
response["output"] || response.to_s
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
def check_response(response)
|
123
|
-
if response["error"]
|
124
|
-
code = response.dig("error", "code")
|
125
|
-
msg = response.dig("error", "message") || "unknown error"
|
126
|
-
raise KeyError, "INTELLIGENCE_API_KEY not valid" if code == "invalid_api_key"
|
127
|
-
|
128
|
-
raise ValueError, "Intelligence error: #{msg}"
|
129
|
-
end
|
130
|
-
|
131
|
-
# Validate response structure
|
132
|
-
return if response["choices"] || response["output"]
|
133
|
-
|
134
|
-
raise Error, "Invalid response format from Intelligence API"
|
135
|
-
end
|
136
|
-
|
137
|
-
def conversation_model?(_model)
|
138
|
-
true
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|
data/perplexity_example.rb
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
require "debug"
|
2
|
-
require "dotenv/load"
|
3
|
-
require "boxcars"
|
4
|
-
|
5
|
-
# Boxcars.configuration.logger = Logger.new($stdout)
|
6
|
-
|
7
|
-
eng = Boxcars::Perplexityai.new
|
8
|
-
# eng = Boxcars::Openai.new(model: "gpt-4")
|
9
|
-
ctemplate = [
|
10
|
-
Boxcars::Boxcar.syst("The user will type in a city name. Your job is to evaluate if the given city is a good place to live. " \
|
11
|
-
"Build a comprehensive report about livability, weather, cost of living, crime rate, drivability, " \
|
12
|
-
"walkability, and bike ability, and direct flights. In the final answer, for the first paragraph, " \
|
13
|
-
"summarize the pros and cons of living in the city followed by the background information and links " \
|
14
|
-
"for the research. Finalize your answer with an overall grade from A to F on the city."),
|
15
|
-
Boxcars::Boxcar.user("%<input>s")
|
16
|
-
]
|
17
|
-
conv = Boxcars::Conversation.new(lines: ctemplate)
|
18
|
-
|
19
|
-
conversation_prompt = Boxcars::ConversationPrompt.new(conversation: conv, input_variables: [:input], other_inputs: [],
|
20
|
-
output_variables: [:answer])
|
21
|
-
|
22
|
-
boxcar = Boxcars::EngineBoxcar.new(engine: eng, name: "City Helper", prompt: conversation_prompt,
|
23
|
-
description: "Evaluate if a city is a good place to live.")
|
24
|
-
data = boxcar.run(ARGV.fetch(0, "San Francisco"))
|
25
|
-
# train = Boxcars.train.new(boxcars: [boxcar])
|
26
|
-
# data = train.run()
|
27
|
-
# debugger
|
28
|
-
puts data
|