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.
- checksums.yaml +4 -4
- data/.rubocop.yml +6 -3
- data/.ruby-version +1 -1
- data/CHANGELOG.md +41 -0
- data/Gemfile +3 -13
- data/Gemfile.lock +29 -25
- data/POSTHOG_TEST_README.md +118 -0
- data/README.md +305 -0
- data/boxcars.gemspec +1 -2
- data/lib/boxcars/boxcar/active_record.rb +9 -10
- data/lib/boxcars/boxcar/calculator.rb +2 -2
- data/lib/boxcars/boxcar/engine_boxcar.rb +4 -4
- data/lib/boxcars/boxcar/google_search.rb +2 -2
- data/lib/boxcars/boxcar/json_engine_boxcar.rb +1 -1
- data/lib/boxcars/boxcar/ruby_calculator.rb +1 -1
- data/lib/boxcars/boxcar/sql_base.rb +4 -4
- data/lib/boxcars/boxcar/swagger.rb +3 -3
- data/lib/boxcars/boxcar/vector_answer.rb +3 -3
- data/lib/boxcars/boxcar/xml_engine_boxcar.rb +1 -1
- data/lib/boxcars/boxcar.rb +6 -6
- data/lib/boxcars/conversation_prompt.rb +3 -3
- data/lib/boxcars/engine/anthropic.rb +121 -23
- data/lib/boxcars/engine/cerebras.rb +2 -2
- data/lib/boxcars/engine/cohere.rb +135 -9
- data/lib/boxcars/engine/gemini_ai.rb +151 -76
- data/lib/boxcars/engine/google.rb +2 -2
- data/lib/boxcars/engine/gpt4all_eng.rb +92 -34
- data/lib/boxcars/engine/groq.rb +124 -73
- data/lib/boxcars/engine/intelligence_base.rb +52 -17
- data/lib/boxcars/engine/ollama.rb +127 -47
- data/lib/boxcars/engine/openai.rb +186 -103
- data/lib/boxcars/engine/perplexityai.rb +116 -136
- data/lib/boxcars/engine/together.rb +2 -2
- data/lib/boxcars/engine/unified_observability.rb +430 -0
- data/lib/boxcars/engine.rb +4 -3
- data/lib/boxcars/engines.rb +74 -0
- data/lib/boxcars/observability.rb +44 -0
- data/lib/boxcars/observability_backend.rb +17 -0
- data/lib/boxcars/observability_backends/multi_backend.rb +42 -0
- data/lib/boxcars/observability_backends/posthog_backend.rb +89 -0
- data/lib/boxcars/observation.rb +8 -8
- data/lib/boxcars/prompt.rb +16 -4
- data/lib/boxcars/result.rb +7 -12
- data/lib/boxcars/ruby_repl.rb +1 -1
- data/lib/boxcars/train/train_action.rb +1 -1
- data/lib/boxcars/train/xml_train.rb +3 -3
- data/lib/boxcars/train/xml_zero_shot.rb +1 -1
- data/lib/boxcars/train/zero_shot.rb +3 -3
- data/lib/boxcars/train.rb +1 -1
- data/lib/boxcars/vector_search.rb +5 -5
- data/lib/boxcars/vector_store/pgvector/build_from_array.rb +116 -88
- data/lib/boxcars/vector_store/pgvector/build_from_files.rb +106 -80
- data/lib/boxcars/vector_store/pgvector/save_to_database.rb +148 -122
- data/lib/boxcars/vector_store/pgvector/search.rb +157 -131
- data/lib/boxcars/vector_store.rb +4 -4
- data/lib/boxcars/version.rb +1 -1
- data/lib/boxcars.rb +31 -20
- metadata +11 -21
@@ -1,160 +1,186 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
require '
|
5
|
-
|
6
|
-
|
7
|
-
module
|
8
|
-
module
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
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
|
-
|
56
|
+
private
|
56
57
|
|
57
|
-
|
58
|
-
|
58
|
+
attr_reader :vector_documents, :vector_store, :db_connection,
|
59
|
+
:table_name, :embedding_column_name, :content_column_name
|
59
60
|
|
60
|
-
|
61
|
-
|
61
|
+
def validate_params(params)
|
62
|
+
@vector_documents = params[:vector_documents]
|
62
63
|
|
63
|
-
|
64
|
-
|
65
|
-
|
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
|
-
|
68
|
-
|
69
|
-
|
68
|
+
@vector_store = vector_documents[:vector_store]
|
69
|
+
@vector_store
|
70
|
+
end
|
70
71
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
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
|
-
|
80
|
-
|
80
|
+
vector_store[:database_url]
|
81
|
+
end
|
81
82
|
|
82
|
-
|
83
|
-
|
83
|
+
def test_db(db_url)
|
84
|
+
conn = ::PG::Connection.new(db_url)
|
84
85
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
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
|
-
|
91
|
-
|
92
|
-
|
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
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
95
|
+
conn
|
96
|
+
rescue PG::Error, PG::UndefinedTable, NameError => e
|
97
|
+
raise_argument_error(e.message)
|
98
|
+
end
|
98
99
|
|
99
|
-
|
100
|
-
|
100
|
+
def check_db_connection(conn)
|
101
|
+
return if conn.status == PG::CONNECTION_OK
|
101
102
|
|
102
|
-
|
103
|
-
|
103
|
+
raise_argument_error("PostgreSQL connection is not ok")
|
104
|
+
end
|
104
105
|
|
105
|
-
|
106
|
-
|
106
|
+
def check_vector_extension(conn)
|
107
|
+
return if conn.exec("SELECT 1 FROM pg_extension WHERE extname = 'vector'").any?
|
107
108
|
|
108
|
-
|
109
|
-
|
109
|
+
raise_argument_error("PostgreSQL 'vector' extension is not installed")
|
110
|
+
end
|
110
111
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
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
|
-
|
118
|
-
|
118
|
+
raise_argument_error("Table '#{table_name}' does not exist")
|
119
|
+
end
|
119
120
|
|
120
|
-
|
121
|
-
|
122
|
-
|
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
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
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
|
-
|
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
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
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
|
data/lib/boxcars/vector_store.rb
CHANGED
@@ -6,8 +6,8 @@ module Boxcars
|
|
6
6
|
module ClassMethods
|
7
7
|
VectorStoreError = Class.new(StandardError)
|
8
8
|
|
9
|
-
def call(
|
10
|
-
new(
|
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
|
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:
|
45
|
+
@openai_client ||= Openai.open_ai_client(openai_access_token:)
|
46
46
|
end
|
47
47
|
|
48
48
|
def raise_argument_error(message)
|
data/lib/boxcars/version.rb
CHANGED
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, **
|
159
|
-
msg = colorize(msg.to_s, 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, **
|
170
|
-
msg = colorize(msg.to_s, 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, **
|
181
|
-
msg = colorize(msg.to_s, 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, **
|
192
|
-
msg = colorize(msg.to_s, 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
|
-
|
215
|
-
|
216
|
-
|
217
|
-
require
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
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.
|
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:
|
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:
|
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.
|
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.
|