boxcars 0.6.2 → 0.6.3

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: 8c7137b49fcea80018efc8f6dc19361fa02b74a575f7e71218784986b366c350
4
+ data.tar.gz: 9b2bba4216ab4f50024d158cc7b18977569fa7b9bada1a4734fb24f7b9e4a3be
5
5
  SHA512:
6
- metadata.gz: f462c9cee5a487fe7f896a0265edcf210cf920633dce4199312557cf5cae2671974c5daf26f70555bb188da377ebc44cef1b3503f2bc56a19f08df151371744a
7
- data.tar.gz: e0997b7e0c21d92299fba883781090d25d93566ec8eb4b23b86acdc0d132256e6ece8bc0286b31dec9656d10d9da028d60247b5fa668d506adf5011a961f4a24
6
+ metadata.gz: 18b58fc68b3837e2f8c7ca1e77e1daf49655843903d39a355233ff1d8c7a68caa885530ba08bcfc3842189b070c547ce152e13ee1a1b5cdb4cdc0424fe8e8ddc
7
+ data.tar.gz: e6240effad988b8cb819070ea37f54eab03dd99b32eff0caa07aa167e0f854492c12843d781e9614f429ffd3a311f426bc58c9569af22f7424bd9b65f60244fa
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.3)
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
@@ -0,0 +1,174 @@
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
+ raise Error, "Groq: #{response['error']}" if response["error"]
75
+
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
+ # 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
+ # make sure we got a valid response
102
+ # @param response [Hash] The response to check.
103
+ # @param must_haves [Array<String>] The keys that must be in the response. Defaults to %w[choices].
104
+ # @raise [KeyError] if there is an issue with the access token.
105
+ # @raise [ValueError] if the response is not valid.
106
+ def check_response(response, must_haves: %w[choices])
107
+ if response['error']
108
+ code = response.dig('error', 'code')
109
+ msg = response.dig('error', 'message') || 'unknown error'
110
+ raise KeyError, "OPENAI_ACCESS_TOKEN not valid" if code == 'invalid_api_key'
111
+
112
+ raise ValueError, "Groq error: #{msg}"
113
+ end
114
+
115
+ must_haves.each do |key|
116
+ raise ValueError, "Expecting key #{key} in response" unless response.key?(key)
117
+ end
118
+ end
119
+
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 })
150
+ end
151
+ # rubocop:enable Metrics/AbcSize
152
+ end
153
+
154
+ # the engine type
155
+ def engine_type
156
+ "groq"
157
+ end
158
+
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)
169
+
170
+ # get max context size for model by name
171
+ max_size = 8096
172
+ max_size - num_tokens
173
+ end
174
+ end
@@ -22,6 +22,7 @@ end
22
22
  require "boxcars/engine/engine_result"
23
23
  require "boxcars/engine/anthropic"
24
24
  require "boxcars/engine/cohere"
25
+ require "boxcars/engine/groq"
25
26
  require "boxcars/engine/openai"
26
27
  require "boxcars/engine/perplexityai"
27
28
  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.3"
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.3
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-26 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,7 @@ 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
163
164
  - lib/boxcars/engine/openai.rb
164
165
  - lib/boxcars/engine/perplexityai.rb
165
166
  - lib/boxcars/generation.rb