boxcars 0.6.2 → 0.6.4

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: dda5f801d5c9725c2b73f4cc0d69667f243c90684c7a55d45ef91cd0440378cb
4
- data.tar.gz: 3d486a0fbc40e4e393a7dd1a646e4c896df842a548c77ab23cb81ec7f6a669b9
3
+ metadata.gz: c7288e4aaea4065096105c84b78e9425330847eba37b55b6479466c55ae840a8
4
+ data.tar.gz: 6ff9ec0d0c69113dc70360750fa8fe5460236401c75a3b4e681009efad733301
5
5
  SHA512:
6
- metadata.gz: f462c9cee5a487fe7f896a0265edcf210cf920633dce4199312557cf5cae2671974c5daf26f70555bb188da377ebc44cef1b3503f2bc56a19f08df151371744a
7
- data.tar.gz: e0997b7e0c21d92299fba883781090d25d93566ec8eb4b23b86acdc0d132256e6ece8bc0286b31dec9656d10d9da028d60247b5fa668d506adf5011a961f4a24
6
+ metadata.gz: e48e4debf178dca7647a08357eaabbdd8ceef6edc3ae40d49a25a31b8ee21fbc41ded409fd55c2b840762868546f1edb0ada72729178c15953e6f48cc0d832fe
7
+ data.tar.gz: 3717955095a68207dd7c0ee914132573d84126c01ea4e53cec0d5a46985f512a59da3fb1b78ededac29c0d72d16e5f70301629ab326aa20ec2feb4b6664ae343
data/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## [v0.6.3](https://github.com/BoxcarsAI/boxcars/tree/v0.6.3) (2024-07-26)
4
+
5
+ [Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.6.2...v0.6.3)
6
+
7
+ **Merged pull requests:**
8
+
9
+ - Add Groq engine [\#199](https://github.com/BoxcarsAI/boxcars/pull/199) ([francis](https://github.com/francis))
10
+
11
+ ## [v0.6.2](https://github.com/BoxcarsAI/boxcars/tree/v0.6.2) (2024-07-24)
12
+
13
+ [Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.6.1...v0.6.2)
14
+
15
+ **Merged pull requests:**
16
+
17
+ - add flag for symbolizing JSON Engine Boxcar results [\#198](https://github.com/BoxcarsAI/boxcars/pull/198) ([francis](https://github.com/francis))
18
+
3
19
  ## [v0.6.1](https://github.com/BoxcarsAI/boxcars/tree/v0.6.1) (2024-07-19)
4
20
 
5
21
  [Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.5.1...v0.6.1)
data/Gemfile.lock CHANGED
@@ -1,14 +1,14 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- boxcars (0.6.2)
4
+ boxcars (0.6.4)
5
5
  anthropic (~> 0.1)
6
6
  google_search_results (~> 2.2)
7
7
  gpt4all (~> 0.0.4)
8
8
  hnswlib (~> 0.8)
9
9
  nokogiri (~> 1.16)
10
10
  pgvector (~> 0.2)
11
- ruby-openai (>= 4.2, < 8.0)
11
+ ruby-openai (>= 7.1, < 8.0)
12
12
 
13
13
  GEM
14
14
  remote: https://rubygems.org/
@@ -81,7 +81,7 @@ GEM
81
81
  faraday (>= 0.8)
82
82
  faraday-multipart (1.0.4)
83
83
  multipart-post (~> 2)
84
- faraday-net_http (3.1.0)
84
+ faraday-net_http (3.1.1)
85
85
  net-http
86
86
  faraday-retry (2.2.1)
87
87
  faraday (~> 2.0)
data/boxcars.gemspec CHANGED
@@ -37,7 +37,7 @@ Gem::Specification.new do |spec|
37
37
  spec.add_dependency "hnswlib", "~> 0.8"
38
38
  spec.add_dependency "nokogiri", "~> 1.16"
39
39
  spec.add_dependency "pgvector", "~> 0.2"
40
- spec.add_dependency "ruby-openai", ">= 4.2", "< 8.0"
40
+ spec.add_dependency "ruby-openai", ">= 7.1", "< 8.0"
41
41
 
42
42
  # For more information and examples about making a new gem, checkout our
43
43
  # guide at: https://bundler.io/guides/creating_gem.html
@@ -90,21 +90,6 @@ module Boxcars
90
90
  llm_params
91
91
  end
92
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
93
  # make sure we got a valid response
109
94
  # @param response [Hash] The response to check.
110
95
  # @param must_haves [Array<String>] The keys that must be in the response. Defaults to %w[choices].
@@ -124,45 +109,11 @@ module Boxcars
124
109
  end
125
110
  end
126
111
 
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
112
  # the engine type
157
113
  def engine_type
158
114
  "claude"
159
115
  end
160
116
 
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
117
  # lookup the context size for a model by name
167
118
  # @param modelname [String] The name of the model to lookup.
168
119
  def modelname_to_contextsize(_modelname)
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Boxcars is a framework for running a series of tools to get an answer to a question.
4
+ module Boxcars
5
+ # A engine that uses Groq's API.
6
+ class Groq < Engine
7
+ attr_reader :prompts, :groq_parmas, :model_kwargs, :batch_size
8
+
9
+ # The default parameters to use when asking the engine.
10
+ DEFAULT_PARAMS = {
11
+ model: "llama3-70b-8192",
12
+ temperature: 0.1,
13
+ max_tokens: 4096
14
+ }.freeze
15
+
16
+ # the default name of the engine
17
+ DEFAULT_NAME = "Groq engine"
18
+ # the default description of the engine
19
+ DEFAULT_DESCRIPTION = "useful for when you need to use AI to answer questions. " \
20
+ "You should ask targeted questions"
21
+
22
+ # A engine is a container for a single tool to run.
23
+ # @param name [String] The name of the engine. Defaults to "Groq engine".
24
+ # @param description [String] A description of the engine. Defaults to:
25
+ # useful for when you need to use AI to answer questions. You should ask targeted questions".
26
+ # @param prompts [Array<String>] The prompts to use when asking the engine. Defaults to [].
27
+ # @param batch_size [Integer] The number of prompts to send to the engine at once. Defaults to 20.
28
+ def initialize(name: DEFAULT_NAME, description: DEFAULT_DESCRIPTION, prompts: [], batch_size: 20, **kwargs)
29
+ @groq_parmas = DEFAULT_PARAMS.merge(kwargs)
30
+ @prompts = prompts
31
+ @batch_size = batch_size
32
+ super(description: description, name: name)
33
+ end
34
+
35
+ # Get the OpenAI API client
36
+ # @param groq_api_key [String] The access token to use when asking the engine.
37
+ # Defaults to Boxcars.configuration.groq_api_key
38
+ # @return [OpenAI::Client] The OpenAI API gem client.
39
+ def self.open_ai_client(groq_api_key: nil)
40
+ access_token = Boxcars.configuration.groq_api_key(groq_api_key: groq_api_key)
41
+ ::OpenAI::Client.new(access_token: access_token, uri_base: "https://api.groq.com/openai")
42
+ end
43
+
44
+ def conversation_model?(_model)
45
+ true
46
+ end
47
+
48
+ # Get an answer from the engine.
49
+ # @param prompt [String] The prompt to use when asking the engine.
50
+ # @param groq_api_key [String] The access token to use when asking the engine.
51
+ # Defaults to Boxcars.configuration.groq_api_key.
52
+ # @param kwargs [Hash] Additional parameters to pass to the engine if wanted.
53
+ def client(prompt:, inputs: {}, groq_api_key: nil, **kwargs)
54
+ clnt = Groq.open_ai_client(groq_api_key: groq_api_key)
55
+ params = groq_parmas.merge(kwargs)
56
+ prompt = prompt.first if prompt.is_a?(Array)
57
+ params = prompt.as_messages(inputs).merge(params)
58
+ if Boxcars.configuration.log_prompts
59
+ Boxcars.debug(params[:messages].last(2).map { |p| ">>>>>> Role: #{p[:role]} <<<<<<\n#{p[:content]}" }.join("\n"), :cyan)
60
+ end
61
+ clnt.chat(parameters: params)
62
+ rescue => e
63
+ Boxcars.error(e, :red)
64
+ raise
65
+ end
66
+
67
+ # get an answer from the engine for a question.
68
+ # @param question [String] The question to ask the engine.
69
+ # @param kwargs [Hash] Additional parameters to pass to the engine if wanted.
70
+ def run(question, **kwargs)
71
+ prompt = Prompt.new(template: question)
72
+ response = client(prompt: prompt, **kwargs)
73
+ raise Error, "Groq: No response from API" unless response
74
+
75
+ check_response(response)
76
+ answer = response["choices"].map { |c| c.dig("message", "content") || c["text"] }.join("\n").strip
77
+ puts answer
78
+ answer
79
+ end
80
+
81
+ # Get the default parameters for the engine.
82
+ def default_params
83
+ groq_parmas
84
+ end
85
+
86
+ # make sure we got a valid response
87
+ # @param response [Hash] The response to check.
88
+ # @param must_haves [Array<String>] The keys that must be in the response. Defaults to %w[choices].
89
+ # @raise [KeyError] if there is an issue with the access token.
90
+ # @raise [ValueError] if the response is not valid.
91
+ def check_response(response, must_haves: %w[choices])
92
+ if response['error'].is_a?(Hash)
93
+ code = response.dig('error', 'code')
94
+ msg = response.dig('error', 'message') || 'unknown error'
95
+ raise KeyError, "GROQ_API_TOKEN not valid" if code == 'invalid_api_key'
96
+
97
+ raise ValueError, "Groq error: #{msg}"
98
+ end
99
+
100
+ must_haves.each do |key|
101
+ raise ValueError, "Expecting key #{key} in response" unless response.key?(key)
102
+ end
103
+ end
104
+
105
+ # the engine type
106
+ def engine_type
107
+ "groq"
108
+ end
109
+
110
+ # Calculate the maximum number of tokens possible to generate for a prompt.
111
+ # @param prompt_text [String] The prompt text to use.
112
+ # @return [Integer] the number of tokens possible to generate.
113
+ def max_tokens_for_prompt(prompt_text)
114
+ num_tokens = get_num_tokens(prompt_text)
115
+
116
+ # get max context size for model by name
117
+ max_size = 8096
118
+ max_size - num_tokens
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Boxcars is a framework for running a series of tools to get an answer to a question.
4
+ module Boxcars
5
+ # A engine that uses local GPT4All API.
6
+ class Ollama < Engine
7
+ attr_reader :prompts, :model_kwargs, :batch_size, :ollama_params
8
+
9
+ # The default parameters to use when asking the engine.
10
+ DEFAULT_PARAMS = {
11
+ model: "llama3",
12
+ temperature: 0.1,
13
+ max_tokens: 4096
14
+ }.freeze
15
+
16
+ # the default name of the engine
17
+ DEFAULT_NAME = "Ollama engine"
18
+ # the default description of the engine
19
+ DEFAULT_DESCRIPTION = "useful for when you need to use local AI to answer questions. " \
20
+ "You should ask targeted questions"
21
+
22
+ # A engine is a container for a single tool to run.
23
+ # @param name [String] The name of the engine. Defaults to "OpenAI engine".
24
+ # @param description [String] A description of the engine. Defaults to:
25
+ # useful for when you need to use AI to answer questions. You should ask targeted questions".
26
+ # @param prompts [Array<String>] The prompts to use when asking the engine. Defaults to [].
27
+ # @param batch_size [Integer] The number of prompts to send to the engine at once. Defaults to 2.
28
+ def initialize(name: DEFAULT_NAME, description: DEFAULT_DESCRIPTION, prompts: [], batch_size: 2, **kwargs)
29
+ @ollama_params = DEFAULT_PARAMS.merge(kwargs)
30
+ @prompts = prompts
31
+ @batch_size = batch_size
32
+ super(description: description, name: name)
33
+ end
34
+
35
+ # Get the OpenAI API client
36
+ # @param groq_api_key [String] The access token to use when asking the engine.
37
+ # Defaults to Boxcars.configuration.groq_api_key
38
+ # @return [OpenAI::Client] The OpenAI API gem client.
39
+ def self.open_ai_client
40
+ ::OpenAI::Client.new(uri_base: "http://localhost:11434")
41
+ end
42
+
43
+ def conversation_model?(_model)
44
+ true
45
+ end
46
+
47
+ # Get an answer from the engine.
48
+ # @param prompt [String] The prompt to use when asking the engine.
49
+ # @param groq_api_key [String] The access token to use when asking the engine.
50
+ # Defaults to Boxcars.configuration.groq_api_key.
51
+ # @param kwargs [Hash] Additional parameters to pass to the engine if wanted.
52
+ def client(prompt:, inputs: {}, **kwargs)
53
+ clnt = Ollama.open_ai_client
54
+ params = ollama_params.merge(kwargs)
55
+ prompt = prompt.first if prompt.is_a?(Array)
56
+ params = prompt.as_messages(inputs).merge(params)
57
+ if Boxcars.configuration.log_prompts
58
+ Boxcars.debug(params[:messages].last(2).map { |p| ">>>>>> Role: #{p[:role]} <<<<<<\n#{p[:content]}" }.join("\n"), :cyan)
59
+ end
60
+ ans = clnt.chat(parameters: params)
61
+ ans['choices'].pluck('message').pluck('content').join("\n")
62
+ rescue => e
63
+ Boxcars.error(e, :red)
64
+ raise
65
+ end
66
+
67
+ # get an answer from the engine for a question.
68
+ # @param question [String] The question to ask the engine.
69
+ # @param kwargs [Hash] Additional parameters to pass to the engine if wanted.
70
+ def run(question, **kwargs)
71
+ prompt = Prompt.new(template: question)
72
+ answer = client(prompt: prompt, **kwargs)
73
+ raise Error, "Ollama: No response from API" unless answer
74
+
75
+ # raise Error, "Ollama: #{response['error']}" if response["error"]
76
+ Boxcars.debug("Answer: #{answer}", :cyan)
77
+ answer
78
+ end
79
+ end
80
+ end
@@ -88,21 +88,6 @@ module Boxcars
88
88
  open_ai_params
89
89
  end
90
90
 
91
- # Get generation informaton
92
- # @param sub_choices [Array<Hash>] The choices to get generation info for.
93
- # @return [Array<Generation>] The generation information.
94
- def generation_info(sub_choices)
95
- sub_choices.map do |choice|
96
- Generation.new(
97
- text: choice.dig("message", "content") || choice["text"],
98
- generation_info: {
99
- finish_reason: choice.fetch("finish_reason", nil),
100
- logprobs: choice.fetch("logprobs", nil)
101
- }
102
- )
103
- end
104
- end
105
-
106
91
  # make sure we got a valid response
107
92
  # @param response [Hash] The response to check.
108
93
  # @param must_haves [Array<String>] The keys that must be in the response. Defaults to %w[choices].
@@ -121,39 +106,6 @@ module Boxcars
121
106
  raise ValueError, "Expecting key #{key} in response" unless response.key?(key)
122
107
  end
123
108
  end
124
-
125
- # Call out to OpenAI's endpoint with k unique prompts.
126
- # @param prompts [Array<String>] The prompts to pass into the model.
127
- # @param inputs [Array<String>] The inputs to subsitite into the prompt.
128
- # @param stop [Array<String>] Optional list of stop words to use when generating.
129
- # @return [EngineResult] The full engine output.
130
- def generate(prompts:, stop: nil)
131
- params = {}
132
- params[:stop] = stop if stop
133
- choices = []
134
- token_usage = {}
135
- # Get the token usage from the response.
136
- # Includes prompt, completion, and total tokens used.
137
- inkeys = %w[completion_tokens prompt_tokens total_tokens].freeze
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.concat(response["choices"])
143
- usage_keys = inkeys & response["usage"].keys
144
- usage_keys.each { |key| token_usage[key] = token_usage[key].to_i + response["usage"][key] }
145
- end
146
- end
147
-
148
- n = params.fetch(:n, 1)
149
- generations = []
150
- prompts.each_with_index do |_prompt, i|
151
- sub_choices = choices[i * n, (i + 1) * n]
152
- generations.push(generation_info(sub_choices))
153
- end
154
- EngineResult.new(generations: generations, engine_output: { token_usage: token_usage })
155
- end
156
- # rubocop:enable Metrics/AbcSize
157
109
  end
158
110
 
159
111
  # the engine type
@@ -161,11 +113,6 @@ module Boxcars
161
113
  "openai"
162
114
  end
163
115
 
164
- # calculate the number of tokens used
165
- def get_num_tokens(text:)
166
- text.split.length # TODO: hook up to token counting gem
167
- end
168
-
169
116
  # lookup the context size for a model by name
170
117
  # @param modelname [String] The name of the model to lookup.
171
118
  def modelname_to_contextsize(modelname)
@@ -8,9 +8,8 @@ module Boxcars
8
8
 
9
9
  # The default parameters to use when asking the engine.
10
10
  DEFAULT_PER_PARAMS = {
11
- model: "llama-2-70b-chat",
12
- temperature: 0.1,
13
- max_tokens: 3200
11
+ model: "'llama-3-sonar-large-32k-online'",
12
+ temperature: 0.1
14
13
  }.freeze
15
14
 
16
15
  # the default name of the engine
@@ -32,28 +31,26 @@ module Boxcars
32
31
  super(description: description, name: name)
33
32
  end
34
33
 
35
- def conversation_model?(model)
36
- ["mistral-7b-instruct", "llama-2-13b-chat", "llama-2-70b-chat", "openhermes-2-mistral-7b"].include?(model)
34
+ def conversation_model?(_model)
35
+ true
37
36
  end
38
37
 
39
38
  def chat(parameters:)
40
- url = URI("https://api.perplexity.ai/chat/completions")
41
-
42
- http = Net::HTTP.new(url.host, url.port)
43
- http.use_ssl = true
44
-
45
- request = Net::HTTP::Post.new(url)
46
- request["accept"] = 'application/json'
47
- request["authorization"] = "Bearer #{ENV.fetch('PERPLEXITY_API_KEY')}"
48
- request["content-type"] = 'application/json'
49
- the_body = {
50
- model: parameters[:model] || "mistral-7b-instruct",
51
- messages: parameters[:messages]
52
- }
53
- request.body = the_body.to_json
54
-
55
- response = http.request(request)
56
- JSON.parse(response.read_body)
39
+ conn = Faraday.new(url: "https://api.perplexity.ai/chat/completions") do |faraday|
40
+ faraday.request :json
41
+ faraday.response :json
42
+ faraday.response :raise_error
43
+ end
44
+
45
+ response = conn.post do |req|
46
+ req.headers['Authorization'] = "Bearer #{ENV.fetch('PERPLEXITY_API_KEY')}"
47
+ req.body = {
48
+ model: parameters[:model],
49
+ messages: parameters[:messages]
50
+ }
51
+ end
52
+
53
+ response.body
57
54
  end
58
55
 
59
56
  # Get an answer from the engine.
@@ -64,7 +61,6 @@ module Boxcars
64
61
  def client(prompt:, inputs: {}, **kwargs)
65
62
  prompt = prompt.first if prompt.is_a?(Array)
66
63
  params = prompt.as_messages(inputs).merge(default_params).merge(kwargs)
67
- params[:model] ||= "llama-2-70b-chat"
68
64
  if Boxcars.configuration.log_prompts
69
65
  Boxcars.debug(params[:messages].last(2).map { |p| ">>>>>> Role: #{p[:role]} <<<<<<\n#{p[:content]}" }.join("\n"), :cyan)
70
66
  end
@@ -90,21 +86,6 @@ module Boxcars
90
86
  perplexity_params
91
87
  end
92
88
 
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.dig("message", "content") || choice["text"],
100
- generation_info: {
101
- finish_reason: choice.fetch("finish_reason", nil),
102
- logprobs: choice.fetch("logprobs", nil)
103
- }
104
- )
105
- end
106
- end
107
-
108
89
  # make sure we got a valid response
109
90
  # @param response [Hash] The response to check.
110
91
  # @param must_haves [Array<String>] The keys that must be in the response. Defaults to %w[choices].
@@ -123,39 +104,6 @@ module Boxcars
123
104
  raise ValueError, "Expecting key #{key} in response" unless response.key?(key)
124
105
  end
125
106
  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
- token_usage = {}
137
- # Get the token usage from the response.
138
- # Includes prompt, completion, and total tokens used.
139
- inkeys = %w[completion_tokens prompt_tokens total_tokens].freeze
140
- prompts.each_slice(batch_size) do |sub_prompts|
141
- sub_prompts.each do |sprompts, inputs|
142
- response = client(prompt: sprompts, inputs: inputs, **params)
143
- check_response(response)
144
- choices.concat(response["choices"])
145
- usage_keys = inkeys & response["usage"].keys
146
- usage_keys.each { |key| token_usage[key] = token_usage[key].to_i + response["usage"][key] }
147
- end
148
- end
149
-
150
- n = params.fetch(:n, 1)
151
- generations = []
152
- prompts.each_with_index do |_prompt, i|
153
- sub_choices = choices[i * n, (i + 1) * n]
154
- generations.push(generation_info(sub_choices))
155
- end
156
- EngineResult.new(generations: generations, engine_output: { token_usage: token_usage })
157
- end
158
- # rubocop:enable Metrics/AbcSize
159
107
  end
160
108
 
161
109
  # the engine type
@@ -168,29 +116,10 @@ module Boxcars
168
116
  text.split.length # TODO: hook up to token counting gem
169
117
  end
170
118
 
171
- # lookup the context size for a model by name
172
- # @param modelname [String] The name of the model to lookup.
173
- def modelname_to_contextsize(modelname)
174
- model_lookup = {
175
- 'text-davinci-003': 4097,
176
- 'text-curie-001': 2048,
177
- 'text-babbage-001': 2048,
178
- 'text-ada-001': 2048,
179
- 'code-davinci-002': 8000,
180
- 'code-cushman-001': 2048,
181
- 'gpt-3.5-turbo-1': 4096
182
- }.freeze
183
- model_lookup[modelname] || 4097
184
- end
185
-
186
119
  # Calculate the maximum number of tokens possible to generate for a prompt.
187
120
  # @param prompt_text [String] The prompt text to use.
188
121
  # @return [Integer] the number of tokens possible to generate.
189
- def max_tokens_for_prompt(prompt_text)
190
- num_tokens = get_num_tokens(prompt_text)
191
-
192
- # get max context size for model by name
193
- max_size = modelname_to_contextsize(model_name)
194
- max_size - num_tokens
122
+ def max_tokens_for_prompt(_prompt_text)
123
+ 8096
195
124
  end
196
125
  end
@@ -16,12 +16,67 @@ module Boxcars
16
16
  def run(question)
17
17
  raise NotImplementedError
18
18
  end
19
+
20
+ # calculate the number of tokens used
21
+ def get_num_tokens(text:)
22
+ text.split.length # TODO: hook up to token counting gem
23
+ end
24
+
25
+ # Get generation informaton
26
+ # @param sub_choices [Array<Hash>] The choices to get generation info for.
27
+ # @return [Array<Generation>] The generation information.
28
+ def generation_info(sub_choices)
29
+ sub_choices.map do |choice|
30
+ Generation.new(
31
+ text: choice.dig("message", "content") || choice["text"],
32
+ generation_info: {
33
+ finish_reason: choice.fetch("finish_reason", nil),
34
+ logprobs: choice.fetch("logprobs", nil)
35
+ }
36
+ )
37
+ end
38
+ end
39
+
40
+ # Call out to OpenAI's endpoint with k unique prompts.
41
+ # @param prompts [Array<String>] The prompts to pass into the model.
42
+ # @param inputs [Array<String>] The inputs to subsitite into the prompt.
43
+ # @param stop [Array<String>] Optional list of stop words to use when generating.
44
+ # @return [EngineResult] The full engine output.
45
+ def generate(prompts:, stop: nil)
46
+ params = {}
47
+ params[:stop] = stop if stop
48
+ choices = []
49
+ token_usage = {}
50
+ # Get the token usage from the response.
51
+ # Includes prompt, completion, and total tokens used.
52
+ inkeys = %w[completion_tokens prompt_tokens total_tokens].freeze
53
+ prompts.each_slice(batch_size) do |sub_prompts|
54
+ sub_prompts.each do |sprompts, inputs|
55
+ response = client(prompt: sprompts, inputs: inputs, **params)
56
+ check_response(response)
57
+ choices.concat(response["choices"])
58
+ usage_keys = inkeys & response["usage"].keys
59
+ usage_keys.each { |key| token_usage[key] = token_usage[key].to_i + response["usage"][key] }
60
+ end
61
+ end
62
+
63
+ n = params.fetch(:n, 1)
64
+ generations = []
65
+ prompts.each_with_index do |_prompt, i|
66
+ sub_choices = choices[i * n, (i + 1) * n]
67
+ generations.push(generation_info(sub_choices))
68
+ end
69
+ EngineResult.new(generations: generations, engine_output: { token_usage: token_usage })
70
+ end
71
+ # rubocop:enable Metrics/AbcSize
19
72
  end
20
73
  end
21
74
 
22
75
  require "boxcars/engine/engine_result"
23
76
  require "boxcars/engine/anthropic"
24
77
  require "boxcars/engine/cohere"
78
+ require "boxcars/engine/groq"
79
+ require "boxcars/engine/ollama"
25
80
  require "boxcars/engine/openai"
26
81
  require "boxcars/engine/perplexityai"
27
82
  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.6.2"
5
+ VERSION = "0.6.4"
6
6
  end
data/lib/boxcars.rb CHANGED
@@ -27,7 +27,7 @@ module Boxcars
27
27
 
28
28
  # Configuration contains gem settings
29
29
  class Configuration
30
- attr_writer :openai_access_token, :serpapi_api_key
30
+ attr_writer :openai_access_token, :serpapi_api_key, :groq_api_key
31
31
  attr_accessor :organization_id, :logger, :log_prompts, :log_generated, :default_train, :default_engine
32
32
 
33
33
  def initialize
@@ -52,11 +52,16 @@ 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.
55
+ # @return [String] The Cohere API key either from arg or env.
56
56
  def cohere_api_key(**kwargs)
57
57
  key_lookup(:cohere_api_key, kwargs)
58
58
  end
59
59
 
60
+ # @return [String] The Groq API key either from arg or env.
61
+ def groq_api_key(**kwargs)
62
+ key_lookup(:groq_api_key, kwargs)
63
+ end
64
+
60
65
  private
61
66
 
62
67
  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.2
4
+ version: 0.6.4
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-07-24 00:00:00.000000000 Z
12
+ date: 2024-07-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: anthropic
@@ -101,7 +101,7 @@ dependencies:
101
101
  requirements:
102
102
  - - ">="
103
103
  - !ruby/object:Gem::Version
104
- version: '4.2'
104
+ version: '7.1'
105
105
  - - "<"
106
106
  - !ruby/object:Gem::Version
107
107
  version: '8.0'
@@ -111,7 +111,7 @@ dependencies:
111
111
  requirements:
112
112
  - - ">="
113
113
  - !ruby/object:Gem::Version
114
- version: '4.2'
114
+ version: '7.1'
115
115
  - - "<"
116
116
  - !ruby/object:Gem::Version
117
117
  version: '8.0'
@@ -160,6 +160,8 @@ files:
160
160
  - lib/boxcars/engine/cohere.rb
161
161
  - lib/boxcars/engine/engine_result.rb
162
162
  - lib/boxcars/engine/gpt4all_eng.rb
163
+ - lib/boxcars/engine/groq.rb
164
+ - lib/boxcars/engine/ollama.rb
163
165
  - lib/boxcars/engine/openai.rb
164
166
  - lib/boxcars/engine/perplexityai.rb
165
167
  - lib/boxcars/generation.rb
@@ -217,7 +219,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
217
219
  - !ruby/object:Gem::Version
218
220
  version: '0'
219
221
  requirements: []
220
- rubygems_version: 3.4.10
222
+ rubygems_version: 3.5.11
221
223
  signing_key:
222
224
  specification_version: 4
223
225
  summary: Boxcars is a gem that enables you to create new systems with AI composability.