boxcars 0.6.6 → 0.6.7
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 +28 -25
- data/lib/boxcars/engine/cohere.rb +0 -3
- data/lib/boxcars/engine/gemini_ai.rb +160 -0
- data/lib/boxcars/engine.rb +1 -0
- data/lib/boxcars/version.rb +1 -1
- data/lib/boxcars/x_node.rb +2 -0
- data/lib/boxcars.rb +5 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: de08261a25297ffb293928ed0f041c23bd40c509becb59a2c959c1a9e29eba15
|
4
|
+
data.tar.gz: a9896ba99310815803c7ce4143d959a37f0e2c740675fcc57a87ecb74e1e6f5f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a400bcd9c05ee4d81b67340b0edb9612b94406b02c93db6deaf29604a812469798d23256aa180bc76464025a7c26730395e3f3bd2d5d0235210acae0c60bbdbe
|
7
|
+
data.tar.gz: 1001cc39c183ae93aa9e8e30fc85679b1bc3ad8a1dc77888a9150ac8ad134d5b595fc94fdf4c68d6e7843bc0be6b1d5690b60263516445e4d6b4fc243b264b93
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
boxcars (0.6.
|
4
|
+
boxcars (0.6.7)
|
5
5
|
anthropic (~> 0.1)
|
6
6
|
google_search_results (~> 2.2)
|
7
7
|
gpt4all (~> 0.0.4)
|
@@ -13,22 +13,24 @@ PATH
|
|
13
13
|
GEM
|
14
14
|
remote: https://rubygems.org/
|
15
15
|
specs:
|
16
|
-
activemodel (7.
|
17
|
-
activesupport (= 7.
|
18
|
-
activerecord (7.
|
19
|
-
activemodel (= 7.
|
20
|
-
activesupport (= 7.
|
16
|
+
activemodel (7.2.2)
|
17
|
+
activesupport (= 7.2.2)
|
18
|
+
activerecord (7.2.2)
|
19
|
+
activemodel (= 7.2.2)
|
20
|
+
activesupport (= 7.2.2)
|
21
21
|
timeout (>= 0.4.0)
|
22
|
-
activesupport (7.
|
22
|
+
activesupport (7.2.2)
|
23
23
|
base64
|
24
|
+
benchmark (>= 0.3)
|
24
25
|
bigdecimal
|
25
|
-
concurrent-ruby (~> 1.0, >= 1.
|
26
|
+
concurrent-ruby (~> 1.0, >= 1.3.1)
|
26
27
|
connection_pool (>= 2.2.5)
|
27
28
|
drb
|
28
29
|
i18n (>= 1.6, < 2)
|
30
|
+
logger (>= 1.4.2)
|
29
31
|
minitest (>= 5.1)
|
30
|
-
|
31
|
-
tzinfo (~> 2.0)
|
32
|
+
securerandom (>= 0.3)
|
33
|
+
tzinfo (~> 2.0, >= 2.0.5)
|
32
34
|
addressable (2.8.7)
|
33
35
|
public_suffix (>= 2.0.2, < 7.0)
|
34
36
|
anthropic (0.3.2)
|
@@ -56,6 +58,7 @@ GEM
|
|
56
58
|
async-pool (0.7.0)
|
57
59
|
async (>= 1.25)
|
58
60
|
base64 (0.2.0)
|
61
|
+
benchmark (0.4.0)
|
59
62
|
bigdecimal (3.1.8)
|
60
63
|
concurrent-ruby (1.3.4)
|
61
64
|
connection_pool (2.4.1)
|
@@ -115,33 +118,32 @@ GEM
|
|
115
118
|
irb (1.14.0)
|
116
119
|
rdoc (>= 4.0.0)
|
117
120
|
reline (>= 0.4.2)
|
118
|
-
json (2.
|
121
|
+
json (2.9.0)
|
119
122
|
language_server-protocol (3.17.0.3)
|
120
|
-
logger (1.6.
|
123
|
+
logger (1.6.2)
|
121
124
|
mime-types (3.5.2)
|
122
125
|
mime-types-data (~> 3.2015)
|
123
126
|
mime-types-data (3.2024.0702)
|
124
|
-
minitest (5.25.
|
127
|
+
minitest (5.25.4)
|
125
128
|
multi_json (1.15.0)
|
126
129
|
multipart-post (2.4.1)
|
127
|
-
mutex_m (0.2.0)
|
128
130
|
net-http (0.4.1)
|
129
131
|
uri
|
130
132
|
netrc (0.11.0)
|
131
133
|
nio4r (2.7.3)
|
132
|
-
nokogiri (1.16.
|
134
|
+
nokogiri (1.16.8-arm64-darwin)
|
133
135
|
racc (~> 1.4)
|
134
|
-
nokogiri (1.16.
|
136
|
+
nokogiri (1.16.8-x86_64-linux)
|
135
137
|
racc (~> 1.4)
|
136
138
|
octokit (4.25.1)
|
137
139
|
faraday (>= 1, < 3)
|
138
140
|
sawyer (~> 0.9)
|
139
141
|
os (1.1.4)
|
140
142
|
parallel (1.26.3)
|
141
|
-
parser (3.3.
|
143
|
+
parser (3.3.6.0)
|
142
144
|
ast (~> 2.4.1)
|
143
145
|
racc
|
144
|
-
pg (1.5.
|
146
|
+
pg (1.5.9)
|
145
147
|
pgvector (0.2.2)
|
146
148
|
protocol-hpack (1.4.3)
|
147
149
|
protocol-http (0.26.8)
|
@@ -158,7 +160,7 @@ GEM
|
|
158
160
|
rake (13.2.1)
|
159
161
|
rdoc (6.7.0)
|
160
162
|
psych (>= 4.0.0)
|
161
|
-
regexp_parser (2.9.
|
163
|
+
regexp_parser (2.9.3)
|
162
164
|
reline (0.5.9)
|
163
165
|
io-console (~> 0.5)
|
164
166
|
rest-client (2.1.0)
|
@@ -180,17 +182,17 @@ GEM
|
|
180
182
|
diff-lcs (>= 1.2.0, < 2.0)
|
181
183
|
rspec-support (~> 3.13.0)
|
182
184
|
rspec-support (3.13.1)
|
183
|
-
rubocop (1.
|
185
|
+
rubocop (1.69.1)
|
184
186
|
json (~> 2.3)
|
185
187
|
language_server-protocol (>= 3.17.0)
|
186
188
|
parallel (~> 1.10)
|
187
189
|
parser (>= 3.3.0.2)
|
188
190
|
rainbow (>= 2.2.2, < 4.0)
|
189
|
-
regexp_parser (>= 2.
|
190
|
-
rubocop-ast (>= 1.
|
191
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
192
|
+
rubocop-ast (>= 1.36.2, < 2.0)
|
191
193
|
ruby-progressbar (~> 1.7)
|
192
|
-
unicode-display_width (>= 2.4.0, <
|
193
|
-
rubocop-ast (1.
|
194
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
195
|
+
rubocop-ast (1.36.2)
|
194
196
|
parser (>= 3.3.1.0)
|
195
197
|
rubocop-rake (0.6.0)
|
196
198
|
rubocop (~> 1.0)
|
@@ -204,11 +206,12 @@ GEM
|
|
204
206
|
sawyer (0.9.2)
|
205
207
|
addressable (>= 2.3.5)
|
206
208
|
faraday (>= 0.17.3, < 3)
|
209
|
+
securerandom (0.4.0)
|
207
210
|
sqlite3 (2.0.4-arm64-darwin)
|
208
211
|
sqlite3 (2.0.4-x86_64-linux-gnu)
|
209
212
|
stringio (3.1.1)
|
210
213
|
strings-ansi (0.2.0)
|
211
|
-
timeout (0.4.
|
214
|
+
timeout (0.4.2)
|
212
215
|
timers (4.3.5)
|
213
216
|
traces (0.11.1)
|
214
217
|
tty-cursor (0.7.1)
|
@@ -50,9 +50,6 @@ module Boxcars
|
|
50
50
|
|
51
51
|
# Make the API call
|
52
52
|
response = connection.post { |req| req.body = params.to_json }
|
53
|
-
|
54
|
-
# response_data = JSON.parse(response.body, symbolize_names: true)
|
55
|
-
# response_data[:text]
|
56
53
|
JSON.parse(response.body, symbolize_names: true)
|
57
54
|
end
|
58
55
|
|
@@ -0,0 +1,160 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Boxcars - a framework for running a series of tools to get an answer to a question.
|
4
|
+
module Boxcars
|
5
|
+
# A engine that uses Gemini's API.
|
6
|
+
class GeminiAi < Engine
|
7
|
+
attr_reader :prompts, :llm_params, :model_kwargs, :batch_size
|
8
|
+
|
9
|
+
# The default parameters to use when asking the engine.
|
10
|
+
DEFAULT_PARAMS = {
|
11
|
+
model: "gemini-1.5-flash-latest"
|
12
|
+
}.freeze
|
13
|
+
|
14
|
+
# the default name of the engine
|
15
|
+
DEFAULT_NAME = "Google Gemini AI engine"
|
16
|
+
# the default description of the engine
|
17
|
+
DEFAULT_DESCRIPTION = "useful for when you need to use Google Gemini AI to answer questions. " \
|
18
|
+
"You should ask targeted questions"
|
19
|
+
|
20
|
+
# A engine is the driver for a single tool to run.
|
21
|
+
# @param name [String] The name of the engine. Defaults to "OpenAI engine".
|
22
|
+
# @param description [String] A description of the engine. Defaults to:
|
23
|
+
# useful for when you need to use AI to answer questions. You should ask targeted questions".
|
24
|
+
# @param prompts [Array<String>] The prompts to use when asking the engine. Defaults to [].
|
25
|
+
def initialize(name: DEFAULT_NAME, description: DEFAULT_DESCRIPTION, prompts: [], **kwargs)
|
26
|
+
@llm_params = DEFAULT_PARAMS.merge(kwargs)
|
27
|
+
@prompts = prompts
|
28
|
+
@batch_size = 20
|
29
|
+
super(description: description, name: name)
|
30
|
+
end
|
31
|
+
|
32
|
+
def conversation_model?(_model)
|
33
|
+
true
|
34
|
+
end
|
35
|
+
|
36
|
+
def chat(params, gemini_api_key)
|
37
|
+
raise Boxcars::ConfigurationError('Google AI API key not set') if gemini_api_key.blank?
|
38
|
+
|
39
|
+
model_string = params.delete(:model_string)
|
40
|
+
raise Boxcars::ConfigurationError('Google AI API key not set') if model_string.blank?
|
41
|
+
|
42
|
+
# Define the API endpoint and parameters
|
43
|
+
api_endpoint = "https://generativelanguage.googleapis.com/v1beta/models/#{model_string}:generateContent?key=#{gemini_api_key}"
|
44
|
+
|
45
|
+
connection = Faraday.new(api_endpoint) do |faraday|
|
46
|
+
faraday.request :url_encoded
|
47
|
+
faraday.headers['Content-Type'] = 'application/json'
|
48
|
+
end
|
49
|
+
|
50
|
+
# Make the API call
|
51
|
+
response = connection.post { |req| req.body = params.to_json }
|
52
|
+
|
53
|
+
JSON.parse(response.body, symbolize_names: true)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Get an answer from the engine.
|
57
|
+
# @param prompt [String] The prompt to use when asking the engine.
|
58
|
+
# @param gemini_api_key [String] Optional api key to use when asking the engine.
|
59
|
+
# Defaults to Boxcars.configuration.gemini_api_key.
|
60
|
+
# @param kwargs [Hash] Additional parameters to pass to the engine if wanted.
|
61
|
+
def client(prompt:, inputs: {}, **kwargs)
|
62
|
+
api_key = Boxcars.configuration.gemini_api_key(**kwargs)
|
63
|
+
option_params = llm_params.merge(kwargs)
|
64
|
+
model_string = option_params.delete(:model) || DEFAULT_PARAMS[:model]
|
65
|
+
convo = prompt.as_messages(inputs: inputs)
|
66
|
+
# Convert conversation to Google Gemini format
|
67
|
+
params = to_google_gemini_format(convo[:messages], option_params)
|
68
|
+
params[:model_string] = model_string
|
69
|
+
Boxcars.debug("Prompt after formatting:#{params[:message]}", :cyan) if Boxcars.configuration.log_prompts
|
70
|
+
chat(params, api_key)
|
71
|
+
end
|
72
|
+
|
73
|
+
# get an answer from the engine for a question.
|
74
|
+
# @param question [String] The question to ask the engine.
|
75
|
+
# @param kwargs [Hash] Additional parameters to pass to the engine if wanted.
|
76
|
+
def run(question, **kwargs)
|
77
|
+
prompt = Prompt.new(template: question)
|
78
|
+
response = client(prompt: prompt, **kwargs)
|
79
|
+
|
80
|
+
raise Error, "GeminiAI: No response from API" unless response
|
81
|
+
raise Error, "GeminiAI: #{response[:error]}" if response[:error]
|
82
|
+
|
83
|
+
answer = response[:candidates].first[:content][:parts].first[:text]
|
84
|
+
Boxcars.debug(response, :yellow)
|
85
|
+
answer
|
86
|
+
end
|
87
|
+
|
88
|
+
# Get the default parameters for the engine.
|
89
|
+
def default_params
|
90
|
+
llm_params
|
91
|
+
end
|
92
|
+
|
93
|
+
# make sure we got a valid response
|
94
|
+
# @param response [Hash] The response to check.
|
95
|
+
# @param must_haves [Array<String>] The keys that must be in the response. Defaults to %w[choices].
|
96
|
+
# @raise [KeyError] if there is an issue with the access token.
|
97
|
+
# @raise [ValueError] if the response is not valid.
|
98
|
+
def check_response(response, must_haves: %w[completion])
|
99
|
+
if response['error']
|
100
|
+
code = response.dig('error', 'code')
|
101
|
+
msg = response.dig('error', 'message') || 'unknown error'
|
102
|
+
raise KeyError, "ANTHOPIC_API_KEY not valid" if code == 'invalid_api_key'
|
103
|
+
|
104
|
+
raise ValueError, "Gemini error: #{msg}"
|
105
|
+
end
|
106
|
+
|
107
|
+
must_haves.each do |key|
|
108
|
+
raise ValueError, "Expecting key #{key} in response" unless response.key?(key)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# the engine type
|
113
|
+
def engine_type
|
114
|
+
"claude"
|
115
|
+
end
|
116
|
+
|
117
|
+
# lookup the context size for a model by name
|
118
|
+
# @param modelname [String] The name of the model to lookup.
|
119
|
+
def modelname_to_contextsize(_modelname)
|
120
|
+
100000
|
121
|
+
end
|
122
|
+
|
123
|
+
# Calculate the maximum number of tokens possible to generate for a prompt.
|
124
|
+
# @param prompt_text [String] The prompt text to use.
|
125
|
+
# @return [Integer] the number of tokens possible to generate.
|
126
|
+
def max_tokens_for_prompt(prompt_text)
|
127
|
+
num_tokens = get_num_tokens(prompt_text)
|
128
|
+
|
129
|
+
# get max context size for model by name
|
130
|
+
max_size = modelname_to_contextsize(model_name)
|
131
|
+
max_size - num_tokens
|
132
|
+
end
|
133
|
+
|
134
|
+
def to_google_gemini_format(convo, option_params)
|
135
|
+
instructions = convo.shift.last if convo.first && convo.first[:role] == :system
|
136
|
+
system_instructions = instructions || "You are a helpful assistant."
|
137
|
+
|
138
|
+
# Convert conversation history to the format expected by Google
|
139
|
+
contents = convo.map { |message| { text: message[:content] } }
|
140
|
+
|
141
|
+
generation_config = {}
|
142
|
+
if option_params.length.positive?
|
143
|
+
generation_config.merge!(option_params)
|
144
|
+
generation_config[:stopSequences] = [generation_config.delete(:stop)] if generation_config[:stop].present?
|
145
|
+
end
|
146
|
+
|
147
|
+
rv = {
|
148
|
+
system_instruction: { parts: { text: system_instructions } }, # System instructions or context
|
149
|
+
contents: { parts: contents } # The chat messages
|
150
|
+
}
|
151
|
+
|
152
|
+
rv[:generationConfig] = generation_config if generation_config.length.positive?
|
153
|
+
rv
|
154
|
+
end
|
155
|
+
|
156
|
+
def default_prefixes
|
157
|
+
{ system: "SYSTEM: ", user: "USER: ", assistant: "CHATBOT: ", history: :history }
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
data/lib/boxcars/engine.rb
CHANGED
data/lib/boxcars/version.rb
CHANGED
data/lib/boxcars/x_node.rb
CHANGED
data/lib/boxcars.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.6.
|
4
|
+
version: 0.6.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Francis Sullivan
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2024-
|
12
|
+
date: 2024-12-04 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: anthropic
|
@@ -159,6 +159,7 @@ files:
|
|
159
159
|
- lib/boxcars/engine/anthropic.rb
|
160
160
|
- lib/boxcars/engine/cohere.rb
|
161
161
|
- lib/boxcars/engine/engine_result.rb
|
162
|
+
- lib/boxcars/engine/gemini_ai.rb
|
162
163
|
- lib/boxcars/engine/gpt4all_eng.rb
|
163
164
|
- lib/boxcars/engine/groq.rb
|
164
165
|
- lib/boxcars/engine/ollama.rb
|