boxcars 0.4.9 → 0.4.10

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: b1199d0161dfab186b1fd3e85789a86d242d5351a0439bbdec719d0d55eecdaa
4
- data.tar.gz: 4f0c4b90d023c7a9bb86d45b0aa01ff0d7805bdf7d5f2b36401c87525eb15403
3
+ metadata.gz: 4e9d011a30aa2b478281dca050fec260a09f5e985454beee40405d0ae6991411
4
+ data.tar.gz: 2bd6e365273101ebd40a35780a1ef9913f4125a29dc24acaab317146a7c19344
5
5
  SHA512:
6
- metadata.gz: abc63d10170ba704a0cc1bffc60dc2cc29ffa6927e945050d7157a7779d576be25bf8ed3c738fa6be0ec1e7fdc898ba2d41fc807026e7a2a1232ce7f68d3f283
7
- data.tar.gz: 85cf33b3832230a9294557f1a23f305c97a1c8bc31414849160118524a64d1e27bdd74d7b1e186895596b9ce9e82afd6b8fb1fe9e7b081b0e33617228a947834
6
+ metadata.gz: f8937c0f246c0488e0656646bdcd9a02a62edbb068fbd92710e2e275c99c8c7006e4bfec277206b63ae5507833481ec2db9b0a274bb74be743d4739e8bfacc98
7
+ data.tar.gz: 5c176f8854d058cad2426786707c11a49759781167e1a861b1e376da1f7c9b679e75b7a33645134cc3e8d7298e907c0c831059328979ff8d590262b5804e76d8
data/Gemfile CHANGED
@@ -7,7 +7,7 @@ gemspec
7
7
 
8
8
  gem "debug", "~> 1.9"
9
9
 
10
- gem "dotenv", "~> 2.8"
10
+ gem "dotenv", "~> 3.1"
11
11
 
12
12
  gem "rake", "~> 13.1"
13
13
 
@@ -31,10 +31,10 @@ gem "pg", "~> 1.5"
31
31
  gem "pgvector", "~> 0.2.2"
32
32
 
33
33
  group :development, :test do
34
- gem "rspec", "~> 3.2"
34
+ gem "rspec", "~> 3.13"
35
35
  gem "rubocop", "~> 1.60"
36
36
  gem "vcr", "~> 6.2.0"
37
- gem "webmock", "~> 3.19.1"
37
+ gem "webmock", "~> 3.23.0"
38
38
  gem "rubocop-rake", "~> 0.6.0"
39
- gem "rubocop-rspec", "~> 2.26"
39
+ gem "rubocop-rspec", "~> 2.29"
40
40
  end
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- boxcars (0.4.9)
4
+ boxcars (0.4.10)
5
5
  anthropic (~> 0.1)
6
6
  google_search_results (~> 2.2)
7
7
  gpt4all (~> 0.0.4)
@@ -48,31 +48,31 @@ GEM
48
48
  async
49
49
  async-pool (0.4.0)
50
50
  async (>= 1.25)
51
- base64 (0.2.0)
51
+ bigdecimal (3.1.6)
52
52
  concurrent-ruby (1.2.2)
53
53
  console (1.17.4)
54
54
  fiber-annotation
55
55
  fiber-local
56
- crack (0.4.5)
56
+ crack (1.0.0)
57
+ bigdecimal
57
58
  rexml
58
59
  debug (1.9.1)
59
60
  irb (~> 1.10)
60
61
  reline (>= 0.3.8)
61
- diff-lcs (1.5.0)
62
+ diff-lcs (1.5.1)
62
63
  domain_name (0.5.20190701)
63
64
  unf (>= 0.0.5, < 1.0.0)
64
- dotenv (2.8.1)
65
+ dotenv (3.1.0)
65
66
  event_stream_parser (1.0.0)
66
- faraday (2.7.12)
67
- base64
68
- faraday-net_http (>= 2.0, < 3.1)
69
- ruby2_keywords (>= 0.0.4)
67
+ faraday (2.9.0)
68
+ faraday-net_http (>= 2.0, < 3.2)
70
69
  faraday-http-cache (2.5.0)
71
70
  faraday (>= 0.8)
72
71
  faraday-multipart (1.0.4)
73
72
  multipart-post (~> 2)
74
- faraday-net_http (3.0.2)
75
- faraday-retry (2.2.0)
73
+ faraday-net_http (3.1.0)
74
+ net-http
75
+ faraday-retry (2.2.1)
76
76
  faraday (~> 2.0)
77
77
  fiber-annotation (0.2.0)
78
78
  fiber-local (1.0.0)
@@ -101,7 +101,7 @@ GEM
101
101
  irb (1.11.0)
102
102
  rdoc
103
103
  reline (>= 0.3.8)
104
- json (2.7.1)
104
+ json (2.7.2)
105
105
  language_server-protocol (3.17.0.3)
106
106
  mime-types (3.4.1)
107
107
  mime-types-data (~> 3.2015)
@@ -109,11 +109,13 @@ GEM
109
109
  minitest (5.20.0)
110
110
  multi_json (1.15.0)
111
111
  multipart-post (2.3.0)
112
+ net-http (0.4.1)
113
+ uri
112
114
  netrc (0.11.0)
113
115
  nio4r (2.5.9)
114
- nokogiri (1.16.0-arm64-darwin)
116
+ nokogiri (1.16.2-arm64-darwin)
115
117
  racc (~> 1.4)
116
- nokogiri (1.16.0-x86_64-linux)
118
+ nokogiri (1.16.2-x86_64-linux)
117
119
  racc (~> 1.4)
118
120
  octokit (4.25.1)
119
121
  faraday (>= 1, < 3)
@@ -123,7 +125,7 @@ GEM
123
125
  parser (3.3.0.5)
124
126
  ast (~> 2.4.1)
125
127
  racc
126
- pg (1.5.4)
128
+ pg (1.5.6)
127
129
  pgvector (0.2.2)
128
130
  protocol-hpack (1.4.2)
129
131
  protocol-http (0.25.0)
@@ -138,7 +140,7 @@ GEM
138
140
  racc (1.7.3)
139
141
  rainbow (3.1.1)
140
142
  rake (13.1.0)
141
- rdoc (6.6.2)
143
+ rdoc (6.6.3.1)
142
144
  psych (>= 4.0.0)
143
145
  regexp_parser (2.9.0)
144
146
  reline (0.4.1)
@@ -149,19 +151,19 @@ GEM
149
151
  mime-types (>= 1.16, < 4.0)
150
152
  netrc (~> 0.8)
151
153
  rexml (3.2.6)
152
- rspec (3.12.0)
153
- rspec-core (~> 3.12.0)
154
- rspec-expectations (~> 3.12.0)
155
- rspec-mocks (~> 3.12.0)
156
- rspec-core (3.12.2)
157
- rspec-support (~> 3.12.0)
158
- rspec-expectations (3.12.3)
154
+ rspec (3.13.0)
155
+ rspec-core (~> 3.13.0)
156
+ rspec-expectations (~> 3.13.0)
157
+ rspec-mocks (~> 3.13.0)
158
+ rspec-core (3.13.0)
159
+ rspec-support (~> 3.13.0)
160
+ rspec-expectations (3.13.0)
159
161
  diff-lcs (>= 1.2.0, < 2.0)
160
- rspec-support (~> 3.12.0)
161
- rspec-mocks (3.12.6)
162
+ rspec-support (~> 3.13.0)
163
+ rspec-mocks (3.13.0)
162
164
  diff-lcs (>= 1.2.0, < 2.0)
163
- rspec-support (~> 3.12.0)
164
- rspec-support (3.12.1)
165
+ rspec-support (~> 3.13.0)
166
+ rspec-support (3.13.1)
165
167
  rubocop (1.60.2)
166
168
  json (~> 2.3)
167
169
  language_server-protocol (>= 3.17.0)
@@ -173,29 +175,31 @@ GEM
173
175
  rubocop-ast (>= 1.30.0, < 2.0)
174
176
  ruby-progressbar (~> 1.7)
175
177
  unicode-display_width (>= 2.4.0, < 3.0)
176
- rubocop-ast (1.30.0)
177
- parser (>= 3.2.1.0)
178
+ rubocop-ast (1.31.2)
179
+ parser (>= 3.3.0.4)
178
180
  rubocop-capybara (2.20.0)
179
181
  rubocop (~> 1.41)
180
182
  rubocop-factory_bot (2.25.1)
181
183
  rubocop (~> 1.41)
182
184
  rubocop-rake (0.6.0)
183
185
  rubocop (~> 1.0)
184
- rubocop-rspec (2.26.1)
186
+ rubocop-rspec (2.29.1)
185
187
  rubocop (~> 1.40)
186
188
  rubocop-capybara (~> 2.17)
187
189
  rubocop-factory_bot (~> 2.22)
190
+ rubocop-rspec_rails (~> 2.28)
191
+ rubocop-rspec_rails (2.28.2)
192
+ rubocop (~> 1.40)
188
193
  ruby-openai (6.3.1)
189
194
  event_stream_parser (>= 0.3.0, < 2.0.0)
190
195
  faraday (>= 1)
191
196
  faraday-multipart (>= 1)
192
197
  ruby-progressbar (1.13.0)
193
- ruby2_keywords (0.0.5)
194
198
  sawyer (0.9.2)
195
199
  addressable (>= 2.3.5)
196
200
  faraday (>= 0.17.3, < 3)
197
- sqlite3 (1.7.1-arm64-darwin)
198
- sqlite3 (1.7.1-x86_64-linux)
201
+ sqlite3 (1.7.2-arm64-darwin)
202
+ sqlite3 (1.7.2-x86_64-linux)
199
203
  stringio (3.1.0)
200
204
  strings-ansi (0.2.0)
201
205
  timers (4.3.5)
@@ -213,8 +217,9 @@ GEM
213
217
  unf_ext
214
218
  unf_ext (0.0.8.2)
215
219
  unicode-display_width (2.5.0)
220
+ uri (0.13.0)
216
221
  vcr (6.2.0)
217
- webmock (3.19.1)
222
+ webmock (3.23.0)
218
223
  addressable (>= 2.8.0)
219
224
  crack (>= 0.3.2)
220
225
  hashdiff (>= 0.4.0, < 2.0.0)
@@ -230,7 +235,7 @@ DEPENDENCIES
230
235
  async (~> 1.31.0)
231
236
  boxcars!
232
237
  debug (~> 1.9)
233
- dotenv (~> 2.8)
238
+ dotenv (~> 3.1)
234
239
  faraday-retry (~> 2.0)
235
240
  github_changelog_generator (~> 1.16)
236
241
  hnswlib (~> 0.9.0)
@@ -238,13 +243,13 @@ DEPENDENCIES
238
243
  pgvector (~> 0.2.2)
239
244
  rake (~> 13.1)
240
245
  rest-client (~> 2.1)
241
- rspec (~> 3.2)
246
+ rspec (~> 3.13)
242
247
  rubocop (~> 1.60)
243
248
  rubocop-rake (~> 0.6.0)
244
- rubocop-rspec (~> 2.26)
249
+ rubocop-rspec (~> 2.29)
245
250
  sqlite3 (~> 1.7)
246
251
  vcr (~> 6.2.0)
247
- webmock (~> 3.19.1)
252
+ webmock (~> 3.23.0)
248
253
 
249
254
  BUNDLED WITH
250
255
  2.4.16
data/README.md CHANGED
@@ -46,7 +46,7 @@ Or install it yourself as:
46
46
 
47
47
  ## Usage
48
48
 
49
- We will be adding more examples soon, but here are a couple to get you started. First, you'll need to set up your environment variables for OpenAI and Google SERP (OPENAI_ACCESS_TOKEN, SERPAPI_API_KEY). If you prefer not to set these variables in your environment, you can pass them directly into the API.
49
+ We will be adding more examples soon, but here are a couple to get you started. First, you'll need to set up your environment variables for services like OpenAI, Anthropic, and Google SERP (OPENAI_ACCESS_TOKEN, ANTHROPIC_API_KEY,SERPAPI_API_KEY) etc. If you prefer not to set these variables in your environment, you can pass them directly into the API.
50
50
 
51
51
  In the examples below, we added one Ruby gem to load the environment at the first line, but depending on what you want, you might not need this.
52
52
  ```ruby
@@ -25,8 +25,8 @@ module Boxcars
25
25
  def run(question)
26
26
  search = ::GoogleSearch.new(q: question)
27
27
  rv = find_answer(search.get_hash)
28
- puts "Question: #{question}"
29
- puts "Answer: #{rv}"
28
+ Boxcars.info "Question: #{question}"
29
+ Boxcars.info "Answer: #{rv}"
30
30
  rv
31
31
  end
32
32
 
@@ -0,0 +1,187 @@
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 Cohere's API.
6
+ class Cohere < 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: "command-r-plus",
12
+ max_tokens: 4000,
13
+ max_input_tokens: 1000,
14
+ temperature: 0.2
15
+ }.freeze
16
+
17
+ # the default name of the engine
18
+ DEFAULT_NAME = "Cohere engine"
19
+ # the default description of the engine
20
+ DEFAULT_DESCRIPTION = "useful for when you need to use Cohere AI to answer questions. " \
21
+ "You should ask targeted questions"
22
+
23
+ # A engine is the driver for a single tool to run.
24
+ # @param name [String] The name of the engine. Defaults to "OpenAI engine".
25
+ # @param description [String] A description of the engine. Defaults to:
26
+ # useful for when you need to use AI to answer questions. You should ask targeted questions".
27
+ # @param prompts [Array<String>] The prompts to use when asking the engine. Defaults to [].
28
+ def initialize(name: DEFAULT_NAME, description: DEFAULT_DESCRIPTION, prompts: [], **kwargs)
29
+ @llm_params = DEFAULT_PARAMS.merge(kwargs)
30
+ @prompts = prompts
31
+ @batch_size = 20
32
+ super(description: description, name: name)
33
+ end
34
+
35
+ def conversation_model?(_model)
36
+ true
37
+ end
38
+
39
+ def chat(params, cohere_api_key)
40
+ raise Boxcars::ConfigurationError('Cohere API key not set') if cohere_api_key.blank?
41
+
42
+ # Define the API endpoint and parameters
43
+ api_endpoint = 'https://api.cohere.ai/v1/chat'
44
+
45
+ connection = Faraday.new(api_endpoint) do |faraday|
46
+ faraday.request :url_encoded
47
+ faraday.headers['Authorization'] = "Bearer #{cohere_api_key}"
48
+ faraday.headers['Content-Type'] = 'application/json'
49
+ end
50
+
51
+ # Make the API call
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
+ JSON.parse(response.body, symbolize_names: true)
57
+ end
58
+
59
+ # Get an answer from the engine.
60
+ # @param prompt [String] The prompt to use when asking the engine.
61
+ # @param cohere_api_key [String] Optional api key to use when asking the engine.
62
+ # Defaults to Boxcars.configuration.cohere_api_key.
63
+ # @param kwargs [Hash] Additional parameters to pass to the engine if wanted.
64
+ def client(prompt:, inputs: {}, **kwargs)
65
+ api_key = Boxcars.configuration.cohere_api_key(**kwargs)
66
+ params = prompt.as_prompt(inputs: inputs, prefixes: default_prefixes, show_roles: true).merge(llm_params.merge(kwargs))
67
+ params[:message] = params.delete(:prompt)
68
+ params[:stop_sequences] = params.delete(:stop) if params.key?(:stop)
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, "Cohere: No response from API" unless response
81
+ raise Error, "Cohere: #{response[:error]}" if response[:error]
82
+
83
+ answer = response[: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
+ # Get generation informaton
94
+ # @param sub_choices [Array<Hash>] The choices to get generation info for.
95
+ # @return [Array<Generation>] The generation information.
96
+ def generation_info(sub_choices)
97
+ sub_choices.map do |choice|
98
+ Generation.new(
99
+ text: choice["completion"],
100
+ generation_info: {
101
+ finish_reason: choice.fetch("stop_reason", nil),
102
+ logprobs: choice.fetch("logprobs", nil)
103
+ }
104
+ )
105
+ end
106
+ end
107
+
108
+ # make sure we got a valid response
109
+ # @param response [Hash] The response to check.
110
+ # @param must_haves [Array<String>] The keys that must be in the response. Defaults to %w[choices].
111
+ # @raise [KeyError] if there is an issue with the access token.
112
+ # @raise [ValueError] if the response is not valid.
113
+ def check_response(response, must_haves: %w[completion])
114
+ if response['error']
115
+ code = response.dig('error', 'code')
116
+ msg = response.dig('error', 'message') || 'unknown error'
117
+ raise KeyError, "ANTHOPIC_API_KEY not valid" if code == 'invalid_api_key'
118
+
119
+ raise ValueError, "Cohere error: #{msg}"
120
+ end
121
+
122
+ must_haves.each do |key|
123
+ raise ValueError, "Expecting key #{key} in response" unless response.key?(key)
124
+ end
125
+ end
126
+
127
+ # Call out to OpenAI's endpoint with k unique prompts.
128
+ # @param prompts [Array<String>] The prompts to pass into the model.
129
+ # @param inputs [Array<String>] The inputs to subsitite into the prompt.
130
+ # @param stop [Array<String>] Optional list of stop words to use when generating.
131
+ # @return [EngineResult] The full engine output.
132
+ def generate(prompts:, stop: nil)
133
+ params = {}
134
+ params[:stop] = stop if stop
135
+ choices = []
136
+ # Get the token usage from the response.
137
+ # Includes prompt, completion, and total tokens used.
138
+ prompts.each_slice(batch_size) do |sub_prompts|
139
+ sub_prompts.each do |sprompts, inputs|
140
+ response = client(prompt: sprompts, inputs: inputs, **params)
141
+ check_response(response)
142
+ choices << response
143
+ end
144
+ end
145
+
146
+ n = params.fetch(:n, 1)
147
+ generations = []
148
+ prompts.each_with_index do |_prompt, i|
149
+ sub_choices = choices[i * n, (i + 1) * n]
150
+ generations.push(generation_info(sub_choices))
151
+ end
152
+ EngineResult.new(generations: generations, engine_output: { token_usage: {} })
153
+ end
154
+ # rubocop:enable Metrics/AbcSize
155
+
156
+ # the engine type
157
+ def engine_type
158
+ "claude"
159
+ end
160
+
161
+ # calculate the number of tokens used
162
+ def get_num_tokens(text:)
163
+ text.split.length # TODO: hook up to token counting gem
164
+ end
165
+
166
+ # lookup the context size for a model by name
167
+ # @param modelname [String] The name of the model to lookup.
168
+ def modelname_to_contextsize(_modelname)
169
+ 100000
170
+ end
171
+
172
+ # Calculate the maximum number of tokens possible to generate for a prompt.
173
+ # @param prompt_text [String] The prompt text to use.
174
+ # @return [Integer] the number of tokens possible to generate.
175
+ def max_tokens_for_prompt(prompt_text)
176
+ num_tokens = get_num_tokens(prompt_text)
177
+
178
+ # get max context size for model by name
179
+ max_size = modelname_to_contextsize(model_name)
180
+ max_size - num_tokens
181
+ end
182
+
183
+ def default_prefixes
184
+ { system: "SYSTEM: ", user: "USER: ", assistant: "CHATBOT: ", history: :history }
185
+ end
186
+ end
187
+ end
@@ -21,6 +21,7 @@ end
21
21
 
22
22
  require "boxcars/engine/engine_result"
23
23
  require "boxcars/engine/anthropic"
24
+ require "boxcars/engine/cohere"
24
25
  require "boxcars/engine/openai"
25
26
  require "boxcars/engine/perplexityai"
26
27
  require "boxcars/engine/gpt4all_eng"
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Boxcars
4
4
  # The current version of the gem.
5
- VERSION = "0.4.9"
5
+ VERSION = "0.4.10"
6
6
  end
data/lib/boxcars.rb CHANGED
@@ -52,6 +52,11 @@ module Boxcars
52
52
  key_lookup(:anthropic_api_key, kwargs)
53
53
  end
54
54
 
55
+ # @return [String] The Anthropic API key either from arg or env.
56
+ def cohere_api_key(**kwargs)
57
+ key_lookup(:cohere_api_key, kwargs)
58
+ end
59
+
55
60
  private
56
61
 
57
62
  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.4.9
4
+ version: 0.4.10
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-01-25 00:00:00.000000000 Z
12
+ date: 2024-04-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: anthropic
@@ -157,6 +157,7 @@ files:
157
157
  - lib/boxcars/conversation_prompt.rb
158
158
  - lib/boxcars/engine.rb
159
159
  - lib/boxcars/engine/anthropic.rb
160
+ - lib/boxcars/engine/cohere.rb
160
161
  - lib/boxcars/engine/engine_result.rb
161
162
  - lib/boxcars/engine/gpt4all_eng.rb
162
163
  - lib/boxcars/engine/openai.rb