boxcars 0.6.6 → 0.6.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 93deffd0bbce00b58479a93a7c20afdd59fa029bff798d3e90abafb37569ae83
4
- data.tar.gz: 314f504e1b061856c90951730b9d3c8465174eaa007363a12c8805ee1e36fc32
3
+ metadata.gz: de08261a25297ffb293928ed0f041c23bd40c509becb59a2c959c1a9e29eba15
4
+ data.tar.gz: a9896ba99310815803c7ce4143d959a37f0e2c740675fcc57a87ecb74e1e6f5f
5
5
  SHA512:
6
- metadata.gz: 29fc4a409bd98e4528142e295a09727b3a5b0c156343d365601f07852f5f09333ebe6a4a1c798e2e0890420e7414c6be5fa17dd02aa40f528375a207fd74da04
7
- data.tar.gz: a317992e392cda328242487910713cd82968ca371783072564379d3a806c1a8e831d00eace9ea6df80da4bc569526f97ecd1b1a6c133b4b5d7bb43ee8c1b305a
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.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.1.4.2)
17
- activesupport (= 7.1.4.2)
18
- activerecord (7.1.4.2)
19
- activemodel (= 7.1.4.2)
20
- activesupport (= 7.1.4.2)
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.1.4.2)
22
+ activesupport (7.2.2)
23
23
  base64
24
+ benchmark (>= 0.3)
24
25
  bigdecimal
25
- concurrent-ruby (~> 1.0, >= 1.0.2)
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
- mutex_m
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.7.5)
121
+ json (2.9.0)
119
122
  language_server-protocol (3.17.0.3)
120
- logger (1.6.1)
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.1)
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.7-arm64-darwin)
134
+ nokogiri (1.16.8-arm64-darwin)
133
135
  racc (~> 1.4)
134
- nokogiri (1.16.7-x86_64-linux)
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.5.0)
143
+ parser (3.3.6.0)
142
144
  ast (~> 2.4.1)
143
145
  racc
144
- pg (1.5.8)
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.2)
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.67.0)
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.4, < 3.0)
190
- rubocop-ast (>= 1.32.2, < 2.0)
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, < 3.0)
193
- rubocop-ast (1.33.0)
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.1)
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
@@ -79,3 +79,4 @@ require "boxcars/engine/ollama"
79
79
  require "boxcars/engine/openai"
80
80
  require "boxcars/engine/perplexityai"
81
81
  require "boxcars/engine/gpt4all_eng"
82
+ require "boxcars/engine/gemini_ai"
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Boxcars
4
4
  # The current version of the gem.
5
- VERSION = "0.6.6"
5
+ VERSION = "0.6.7"
6
6
  end
@@ -54,7 +54,9 @@ module Boxcars
54
54
  end
55
55
 
56
56
  def xtext(path)
57
+ # rubocop:disable Style/SafeNavigationChainLength
57
58
  rv = xpath(path)&.text&.gsub(/[[:space:]]+/, " ")&.strip
59
+ # rubocop:enable Style/SafeNavigationChainLength
58
60
  return nil if rv.empty?
59
61
 
60
62
  rv
data/lib/boxcars.rb CHANGED
@@ -62,6 +62,11 @@ module Boxcars
62
62
  key_lookup(:groq_api_key, kwargs)
63
63
  end
64
64
 
65
+ # @return [String] The Google AI API key either from arg or env.
66
+ def gemini_api_key(**kwargs)
67
+ key_lookup(:gemini_api_key, kwargs)
68
+ end
69
+
65
70
  private
66
71
 
67
72
  def check_key(key, val)
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.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-11-15 00:00:00.000000000 Z
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