boxcars 0.7.6 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +6 -3
  3. data/.ruby-version +1 -1
  4. data/CHANGELOG.md +41 -0
  5. data/Gemfile +3 -13
  6. data/Gemfile.lock +29 -25
  7. data/POSTHOG_TEST_README.md +118 -0
  8. data/README.md +305 -0
  9. data/boxcars.gemspec +1 -2
  10. data/lib/boxcars/boxcar/active_record.rb +9 -10
  11. data/lib/boxcars/boxcar/calculator.rb +2 -2
  12. data/lib/boxcars/boxcar/engine_boxcar.rb +4 -4
  13. data/lib/boxcars/boxcar/google_search.rb +2 -2
  14. data/lib/boxcars/boxcar/json_engine_boxcar.rb +1 -1
  15. data/lib/boxcars/boxcar/ruby_calculator.rb +1 -1
  16. data/lib/boxcars/boxcar/sql_base.rb +4 -4
  17. data/lib/boxcars/boxcar/swagger.rb +3 -3
  18. data/lib/boxcars/boxcar/vector_answer.rb +3 -3
  19. data/lib/boxcars/boxcar/xml_engine_boxcar.rb +1 -1
  20. data/lib/boxcars/boxcar.rb +6 -6
  21. data/lib/boxcars/conversation_prompt.rb +3 -3
  22. data/lib/boxcars/engine/anthropic.rb +121 -23
  23. data/lib/boxcars/engine/cerebras.rb +2 -2
  24. data/lib/boxcars/engine/cohere.rb +135 -9
  25. data/lib/boxcars/engine/gemini_ai.rb +151 -76
  26. data/lib/boxcars/engine/google.rb +2 -2
  27. data/lib/boxcars/engine/gpt4all_eng.rb +92 -34
  28. data/lib/boxcars/engine/groq.rb +124 -73
  29. data/lib/boxcars/engine/intelligence_base.rb +52 -17
  30. data/lib/boxcars/engine/ollama.rb +127 -47
  31. data/lib/boxcars/engine/openai.rb +186 -103
  32. data/lib/boxcars/engine/perplexityai.rb +116 -136
  33. data/lib/boxcars/engine/together.rb +2 -2
  34. data/lib/boxcars/engine/unified_observability.rb +430 -0
  35. data/lib/boxcars/engine.rb +4 -3
  36. data/lib/boxcars/engines.rb +74 -0
  37. data/lib/boxcars/observability.rb +44 -0
  38. data/lib/boxcars/observability_backend.rb +17 -0
  39. data/lib/boxcars/observability_backends/multi_backend.rb +42 -0
  40. data/lib/boxcars/observability_backends/posthog_backend.rb +89 -0
  41. data/lib/boxcars/observation.rb +8 -8
  42. data/lib/boxcars/prompt.rb +16 -4
  43. data/lib/boxcars/result.rb +7 -12
  44. data/lib/boxcars/ruby_repl.rb +1 -1
  45. data/lib/boxcars/train/train_action.rb +1 -1
  46. data/lib/boxcars/train/xml_train.rb +3 -3
  47. data/lib/boxcars/train/xml_zero_shot.rb +1 -1
  48. data/lib/boxcars/train/zero_shot.rb +3 -3
  49. data/lib/boxcars/train.rb +1 -1
  50. data/lib/boxcars/vector_search.rb +5 -5
  51. data/lib/boxcars/vector_store/pgvector/build_from_array.rb +116 -88
  52. data/lib/boxcars/vector_store/pgvector/build_from_files.rb +106 -80
  53. data/lib/boxcars/vector_store/pgvector/save_to_database.rb +148 -122
  54. data/lib/boxcars/vector_store/pgvector/search.rb +157 -131
  55. data/lib/boxcars/vector_store.rb +4 -4
  56. data/lib/boxcars/version.rb +1 -1
  57. data/lib/boxcars.rb +31 -20
  58. metadata +11 -21
@@ -1,160 +1,186 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'pg'
4
- require 'json'
5
-
6
- module Boxcars
7
- module VectorStore
8
- module Pgvector
9
- class Search
10
- include VectorStore
11
-
12
- # initialize the vector store with the following parameters:
13
- # @param params [Hash] A Hash containing the initial configuration.
14
- # @option params [Hash] :vector_documents The vector documents to search.
15
- # example:
16
- # {
17
- # type: :pgvector,
18
- # vector_store: {
19
- # table_name: "vector_store",
20
- # embedding_column_name: "embedding",
21
- # content_column_name: "content",
22
- # database_url: ENV['DATABASE_URL']
23
- # }
24
- # }
25
- #
26
- # @option params [Hash] :vector_store The vector store to search.
27
- def initialize(params)
28
- vector_store = validate_params(params)
29
- db_url = validate_vector_store(vector_store)
30
- @db_connection = test_db(db_url)
31
-
32
- @vector_documents = params[:vector_documents]
33
- end
3
+ if Gem.loaded_specs.key?('pgvector') && Gem.loaded_specs.key?('pg')
4
+ require 'pg'
5
+ require 'json'
6
+
7
+ module Boxcars
8
+ module VectorStore
9
+ module Pgvector
10
+ class Search
11
+ include VectorStore
12
+
13
+ # initialize the vector store with the following parameters:
14
+ # @param params [Hash] A Hash containing the initial configuration.
15
+ # @option params [Hash] :vector_documents The vector documents to search.
16
+ # example:
17
+ # {
18
+ # type: :pgvector,
19
+ # vector_store: {
20
+ # table_name: "vector_store",
21
+ # embedding_column_name: "embedding",
22
+ # content_column_name: "content",
23
+ # database_url: ENV['DATABASE_URL']
24
+ # }
25
+ # }
26
+ #
27
+ # @option params [Hash] :vector_store The vector store to search.
28
+ def initialize(params)
29
+ vector_store = validate_params(params)
30
+ db_url = validate_vector_store(vector_store)
31
+ @db_connection = test_db(db_url)
32
+
33
+ @vector_documents = params[:vector_documents]
34
+ end
34
35
 
35
- # @param query_vector [Array] The query vector to search for.
36
- # @param count [Integer] The number of results to return.
37
- # @return [Array] array of hashes with :document and :distance keys
38
- # @example
39
- # [
40
- # {
41
- # document: Boxcars::VectorStore::Document.new(
42
- # content: "hello",
43
- # embedding: [0.1, 0.2, 0.3],
44
- # metadata: { a: 1 }
45
- # ),
46
- # distance: 0.1
47
- # }
48
- # ]
49
- def call(query_vector:, count: 1)
50
- raise ::Boxcars::ArgumentError, 'query_vector is empty' if query_vector.empty?
51
-
52
- search(query_vector, count)
53
- end
36
+ # @param query_vector [Array] The query vector to search for.
37
+ # @param count [Integer] The number of results to return.
38
+ # @return [Array] array of hashes with :document and :distance keys
39
+ # @example
40
+ # [
41
+ # {
42
+ # document: Boxcars::VectorStore::Document.new(
43
+ # content: "hello",
44
+ # embedding: [0.1, 0.2, 0.3],
45
+ # metadata: { a: 1 }
46
+ # ),
47
+ # distance: 0.1
48
+ # }
49
+ # ]
50
+ def call(query_vector:, count: 1)
51
+ raise ::Boxcars::ArgumentError, 'query_vector is empty' if query_vector.empty?
52
+
53
+ search(query_vector, count)
54
+ end
54
55
 
55
- private
56
+ private
56
57
 
57
- attr_reader :vector_documents, :vector_store, :db_connection,
58
- :table_name, :embedding_column_name, :content_column_name
58
+ attr_reader :vector_documents, :vector_store, :db_connection,
59
+ :table_name, :embedding_column_name, :content_column_name
59
60
 
60
- def validate_params(params)
61
- @vector_documents = params[:vector_documents]
61
+ def validate_params(params)
62
+ @vector_documents = params[:vector_documents]
62
63
 
63
- raise_argument_error('vector_documents is nil') unless vector_documents
64
- raise_arugment_error('vector_documents must be a hash') unless vector_documents.is_a?(Hash)
65
- raise_arugment_error('type must be pgvector') unless vector_documents[:type] == :pgvector
64
+ raise_argument_error('vector_documents is nil') unless vector_documents
65
+ raise_arugment_error('vector_documents must be a hash') unless vector_documents.is_a?(Hash)
66
+ raise_arugment_error('type must be pgvector') unless vector_documents[:type] == :pgvector
66
67
 
67
- @vector_store = vector_documents[:vector_store]
68
- @vector_store
69
- end
68
+ @vector_store = vector_documents[:vector_store]
69
+ @vector_store
70
+ end
70
71
 
71
- def validate_vector_store(vector_store)
72
- raise_arugment_error('vector_store is nil') unless vector_store
73
- raise_arugment_error('vector_store must be a hash') unless vector_store.is_a?(Hash)
74
- raise_arugment_error('vector_store must have a table_name') unless vector_store[:table_name]
75
- raise_arugment_error('vector_store must have a embedding_column_name') unless vector_store[:embedding_column_name]
76
- raise_arugment_error('vector_store must have a content_column_name') unless vector_store[:content_column_name]
77
- raise_argument_error('missing DATABASE_URL') unless vector_store[:database_url]
72
+ def validate_vector_store(vector_store)
73
+ raise_arugment_error('vector_store is nil') unless vector_store
74
+ raise_arugment_error('vector_store must be a hash') unless vector_store.is_a?(Hash)
75
+ raise_arugment_error('vector_store must have a table_name') unless vector_store[:table_name]
76
+ raise_arugment_error('vector_store must have a embedding_column_name') unless vector_store[:embedding_column_name]
77
+ raise_arugment_error('vector_store must have a content_column_name') unless vector_store[:content_column_name]
78
+ raise_argument_error('missing DATABASE_URL') unless vector_store[:database_url]
78
79
 
79
- vector_store[:database_url]
80
- end
80
+ vector_store[:database_url]
81
+ end
81
82
 
82
- def test_db(db_url)
83
- conn = ::PG::Connection.new(db_url)
83
+ def test_db(db_url)
84
+ conn = ::PG::Connection.new(db_url)
84
85
 
85
- check_db_connection(conn)
86
- check_vector_extension(conn)
87
- check_table_exists(conn, vector_store[:table_name])
88
- check_column_exists(conn)
86
+ check_db_connection(conn)
87
+ check_vector_extension(conn)
88
+ check_table_exists(conn, vector_store[:table_name])
89
+ check_column_exists(conn)
89
90
 
90
- @table_name = vector_store[:table_name]
91
- @embedding_column_name = vector_store[:embedding_column_name]
92
- @content_column_name = vector_store[:content_column_name]
91
+ @table_name = vector_store[:table_name]
92
+ @embedding_column_name = vector_store[:embedding_column_name]
93
+ @content_column_name = vector_store[:content_column_name]
93
94
 
94
- conn
95
- rescue PG::Error, PG::UndefinedTable, NameError => e
96
- raise_argument_error(e.message)
97
- end
95
+ conn
96
+ rescue PG::Error, PG::UndefinedTable, NameError => e
97
+ raise_argument_error(e.message)
98
+ end
98
99
 
99
- def check_db_connection(conn)
100
- return if conn.status == PG::CONNECTION_OK
100
+ def check_db_connection(conn)
101
+ return if conn.status == PG::CONNECTION_OK
101
102
 
102
- raise_argument_error("PostgreSQL connection is not ok")
103
- end
103
+ raise_argument_error("PostgreSQL connection is not ok")
104
+ end
104
105
 
105
- def check_vector_extension(conn)
106
- return if conn.exec("SELECT 1 FROM pg_extension WHERE extname = 'vector'").any?
106
+ def check_vector_extension(conn)
107
+ return if conn.exec("SELECT 1 FROM pg_extension WHERE extname = 'vector'").any?
107
108
 
108
- raise_argument_error("PostgreSQL 'vector' extension is not installed")
109
- end
109
+ raise_argument_error("PostgreSQL 'vector' extension is not installed")
110
+ end
110
111
 
111
- def check_table_exists(conn, table_name)
112
- table_exists = conn.exec_params(
113
- "SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = $1)", [table_name]
114
- ).getvalue(0, 0) == "t"
115
- return if table_exists
112
+ def check_table_exists(conn, table_name)
113
+ table_exists = conn.exec_params(
114
+ "SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = $1)", [table_name]
115
+ ).getvalue(0, 0) == "t"
116
+ return if table_exists
116
117
 
117
- raise_argument_error("Table '#{table_name}' does not exist")
118
- end
118
+ raise_argument_error("Table '#{table_name}' does not exist")
119
+ end
119
120
 
120
- def check_column_exists(conn)
121
- column_names = %i[embedding_column_name content_column_name]
122
- table_name = vector_store[:table_name]
121
+ def check_column_exists(conn)
122
+ column_names = %i[embedding_column_name content_column_name]
123
+ table_name = vector_store[:table_name]
123
124
 
124
- column_names.each do |target|
125
- column_name = vector_store[target]
126
- column_exists = conn.exec_params(
127
- "SELECT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = $1 AND column_name = $2)",
128
- [table_name, column_name]
129
- ).getvalue(0, 0) == "t"
130
- next if column_exists
125
+ column_names.each do |target|
126
+ column_name = vector_store[target]
127
+ column_exists = conn.exec_params(
128
+ "SELECT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = $1 AND column_name = $2)",
129
+ [table_name, column_name]
130
+ ).getvalue(0, 0) == "t"
131
+ next if column_exists
131
132
 
132
- raise_argument_error("Column '#{column_name}' does not exist in table '#{table_name}'")
133
+ raise_argument_error("Column '#{column_name}' does not exist in table '#{table_name}'")
134
+ end
135
+ end
136
+
137
+ def search(query_vector, num_neighbors)
138
+ sql = <<-SQL
139
+ SELECT *, #{embedding_column_name} <-> $1 AS distance FROM #{table_name}
140
+ ORDER BY #{embedding_column_name} <-> $1
141
+ LIMIT #{num_neighbors}
142
+ SQL
143
+ result = db_connection.exec_params(sql, [query_vector.to_s]).to_a
144
+ return [] if result.empty?
145
+
146
+ result.map { |hash| hash.transform_keys(&:to_sym) }
147
+ .map do |item|
148
+ {
149
+ document: Boxcars::VectorStore::Document.new(
150
+ content: item[:content],
151
+ embedding: JSON.parse(item[:embedding]),
152
+ metadata: JSON.parse(item[:metadata], symbolize_names: true)
153
+ ),
154
+ distance: item[:distance].to_f
155
+ }
156
+ end
157
+ rescue StandardError => e
158
+ raise_argument_error("Error searching for #{query_vector}: #{e.message}")
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
164
+ else
165
+ # Define placeholder modules/classes that raise an error if pgvector is not available
166
+ module Boxcars
167
+ module VectorStore
168
+ module Pgvector
169
+ class PgvectorNotAvailableError < StandardError
170
+ DEFAULT_MESSAGE = "The 'pgvector' and 'pg' gems are required. Please add them to your Gemfile."
171
+ def initialize(message = DEFAULT_MESSAGE)
172
+ super
133
173
  end
134
174
  end
135
175
 
136
- def search(query_vector, num_neighbors)
137
- sql = <<-SQL
138
- SELECT *, #{embedding_column_name} <-> $1 AS distance FROM #{table_name}
139
- ORDER BY #{embedding_column_name} <-> $1
140
- LIMIT #{num_neighbors}
141
- SQL
142
- result = db_connection.exec_params(sql, [query_vector.to_s]).to_a
143
- return [] if result.empty?
144
-
145
- result.map { |hash| hash.transform_keys(&:to_sym) }
146
- .map do |item|
147
- {
148
- document: Boxcars::VectorStore::Document.new(
149
- content: item[:content],
150
- embedding: JSON.parse(item[:embedding]),
151
- metadata: JSON.parse(item[:metadata], symbolize_names: true)
152
- ),
153
- distance: item[:distance].to_f
154
- }
155
- end
156
- rescue StandardError => e
157
- raise_argument_error("Error searching for #{query_vector}: #{e.message}")
176
+ class Search
177
+ def initialize(*_args)
178
+ raise PgvectorNotAvailableError
179
+ end
180
+
181
+ def call(*_args)
182
+ raise PgvectorNotAvailableError
183
+ end
158
184
  end
159
185
  end
160
186
  end
@@ -6,8 +6,8 @@ module Boxcars
6
6
  module ClassMethods
7
7
  VectorStoreError = Class.new(StandardError)
8
8
 
9
- def call(*args, **kw_args)
10
- new(*args, **kw_args).call
9
+ def call(*, **kw_args)
10
+ new(*, **kw_args).call
11
11
  end
12
12
  end
13
13
 
@@ -24,7 +24,7 @@ module Boxcars
24
24
 
25
25
  embeddings_method[:klass]
26
26
  .call(
27
- texts: texts, client: embeddings_method[:client]
27
+ texts:, client: embeddings_method[:client]
28
28
  )
29
29
  .map { |item| item.transform_keys(&:to_sym) }
30
30
  end
@@ -42,7 +42,7 @@ module Boxcars
42
42
  # @param openai_access_token [String] the OpenAI access token
43
43
  # @return [OpenAI::Client]
44
44
  def openai_client(openai_access_token: nil)
45
- @openai_client ||= Openai.open_ai_client(openai_access_token: openai_access_token)
45
+ @openai_client ||= Openai.open_ai_client(openai_access_token:)
46
46
  end
47
47
 
48
48
  def raise_argument_error(message)
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Boxcars
4
4
  # The current version of the gem.
5
- VERSION = "0.7.6"
5
+ VERSION = "0.8.0"
6
6
  end
data/lib/boxcars.rb CHANGED
@@ -27,8 +27,9 @@ module Boxcars
27
27
 
28
28
  # Configuration contains gem settings
29
29
  class Configuration
30
- attr_writer :openai_access_token, :serpapi_api_key, :groq_api_key, :cerebras_api_key
31
- attr_accessor :organization_id, :logger, :log_prompts, :log_generated, :default_train, :default_engine
30
+ attr_writer :openai_access_token, :serpapi_api_key, :groq_api_key, :cerebras_api_key, :perplexity_api_key
31
+ attr_accessor :organization_id, :logger, :log_prompts, :log_generated, :default_train, :default_engine, :default_model,
32
+ :observability_backend
32
33
 
33
34
  def initialize
34
35
  @organization_id = nil
@@ -77,6 +78,11 @@ module Boxcars
77
78
  key_lookup(:together_api_key, kwargs)
78
79
  end
79
80
 
81
+ # @return [String] The Perplexity API key either from arg or env.
82
+ def perplexity_api_key(**kwargs)
83
+ key_lookup(:perplexity_api_key, kwargs)
84
+ end
85
+
80
86
  private
81
87
 
82
88
  def check_key(key, val)
@@ -155,8 +161,8 @@ module Boxcars
155
161
 
156
162
  # Logging system
157
163
  # debug log
158
- def self.debug(msg, color = nil, **options)
159
- msg = colorize(msg.to_s, color, **options) if color
164
+ def self.debug(msg, color = nil, **)
165
+ msg = colorize(msg.to_s, color, **) if color
160
166
  log << msg
161
167
  if logger
162
168
  logger.debug(msg)
@@ -166,8 +172,8 @@ module Boxcars
166
172
  end
167
173
 
168
174
  # info log
169
- def self.info(msg, color = nil, **options)
170
- msg = colorize(msg.to_s, color, **options) if color
175
+ def self.info(msg, color = nil, **)
176
+ msg = colorize(msg.to_s, color, **) if color
171
177
  log << msg
172
178
  if logger
173
179
  logger.info(msg)
@@ -177,8 +183,8 @@ module Boxcars
177
183
  end
178
184
 
179
185
  # warn log
180
- def self.warn(msg, color = nil, **options)
181
- msg = colorize(msg.to_s, color, **options) if color
186
+ def self.warn(msg, color = nil, **)
187
+ msg = colorize(msg.to_s, color, **) if color
182
188
  log << msg
183
189
  if logger
184
190
  logger.warn(msg)
@@ -188,8 +194,8 @@ module Boxcars
188
194
  end
189
195
 
190
196
  # error log
191
- def self.error(msg, color = nil, **options)
192
- msg = colorize(msg.to_s, color, **options) if color
197
+ def self.error(msg, color = nil, **)
198
+ msg = colorize(msg.to_s, color, **) if color
193
199
  log << msg
194
200
  if logger
195
201
  logger.error(msg)
@@ -211,13 +217,18 @@ module Boxcars
211
217
  end
212
218
  end
213
219
 
214
- require "boxcars/version"
215
- require "boxcars/x_node"
216
- require "boxcars/prompt"
217
- require "boxcars/conversation_prompt"
218
- require "boxcars/conversation"
219
- require "boxcars/generation"
220
- require "boxcars/ruby_repl"
221
- require "boxcars/engine"
222
- require "boxcars/boxcar"
223
- require "boxcars/train"
220
+ require_relative "boxcars/version"
221
+ require_relative "boxcars/observability_backend"
222
+ require_relative "boxcars/observability"
223
+ # If users want it, they can require 'boxcars/observability_backends/multi_backend'
224
+ require_relative "boxcars/x_node"
225
+ require_relative "boxcars/prompt"
226
+ require_relative "boxcars/conversation_prompt"
227
+ require_relative "boxcars/conversation"
228
+ require_relative "boxcars/generation"
229
+ require_relative "boxcars/ruby_repl"
230
+ require_relative "boxcars/result"
231
+ require_relative "boxcars/engine"
232
+ require_relative "boxcars/boxcar"
233
+ require_relative "boxcars/train"
234
+ require_relative "boxcars/engines"
metadata CHANGED
@@ -1,15 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: boxcars
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.6
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Francis Sullivan
8
8
  - Tabrez Syed
9
- autorequire:
10
9
  bindir: exe
11
10
  cert_chain: []
12
- date: 2025-04-22 00:00:00.000000000 Z
11
+ date: 1980-01-02 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: google_search_results
@@ -81,20 +80,6 @@ dependencies:
81
80
  - - "~>"
82
81
  - !ruby/object:Gem::Version
83
82
  version: '1.18'
84
- - !ruby/object:Gem::Dependency
85
- name: pgvector
86
- requirement: !ruby/object:Gem::Requirement
87
- requirements:
88
- - - "~>"
89
- - !ruby/object:Gem::Version
90
- version: '0.2'
91
- type: :runtime
92
- prerelease: false
93
- version_requirements: !ruby/object:Gem::Requirement
94
- requirements:
95
- - - "~>"
96
- - !ruby/object:Gem::Version
97
- version: '0.2'
98
83
  - !ruby/object:Gem::Dependency
99
84
  name: ruby-anthropic
100
85
  requirement: !ruby/object:Gem::Requirement
@@ -140,6 +125,7 @@ files:
140
125
  - Gemfile
141
126
  - Gemfile.lock
142
127
  - LICENSE.txt
128
+ - POSTHOG_TEST_README.md
143
129
  - README.md
144
130
  - Rakefile
145
131
  - bin/console
@@ -177,7 +163,13 @@ files:
177
163
  - lib/boxcars/engine/openai.rb
178
164
  - lib/boxcars/engine/perplexityai.rb
179
165
  - lib/boxcars/engine/together.rb
166
+ - lib/boxcars/engine/unified_observability.rb
167
+ - lib/boxcars/engines.rb
180
168
  - lib/boxcars/generation.rb
169
+ - lib/boxcars/observability.rb
170
+ - lib/boxcars/observability_backend.rb
171
+ - lib/boxcars/observability_backends/multi_backend.rb
172
+ - lib/boxcars/observability_backends/posthog_backend.rb
181
173
  - lib/boxcars/observation.rb
182
174
  - lib/boxcars/prompt.rb
183
175
  - lib/boxcars/result.rb
@@ -216,7 +208,6 @@ metadata:
216
208
  source_code_uri: https://github.com/BoxcarsAI/boxcars
217
209
  changelog_uri: https://github.com/BoxcarsAI/boxcars/blob/main/CHANGELOG.md
218
210
  rubygems_mfa_required: 'true'
219
- post_install_message:
220
211
  rdoc_options: []
221
212
  require_paths:
222
213
  - lib
@@ -224,15 +215,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
224
215
  requirements:
225
216
  - - ">="
226
217
  - !ruby/object:Gem::Version
227
- version: '3.0'
218
+ version: 3.2.0
228
219
  required_rubygems_version: !ruby/object:Gem::Requirement
229
220
  requirements:
230
221
  - - ">="
231
222
  - !ruby/object:Gem::Version
232
223
  version: '0'
233
224
  requirements: []
234
- rubygems_version: 3.5.16
235
- signing_key:
225
+ rubygems_version: 3.6.7
236
226
  specification_version: 4
237
227
  summary: Boxcars is a gem that enables you to create new systems with AI composability.
238
228
  Inspired by python langchain.