boxcars 0.6.3 → 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: 8c7137b49fcea80018efc8f6dc19361fa02b74a575f7e71218784986b366c350
4
- data.tar.gz: 9b2bba4216ab4f50024d158cc7b18977569fa7b9bada1a4734fb24f7b9e4a3be
3
+ metadata.gz: c7288e4aaea4065096105c84b78e9425330847eba37b55b6479466c55ae840a8
4
+ data.tar.gz: 6ff9ec0d0c69113dc70360750fa8fe5460236401c75a3b4e681009efad733301
5
5
  SHA512:
6
- metadata.gz: 18b58fc68b3837e2f8c7ca1e77e1daf49655843903d39a355233ff1d8c7a68caa885530ba08bcfc3842189b070c547ce152e13ee1a1b5cdb4cdc0424fe8e8ddc
7
- data.tar.gz: e6240effad988b8cb819070ea37f54eab03dd99b32eff0caa07aa167e0f854492c12843d781e9614f429ffd3a311f426bc58c9569af22f7424bd9b65f60244fa
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,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- boxcars (0.6.3)
4
+ boxcars (0.6.4)
5
5
  anthropic (~> 0.1)
6
6
  google_search_results (~> 2.2)
7
7
  gpt4all (~> 0.0.4)
@@ -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)
@@ -71,8 +71,8 @@ module Boxcars
71
71
  prompt = Prompt.new(template: question)
72
72
  response = client(prompt: prompt, **kwargs)
73
73
  raise Error, "Groq: No response from API" unless response
74
- raise Error, "Groq: #{response['error']}" if response["error"]
75
74
 
75
+ check_response(response)
76
76
  answer = response["choices"].map { |c| c.dig("message", "content") || c["text"] }.join("\n").strip
77
77
  puts answer
78
78
  answer
@@ -83,31 +83,16 @@ module Boxcars
83
83
  groq_parmas
84
84
  end
85
85
 
86
- # Get generation informaton
87
- # @param sub_choices [Array<Hash>] The choices to get generation info for.
88
- # @return [Array<Generation>] The generation information.
89
- def generation_info(sub_choices)
90
- sub_choices.map do |choice|
91
- Generation.new(
92
- text: choice.dig("message", "content") || choice["text"],
93
- generation_info: {
94
- finish_reason: choice.fetch("finish_reason", nil),
95
- logprobs: choice.fetch("logprobs", nil)
96
- }
97
- )
98
- end
99
- end
100
-
101
86
  # make sure we got a valid response
102
87
  # @param response [Hash] The response to check.
103
88
  # @param must_haves [Array<String>] The keys that must be in the response. Defaults to %w[choices].
104
89
  # @raise [KeyError] if there is an issue with the access token.
105
90
  # @raise [ValueError] if the response is not valid.
106
91
  def check_response(response, must_haves: %w[choices])
107
- if response['error']
92
+ if response['error'].is_a?(Hash)
108
93
  code = response.dig('error', 'code')
109
94
  msg = response.dig('error', 'message') || 'unknown error'
110
- raise KeyError, "OPENAI_ACCESS_TOKEN not valid" if code == 'invalid_api_key'
95
+ raise KeyError, "GROQ_API_TOKEN not valid" if code == 'invalid_api_key'
111
96
 
112
97
  raise ValueError, "Groq error: #{msg}"
113
98
  end
@@ -117,58 +102,20 @@ module Boxcars
117
102
  end
118
103
  end
119
104
 
120
- # Call out to Groq's endpoint with k unique prompts.
121
- # @param prompts [Array<String>] The prompts to pass into the model.
122
- # @param inputs [Array<String>] The inputs to subsitite into the prompt.
123
- # @param stop [Array<String>] Optional list of stop words to use when generating.
124
- # @return [EngineResult] The full engine output.
125
- def generate(prompts:, stop: nil)
126
- params = {}
127
- params[:stop] = stop if stop
128
- choices = []
129
- token_usage = {}
130
- # Get the token usage from the response.
131
- # Includes prompt, completion, and total tokens used.
132
- inkeys = %w[completion_tokens prompt_tokens total_tokens].freeze
133
- prompts.each_slice(batch_size) do |sub_prompts|
134
- sub_prompts.each do |sprompts, inputs|
135
- response = client(prompt: sprompts, inputs: inputs, **params)
136
- check_response(response)
137
- choices.concat(response["choices"])
138
- usage_keys = inkeys & response["usage"].keys
139
- usage_keys.each { |key| token_usage[key] = token_usage[key].to_i + response["usage"][key] }
140
- end
141
- end
142
-
143
- n = params.fetch(:n, 1)
144
- generations = []
145
- prompts.each_with_index do |_prompt, i|
146
- sub_choices = choices[i * n, (i + 1) * n]
147
- generations.push(generation_info(sub_choices))
148
- end
149
- EngineResult.new(generations: generations, engine_output: { token_usage: token_usage })
105
+ # the engine type
106
+ def engine_type
107
+ "groq"
150
108
  end
151
- # rubocop:enable Metrics/AbcSize
152
- end
153
-
154
- # the engine type
155
- def engine_type
156
- "groq"
157
- end
158
109
 
159
- # calculate the number of tokens used
160
- def get_num_tokens(text:)
161
- text.split.length # TODO: hook up to token counting gem
162
- end
163
-
164
- # Calculate the maximum number of tokens possible to generate for a prompt.
165
- # @param prompt_text [String] The prompt text to use.
166
- # @return [Integer] the number of tokens possible to generate.
167
- def max_tokens_for_prompt(prompt_text)
168
- num_tokens = get_num_tokens(prompt_text)
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)
169
115
 
170
- # get max context size for model by name
171
- max_size = 8096
172
- max_size - num_tokens
116
+ # get max context size for model by name
117
+ max_size = 8096
118
+ max_size - num_tokens
119
+ end
173
120
  end
174
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,6 +16,59 @@ 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
 
@@ -23,6 +76,7 @@ require "boxcars/engine/engine_result"
23
76
  require "boxcars/engine/anthropic"
24
77
  require "boxcars/engine/cohere"
25
78
  require "boxcars/engine/groq"
79
+ require "boxcars/engine/ollama"
26
80
  require "boxcars/engine/openai"
27
81
  require "boxcars/engine/perplexityai"
28
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.3"
5
+ VERSION = "0.6.4"
6
6
  end
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.3
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-26 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
@@ -161,6 +161,7 @@ files:
161
161
  - lib/boxcars/engine/engine_result.rb
162
162
  - lib/boxcars/engine/gpt4all_eng.rb
163
163
  - lib/boxcars/engine/groq.rb
164
+ - lib/boxcars/engine/ollama.rb
164
165
  - lib/boxcars/engine/openai.rb
165
166
  - lib/boxcars/engine/perplexityai.rb
166
167
  - lib/boxcars/generation.rb
@@ -218,7 +219,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
218
219
  - !ruby/object:Gem::Version
219
220
  version: '0'
220
221
  requirements: []
221
- rubygems_version: 3.4.10
222
+ rubygems_version: 3.5.11
222
223
  signing_key:
223
224
  specification_version: 4
224
225
  summary: Boxcars is a gem that enables you to create new systems with AI composability.