boxcars 0.10.14 → 1.0.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 +1 -1
- data/.ruby-version +1 -1
- data/CHANGELOG.md +23 -0
- data/CLAUDE.md +2 -2
- data/Gemfile.lock +21 -22
- data/README.md +2 -0
- data/UPGRADING.md +16 -20
- data/boxcars.gemspec +3 -2
- data/docs/boxcars_guide.md +16 -32
- data/lib/boxcars/boxcar/active_record.rb +2 -4
- data/lib/boxcars/engine/anthropic.rb +17 -9
- data/lib/boxcars/engine/openai.rb +1 -2
- data/lib/boxcars/engine/perplexityai.rb +1 -2
- data/lib/boxcars/engine/unified_observability.rb +1 -2
- data/lib/boxcars/engines.rb +14 -82
- data/lib/boxcars/prompt.rb +1 -2
- data/lib/boxcars/version.rb +1 -1
- data/lib/boxcars/x_node.rb +2 -4
- data/lib/boxcars.rb +1 -2
- metadata +18 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 06c8b47287c93e2a1f56dbf8b282b41759017e89ce75b4da3b1549fc1fd91bc7
|
|
4
|
+
data.tar.gz: ee8353dceaf1ad652d056c452a89fb39a37f79973ba553204df0ddecc865dec3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3d0cc047e675833cf40a0cee54e8021c049a6e0157218d76eea525d6d09180724e1390c384ebde247cea9fb7a95b3ee2f869bd2e60c82100992ae54ef12539ef
|
|
7
|
+
data.tar.gz: 0e7d67e1039632f0cada81fbdcb5a1a15f58885a0b579b57bf76340dcb28743834f7f7907355c64c91a0de1d417fbf6eba1bd776cf1c8f77a93335d15412802c
|
data/.rubocop.yml
CHANGED
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
4.0.6
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [1.0.0] - 2026-08-28
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added Ruby 4.0 to the supported CI test matrix and made Ruby 4.0.6 the default development runtime.
|
|
10
|
+
- Added explicit engine-factory routing for the Cerebras `gpt-oss-120b` and Together AI `Qwen/Qwen2.5-VL-72B-Instruct` model IDs.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Raised the minimum supported Ruby version from 3.2 to 3.3, matching the current official OpenAI Ruby SDK.
|
|
15
|
+
- Updated the curated `sonnet` and `opus` aliases to Claude Sonnet 5 and Claude Opus 5.
|
|
16
|
+
- Claude Sonnet 5, Opus 5, and Fable 5 requests now omit unsupported `temperature`, `top_p`, and `top_k` parameters.
|
|
17
|
+
|
|
18
|
+
### Removed
|
|
19
|
+
|
|
20
|
+
- Removed the deprecated model aliases `anthropic`, `groq`, `deepseek`, `mistral`, `online`, `huge`, `online_huge`, `sonar-huge`, `sonar_huge`, `sonar_pro`, `flash`, `gemini-flash`, `gemini-pro`, `cerebras`, and `qwen`.
|
|
21
|
+
- Removed the obsolete strict-mode configuration used to detect deprecated model aliases. The curated `sonnet`, `opus`, `sonar`, and `sonar-pro` aliases remain supported.
|
|
22
|
+
|
|
23
|
+
### Maintenance
|
|
24
|
+
|
|
25
|
+
- Declared `logger` as a runtime dependency because Ruby 4 no longer bundles it as a default gem.
|
|
26
|
+
- Refreshed bundled dependencies for Ruby 3.3+ and Ruby 4 compatibility, including OpenAI, PostHog, RuboCop, SQLite, and WebMock, and updated the lockfile to Bundler 4.
|
|
27
|
+
|
|
5
28
|
## [0.10.14] - 2026-08-04
|
|
6
29
|
|
|
7
30
|
### Fixed
|
data/CLAUDE.md
CHANGED
|
@@ -39,7 +39,7 @@ Orchestrator that runs a series of boxcars using an engine. Three implementation
|
|
|
39
39
|
- **XMLTrain** (`lib/boxcars/train/xml_train.rb`) — XML-based structured outputs
|
|
40
40
|
|
|
41
41
|
### Engine Factory (`lib/boxcars/engines.rb`)
|
|
42
|
-
`Boxcars::Engines.engine(model:)` creates the right engine from a model name or alias. Default model: `"gemini-2.5-flash"`. Curated aliases
|
|
42
|
+
`Boxcars::Engines.engine(model:)` creates the right engine from a model name or alias. Default model: `"gemini-2.5-flash"`. Curated aliases are `"sonnet"`, `"opus"`, `"sonar"`, and `"sonar-pro"`; legacy aliases were removed in v1.0.
|
|
43
43
|
|
|
44
44
|
### Other Key Components
|
|
45
45
|
- **MCP** (`lib/boxcars/mcp/`) — Model Context Protocol integration for external tool servers
|
|
@@ -57,5 +57,5 @@ Orchestrator that runs a series of boxcars using an engine. Three implementation
|
|
|
57
57
|
|
|
58
58
|
- Rubocop with `rubocop-rspec` and `rubocop-rake` plugins
|
|
59
59
|
- Max line length: 130 (excluded for specs)
|
|
60
|
-
- Target Ruby: 3.
|
|
60
|
+
- Target Ruby: 3.3+
|
|
61
61
|
- Provider gems are optional — core has zero runtime dependencies; providers are loaded on demand via `OptionalDependency`
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
boxcars (0.
|
|
4
|
+
boxcars (1.0.0)
|
|
5
|
+
logger
|
|
5
6
|
|
|
6
7
|
GEM
|
|
7
8
|
remote: https://rubygems.org/
|
|
@@ -30,7 +31,6 @@ GEM
|
|
|
30
31
|
ast (2.4.3)
|
|
31
32
|
base64 (0.3.0)
|
|
32
33
|
bigdecimal (4.1.2)
|
|
33
|
-
cgi (0.5.2)
|
|
34
34
|
concurrent-ruby (1.3.8)
|
|
35
35
|
connection_pool (3.0.2)
|
|
36
36
|
crack (1.0.1)
|
|
@@ -57,7 +57,7 @@ GEM
|
|
|
57
57
|
hnswlib (0.9.3)
|
|
58
58
|
i18n (1.15.2)
|
|
59
59
|
concurrent-ruby (~> 1.0)
|
|
60
|
-
io-console (0.
|
|
60
|
+
io-console (0.9.2)
|
|
61
61
|
irb (1.18.0)
|
|
62
62
|
pp (>= 0.6.0)
|
|
63
63
|
prism (>= 1.3.0)
|
|
@@ -89,10 +89,9 @@ GEM
|
|
|
89
89
|
racc (~> 1.4)
|
|
90
90
|
nokogiri (1.19.4-x86_64-linux-musl)
|
|
91
91
|
racc (~> 1.4)
|
|
92
|
-
openai (0.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
connection_pool
|
|
92
|
+
openai (0.83.0)
|
|
93
|
+
connection_pool (>= 2.2.3)
|
|
94
|
+
logger
|
|
96
95
|
parallel (2.1.0)
|
|
97
96
|
parser (3.3.12.0)
|
|
98
97
|
ast (~> 2.4.1)
|
|
@@ -105,7 +104,7 @@ GEM
|
|
|
105
104
|
pg (1.6.3-x86_64-linux)
|
|
106
105
|
pg (1.6.3-x86_64-linux-musl)
|
|
107
106
|
pgvector (0.3.3)
|
|
108
|
-
posthog-ruby (3.
|
|
107
|
+
posthog-ruby (3.23.5)
|
|
109
108
|
concurrent-ruby (~> 1)
|
|
110
109
|
pp (0.6.4)
|
|
111
110
|
prettyprint
|
|
@@ -115,7 +114,7 @@ GEM
|
|
|
115
114
|
racc (1.8.1)
|
|
116
115
|
rainbow (3.1.1)
|
|
117
116
|
rake (13.4.2)
|
|
118
|
-
rbs (4.
|
|
117
|
+
rbs (4.2.0)
|
|
119
118
|
logger
|
|
120
119
|
prism (>= 1.6.0)
|
|
121
120
|
tsort
|
|
@@ -125,7 +124,7 @@ GEM
|
|
|
125
124
|
rbs (>= 4.0.0)
|
|
126
125
|
tsort
|
|
127
126
|
regexp_parser (2.12.0)
|
|
128
|
-
reline (0.
|
|
127
|
+
reline (0.7.0)
|
|
129
128
|
io-console (~> 0.5)
|
|
130
129
|
rexml (3.4.4)
|
|
131
130
|
rspec (3.13.2)
|
|
@@ -141,8 +140,8 @@ GEM
|
|
|
141
140
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
142
141
|
rspec-support (~> 3.13.0)
|
|
143
142
|
rspec-support (3.13.7)
|
|
144
|
-
rubocop (1.
|
|
145
|
-
json (
|
|
143
|
+
rubocop (1.90.0)
|
|
144
|
+
json (>= 2.3)
|
|
146
145
|
language_server-protocol (~> 3.17.0.2)
|
|
147
146
|
lint_roller (~> 1.1.0)
|
|
148
147
|
parallel (>= 1.10)
|
|
@@ -170,14 +169,14 @@ GEM
|
|
|
170
169
|
securerandom (0.4.1)
|
|
171
170
|
sequel (5.107.0)
|
|
172
171
|
bigdecimal
|
|
173
|
-
sqlite3 (2.9.
|
|
174
|
-
sqlite3 (2.9.
|
|
175
|
-
sqlite3 (2.9.
|
|
176
|
-
sqlite3 (2.9.
|
|
177
|
-
sqlite3 (2.9.
|
|
178
|
-
sqlite3 (2.9.
|
|
179
|
-
sqlite3 (2.9.
|
|
180
|
-
sqlite3 (2.9.
|
|
172
|
+
sqlite3 (2.9.6-aarch64-linux-gnu)
|
|
173
|
+
sqlite3 (2.9.6-aarch64-linux-musl)
|
|
174
|
+
sqlite3 (2.9.6-arm-linux-gnu)
|
|
175
|
+
sqlite3 (2.9.6-arm-linux-musl)
|
|
176
|
+
sqlite3 (2.9.6-arm64-darwin)
|
|
177
|
+
sqlite3 (2.9.6-x86_64-darwin)
|
|
178
|
+
sqlite3 (2.9.6-x86_64-linux-gnu)
|
|
179
|
+
sqlite3 (2.9.6-x86_64-linux-musl)
|
|
181
180
|
timeout (0.6.1)
|
|
182
181
|
tsort (0.2.0)
|
|
183
182
|
tzinfo (2.0.6)
|
|
@@ -187,7 +186,7 @@ GEM
|
|
|
187
186
|
unicode-emoji (4.2.0)
|
|
188
187
|
uri (1.1.1)
|
|
189
188
|
vcr (6.4.0)
|
|
190
|
-
webmock (3.26.
|
|
189
|
+
webmock (3.26.3)
|
|
191
190
|
addressable (>= 2.8.0)
|
|
192
191
|
crack (>= 0.3.2)
|
|
193
192
|
hashdiff (>= 0.4.0, < 2.0.0)
|
|
@@ -228,4 +227,4 @@ DEPENDENCIES
|
|
|
228
227
|
webmock (~> 3.26.1)
|
|
229
228
|
|
|
230
229
|
BUNDLED WITH
|
|
231
|
-
|
|
230
|
+
4.0.16
|
data/README.md
CHANGED
data/UPGRADING.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Upgrading Boxcars (v0.9 -> v1.0)
|
|
2
2
|
|
|
3
|
-
This guide covers the migration path
|
|
3
|
+
This guide covers the migration path from Boxcars v0.9 and v0.10 to v1.0.
|
|
4
4
|
|
|
5
5
|
## Summary
|
|
6
6
|
|
|
@@ -11,10 +11,15 @@ v0.9 introduces:
|
|
|
11
11
|
- JSON Schema support for `JSONEngineBoxcar`
|
|
12
12
|
- Deprecated model alias warnings with optional strict mode
|
|
13
13
|
|
|
14
|
-
v1.0
|
|
14
|
+
v1.0:
|
|
15
15
|
|
|
16
16
|
- Remove deprecated model aliases
|
|
17
17
|
- Prefer explicit model names (with a small curated alias set)
|
|
18
|
+
- Require Ruby 3.3 or newer, with Ruby 4 fully supported
|
|
19
|
+
|
|
20
|
+
## Ruby 3.3 or Newer Is Required
|
|
21
|
+
|
|
22
|
+
Boxcars 1.0 drops Ruby 3.2 support and requires Ruby 3.3 or newer. Upgrade your Ruby runtime before installing Boxcars 1.0. Applications that must remain on Ruby 3.2 should stay on Boxcars 0.10.x and OpenAI Ruby SDK 0.75.x, the final compatible release line.
|
|
18
23
|
|
|
19
24
|
## SQL Boxcars Now Default to Read-Only (v0.10.x)
|
|
20
25
|
|
|
@@ -115,7 +120,7 @@ result.answer
|
|
|
115
120
|
|
|
116
121
|
## 1. Model Alias Migration (Do This First)
|
|
117
122
|
|
|
118
|
-
|
|
123
|
+
The aliases below emitted one-time warnings in v0.9 and v0.10 and are removed in v1.0. Using one now raises `Boxcars::ArgumentError` with an `Unknown model` message.
|
|
119
124
|
|
|
120
125
|
### Kept curated aliases (not deprecated)
|
|
121
126
|
|
|
@@ -124,6 +129,10 @@ Deprecated aliases still work in v0.9, but emit one-time warnings.
|
|
|
124
129
|
- `sonnet`
|
|
125
130
|
- `opus`
|
|
126
131
|
|
|
132
|
+
In v1.0, `sonnet` targets `claude-sonnet-5` and `opus` targets `claude-opus-5`. These Claude 5 models do not accept
|
|
133
|
+
custom `temperature`, `top_p`, or `top_k` values, so Boxcars removes those options before sending the request. Use an
|
|
134
|
+
explicit older Claude model ID if your application depends on custom sampling values.
|
|
135
|
+
|
|
127
136
|
### Replace deprecated aliases
|
|
128
137
|
|
|
129
138
|
- `anthropic` -> `sonnet`
|
|
@@ -148,27 +157,14 @@ Prefer explicit model names in app code:
|
|
|
148
157
|
|
|
149
158
|
```ruby
|
|
150
159
|
Boxcars::Engines.engine(model: "gpt-4o")
|
|
151
|
-
Boxcars::Engines.engine(model: "claude-sonnet-
|
|
160
|
+
Boxcars::Engines.engine(model: "claude-sonnet-5")
|
|
152
161
|
Boxcars::Engines.engine(model: "gemini-2.5-flash")
|
|
153
162
|
Boxcars::Engines.engine(model: "sonar-pro")
|
|
154
163
|
```
|
|
155
164
|
|
|
156
|
-
## 2.
|
|
157
|
-
|
|
158
|
-
Use this to fail builds when deprecated aliases are used.
|
|
159
|
-
|
|
160
|
-
```ruby
|
|
161
|
-
# config/initializers/boxcars.rb
|
|
162
|
-
Boxcars.configure do |config|
|
|
163
|
-
config.strict_deprecated_model_aliases = ENV["CI"] == "true"
|
|
164
|
-
end
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
Or enforce globally in tests:
|
|
165
|
+
## 2. Verify Removed Aliases in CI
|
|
168
166
|
|
|
169
|
-
|
|
170
|
-
Boxcars::Engines.strict_deprecated_aliases = true
|
|
171
|
-
```
|
|
167
|
+
No strict-mode setting is needed in v1.0. Removed aliases fail during engine construction, so normal application tests catch any remaining uses.
|
|
172
168
|
|
|
173
169
|
## 3. Migrate ReAct/Text Trains to Native Tool Calling (Optional, Recommended)
|
|
174
170
|
|
|
@@ -260,7 +256,7 @@ boxcar = Boxcars::JSONEngineBoxcar.new(json_schema: schema, json_schema_strict:
|
|
|
260
256
|
3. Migrate one workflow from `ZeroShot` to `ToolTrain`.
|
|
261
257
|
4. Add MCP tools where they simplify app-specific integrations.
|
|
262
258
|
5. Add JSON Schema to `JSONEngineBoxcar` uses that need reliable structure.
|
|
263
|
-
6. Upgrade to v1.0 after
|
|
259
|
+
6. Upgrade to v1.0 after the application test suite passes with explicit models or curated aliases.
|
|
264
260
|
|
|
265
261
|
## 7. Known Ongoing Modernization Work
|
|
266
262
|
|
data/boxcars.gemspec
CHANGED
|
@@ -12,9 +12,8 @@ Gem::Specification.new do |spec|
|
|
|
12
12
|
spec.description = "You simply set an OpenAI key, give a number of Boxcars to a Train, and magic ensues when you run it."
|
|
13
13
|
spec.homepage = "https://github.com/BoxcarsAI/boxcars"
|
|
14
14
|
spec.license = "MIT"
|
|
15
|
-
spec.required_ruby_version = ">= 3.
|
|
15
|
+
spec.required_ruby_version = ">= 3.3.0"
|
|
16
16
|
|
|
17
|
-
spec.metadata["homepage_uri"] = spec.homepage
|
|
18
17
|
spec.metadata["source_code_uri"] = spec.homepage
|
|
19
18
|
spec.metadata["changelog_uri"] = "https://github.com/BoxcarsAI/boxcars/blob/main/CHANGELOG.md"
|
|
20
19
|
spec.metadata["rubygems_mfa_required"] = "true"
|
|
@@ -31,6 +30,8 @@ Gem::Specification.new do |spec|
|
|
|
31
30
|
spec.require_paths = ["lib"]
|
|
32
31
|
|
|
33
32
|
# runtime dependencies
|
|
33
|
+
spec.add_dependency "logger"
|
|
34
|
+
|
|
34
35
|
# Provider/tooling gems are optional and loaded on use.
|
|
35
36
|
|
|
36
37
|
# For more information and examples about making a new gem, checkout our
|
data/docs/boxcars_guide.md
CHANGED
|
@@ -93,35 +93,38 @@ perplexity_engine = Boxcars::Engines.engine(model: "sonar")
|
|
|
93
93
|
#### Supported Model Aliases
|
|
94
94
|
|
|
95
95
|
**OpenAI Models:**
|
|
96
|
-
- Any OpenAI model ID from the [OpenAI pricing/models page](https://developers.openai.com/api/pricing) (for example `"gpt-5-
|
|
96
|
+
- Any OpenAI model ID from the [OpenAI pricing/models page](https://developers.openai.com/api/pricing) (for example `"gpt-5.6-sol"`, `"gpt-5.6-terra"`, `"gpt-5.6-luna"`, `"o1"`, or `"o3"`) creates `Boxcars::Openai` engines
|
|
97
97
|
|
|
98
98
|
**Anthropic Models:**
|
|
99
|
-
- `"
|
|
100
|
-
- `"opus"` - Creates `Boxcars::Anthropic` with
|
|
99
|
+
- `"sonnet"` - Creates `Boxcars::Anthropic` with `claude-sonnet-5`
|
|
100
|
+
- `"opus"` - Creates `Boxcars::Anthropic` with `claude-opus-5`
|
|
101
|
+
- `"claude-fable-5"` - Creates `Boxcars::Anthropic` with Claude Fable 5
|
|
101
102
|
- `"claude-3-5-sonnet"`, etc. - Any model starting with "claude-"
|
|
102
103
|
|
|
104
|
+
Use the full model IDs for Sol, Terra, Luna, and Fable; their short names are not Boxcars aliases.
|
|
105
|
+
|
|
103
106
|
**Groq Models:**
|
|
104
|
-
- `"
|
|
105
|
-
- `"deepseek"` - Creates `Boxcars::Groq` with DeepSeek R1
|
|
106
|
-
- `"mistral"` - Creates `Boxcars::Groq` with Mistral
|
|
107
|
+
- `"llama-3.3-70b-versatile"` - Creates `Boxcars::Groq` with Llama 3.3 70B
|
|
107
108
|
- Models starting with `"mistral-"`, `"meta-llama/"`, or `"deepseek-"`
|
|
108
109
|
|
|
109
110
|
**Gemini Models:**
|
|
110
|
-
- `"flash"`, `"gemini-flash"` - Creates `Boxcars::GeminiAi` with Gemini 2.5 Flash
|
|
111
|
-
- `"gemini-pro"` - Creates `Boxcars::GeminiAi` with Gemini 2.5 Pro
|
|
112
111
|
- Any model starting with `"gemini-"`
|
|
113
112
|
|
|
114
113
|
**Perplexity Models:**
|
|
115
|
-
- `"
|
|
116
|
-
- `"sonar-pro"
|
|
114
|
+
- `"sonar"` - Creates `Boxcars::Perplexityai` with Sonar
|
|
115
|
+
- `"sonar-pro"` - Creates `Boxcars::Perplexityai` with Sonar Pro
|
|
117
116
|
- Models containing `"-sonar-"`
|
|
118
117
|
|
|
118
|
+
**Cerebras Models:**
|
|
119
|
+
- `"gpt-oss-120b"` - Creates `Boxcars::Cerebras`
|
|
120
|
+
|
|
119
121
|
**Together AI Models:**
|
|
120
122
|
- `"together-model-name"` - Creates `Boxcars::Together` (strips "together-" prefix)
|
|
123
|
+
- `"Qwen/Qwen2.5-VL-72B-Instruct"` - Creates `Boxcars::Together`
|
|
121
124
|
|
|
122
|
-
####
|
|
125
|
+
#### Aliases Removed in v1.0
|
|
123
126
|
|
|
124
|
-
|
|
127
|
+
The following aliases were deprecated in v0.9 and v0.10 and are removed in v1.0:
|
|
125
128
|
|
|
126
129
|
- `"anthropic"` (use `"sonnet"`)
|
|
127
130
|
- `"groq"` (use an explicit model like `"llama-3.3-70b-versatile"`)
|
|
@@ -130,26 +133,7 @@ Some older aliases are still supported but emit a one-time deprecation warning (
|
|
|
130
133
|
- `"flash"` / `"gemini-flash"` / `"gemini-pro"` (use explicit Gemini models)
|
|
131
134
|
- `"deepseek"`, `"mistral"`, `"cerebras"`, `"qwen"` (use explicit model names)
|
|
132
135
|
|
|
133
|
-
`"
|
|
134
|
-
|
|
135
|
-
Enable strict mode to raise an error instead of warning on deprecated aliases:
|
|
136
|
-
|
|
137
|
-
```ruby
|
|
138
|
-
Boxcars.configure do |config|
|
|
139
|
-
config.strict_deprecated_model_aliases = true
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
# or:
|
|
143
|
-
Boxcars::Engines.strict_deprecated_aliases = true
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
Temporarily silence deprecation warnings during migration:
|
|
147
|
-
|
|
148
|
-
```ruby
|
|
149
|
-
Boxcars.configure do |config|
|
|
150
|
-
config.emit_deprecation_warnings = false
|
|
151
|
-
end
|
|
152
|
-
```
|
|
136
|
+
`"sonnet"`, `"opus"`, `"sonar"`, and `"sonar-pro"` remain supported curated aliases. See [UPGRADING.md](../UPGRADING.md) for the complete replacement table.
|
|
153
137
|
|
|
154
138
|
#### Passing Additional Parameters
|
|
155
139
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# Boxcars is a framework for running a series of tools to get an answer to a question.
|
|
4
4
|
module Boxcars
|
|
5
5
|
# A Boxcar that interprets a prompt and executes Active Record code to get answers.
|
|
6
|
-
# rubocop:disable Metrics/ClassLength
|
|
6
|
+
# rubocop:disable-next Metrics/ClassLength
|
|
7
7
|
class ActiveRecord < EngineBoxcar
|
|
8
8
|
# Default description for this boxcar.
|
|
9
9
|
ARDESC = "useful for when you need to query a database for an application named %<name>s."
|
|
@@ -172,9 +172,8 @@ module Boxcars
|
|
|
172
172
|
new_code = new_code.gsub(/\b(puts|print)\b/, '')
|
|
173
173
|
|
|
174
174
|
proc do
|
|
175
|
-
# rubocop:disable Security/Eval
|
|
175
|
+
# rubocop:disable-next Security/Eval
|
|
176
176
|
eval(new_code, binding)
|
|
177
|
-
# rubocop:enable Security/Eval
|
|
178
177
|
end.call
|
|
179
178
|
end
|
|
180
179
|
|
|
@@ -288,5 +287,4 @@ module Boxcars
|
|
|
288
287
|
output_variables: [:answer])
|
|
289
288
|
end
|
|
290
289
|
end
|
|
291
|
-
# rubocop:enable Metrics/ClassLength
|
|
292
290
|
end
|
|
@@ -15,8 +15,13 @@ module Boxcars
|
|
|
15
15
|
max_tokens: 4096,
|
|
16
16
|
temperature: 0.1
|
|
17
17
|
}.freeze
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
MODELS_WITHOUT_SAMPLING_PARAMS = %w[
|
|
19
|
+
claude-opus-4-7
|
|
20
|
+
claude-sonnet-5
|
|
21
|
+
claude-opus-5
|
|
22
|
+
claude-fable-5
|
|
23
|
+
].freeze
|
|
24
|
+
UNSUPPORTED_SAMPLING_PARAMS = %i[temperature top_p top_k].freeze
|
|
20
25
|
|
|
21
26
|
# The default name of the engine.
|
|
22
27
|
DEFAULT_NAME = "Anthropic engine"
|
|
@@ -29,7 +34,7 @@ module Boxcars
|
|
|
29
34
|
raise ArgumentError, "unknown keyword: :prompts" if kwargs.key?(:prompts)
|
|
30
35
|
|
|
31
36
|
user_id = kwargs.delete(:user_id)
|
|
32
|
-
@llm_params =
|
|
37
|
+
@llm_params = remove_unsupported_sampling_params(DEFAULT_PARAMS.merge(kwargs))
|
|
33
38
|
super(description:, name:, batch_size: 20, user_id:)
|
|
34
39
|
end
|
|
35
40
|
|
|
@@ -85,7 +90,7 @@ module Boxcars
|
|
|
85
90
|
params[:stop_sequences] = params.delete(:stop) if params.key?(:stop)
|
|
86
91
|
params[:system] = params[:messages].shift[:content] if params.dig(:messages, 0, :role) == :system
|
|
87
92
|
params[:messages].pop if params[:messages].last[:content].nil? || params[:messages].last[:content].strip.empty?
|
|
88
|
-
|
|
93
|
+
remove_unsupported_sampling_params(params)
|
|
89
94
|
combine_assistant(params)
|
|
90
95
|
end
|
|
91
96
|
|
|
@@ -114,18 +119,21 @@ module Boxcars
|
|
|
114
119
|
|
|
115
120
|
private
|
|
116
121
|
|
|
117
|
-
def
|
|
118
|
-
return params unless
|
|
122
|
+
def remove_unsupported_sampling_params(params)
|
|
123
|
+
return params unless sampling_params_unsupported?(params[:model] || params["model"])
|
|
119
124
|
|
|
120
|
-
|
|
125
|
+
UNSUPPORTED_SAMPLING_PARAMS.each do |key|
|
|
121
126
|
params.delete(key)
|
|
122
127
|
params.delete(key.to_s)
|
|
123
128
|
end
|
|
124
129
|
params
|
|
125
130
|
end
|
|
126
131
|
|
|
127
|
-
def
|
|
128
|
-
model.to_s
|
|
132
|
+
def sampling_params_unsupported?(model)
|
|
133
|
+
model_name = model.to_s
|
|
134
|
+
MODELS_WITHOUT_SAMPLING_PARAMS.any? do |base_model|
|
|
135
|
+
model_name == base_model || model_name.start_with?("#{base_model}-")
|
|
136
|
+
end
|
|
129
137
|
end
|
|
130
138
|
|
|
131
139
|
# Process the raw response from Anthropic API
|
|
@@ -356,7 +356,7 @@ module Boxcars
|
|
|
356
356
|
when "output_text"
|
|
357
357
|
content = i["content"]
|
|
358
358
|
if content.is_a?(Array)
|
|
359
|
-
# rubocop:disable Metrics/BlockNesting
|
|
359
|
+
# rubocop:disable-next Metrics/BlockNesting
|
|
360
360
|
texts << content.filter_map { |c|
|
|
361
361
|
if c.is_a?(Hash)
|
|
362
362
|
if c["text"].is_a?(String)
|
|
@@ -366,7 +366,6 @@ module Boxcars
|
|
|
366
366
|
end
|
|
367
367
|
end
|
|
368
368
|
}.join
|
|
369
|
-
# rubocop:enable Metrics/BlockNesting
|
|
370
369
|
elsif content.is_a?(String)
|
|
371
370
|
texts << content
|
|
372
371
|
end
|
|
@@ -26,7 +26,7 @@ module Boxcars
|
|
|
26
26
|
super(description:, name:, batch_size:, user_id:)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
# rubocop:disable Metrics/MethodLength
|
|
29
|
+
# rubocop:disable-next Metrics/MethodLength
|
|
30
30
|
def client(prompt:, inputs: {}, perplexity_api_key: nil, **kwargs)
|
|
31
31
|
start_time = Time.now
|
|
32
32
|
response_data = { response_obj: nil, parsed_json: nil, success: false, error: nil, status_code: nil }
|
|
@@ -108,7 +108,6 @@ module Boxcars
|
|
|
108
108
|
|
|
109
109
|
perplexity_handle_call_outcome(response_data:)
|
|
110
110
|
end
|
|
111
|
-
# rubocop:enable Metrics/MethodLength
|
|
112
111
|
|
|
113
112
|
def default_params
|
|
114
113
|
@perplexity_params
|
|
@@ -6,7 +6,7 @@ require 'securerandom'
|
|
|
6
6
|
module Boxcars
|
|
7
7
|
# Unified observability module that provides PostHog-centric tracking for all engines
|
|
8
8
|
# Uses standardized $ai_* properties as defined by PostHog's LLM observability spec
|
|
9
|
-
# rubocop:disable Metrics/ModuleLength
|
|
9
|
+
# rubocop:disable-next Metrics/ModuleLength
|
|
10
10
|
module UnifiedObservability
|
|
11
11
|
private
|
|
12
12
|
|
|
@@ -486,5 +486,4 @@ module Boxcars
|
|
|
486
486
|
(response_data[:response_obj].respond_to?(:status) ? response_data[:response_obj].status : nil)
|
|
487
487
|
end
|
|
488
488
|
end
|
|
489
|
-
# rubocop:enable Metrics/ModuleLength
|
|
490
489
|
end
|
data/lib/boxcars/engines.rb
CHANGED
|
@@ -6,33 +6,12 @@ module Boxcars
|
|
|
6
6
|
class Engines
|
|
7
7
|
VALID_ANSWER_REMOVE_IN = "3.0"
|
|
8
8
|
DEFAULT_MODEL = "gemini-2.5-flash"
|
|
9
|
-
DEPRECATED_MODEL_ALIASES = {
|
|
10
|
-
# Generic provider aliases are convenient but ambiguous and make pruning harder.
|
|
11
|
-
"anthropic" => { replacement: "sonnet", remove_in: "3.0", reason: "generic provider alias" },
|
|
12
|
-
"groq" => { replacement: "llama-3.3-70b-versatile", remove_in: "3.0", reason: "generic provider alias" },
|
|
13
|
-
"deepseek" => { replacement: "deepseek-r1-distill-llama-70b", remove_in: "3.0",
|
|
14
|
-
reason: "provider-specific shortcut alias" },
|
|
15
|
-
"mistral" => { replacement: "mistral-saba-24b", remove_in: "3.0", reason: "provider-specific shortcut alias" },
|
|
16
|
-
"online" => { replacement: "sonar", remove_in: "3.0", reason: "ambiguous alias" },
|
|
17
|
-
"huge" => { replacement: "sonar-pro", remove_in: "3.0", reason: "ambiguous alias" },
|
|
18
|
-
"online_huge" => { replacement: "sonar-pro", remove_in: "3.0", reason: "ambiguous alias" },
|
|
19
|
-
"sonar-huge" => { replacement: "sonar-pro", remove_in: "3.0", reason: "legacy alias spelling" },
|
|
20
|
-
"sonar_huge" => { replacement: "sonar-pro", remove_in: "3.0", reason: "legacy alias spelling" },
|
|
21
|
-
"sonar_pro" => { replacement: "sonar-pro", remove_in: "3.0", reason: "legacy alias spelling" },
|
|
22
|
-
"flash" => { replacement: "gemini-2.5-flash", remove_in: "3.0", reason: "generic alias" },
|
|
23
|
-
"gemini-flash" => { replacement: "gemini-2.5-flash", remove_in: "3.0", reason: "generic alias" },
|
|
24
|
-
"gemini-pro" => { replacement: "gemini-2.5-pro", remove_in: "3.0", reason: "generic alias" },
|
|
25
|
-
"cerebras" => { replacement: "gpt-oss-120b", remove_in: "3.0", reason: "generic provider alias" },
|
|
26
|
-
"qwen" => { replacement: "Qwen/Qwen2.5-VL-72B-Instruct", remove_in: "3.0", reason: "provider-specific shortcut alias" }
|
|
27
|
-
}.freeze
|
|
28
9
|
|
|
29
10
|
@emit_deprecation_warnings = true
|
|
30
|
-
@strict_deprecated_aliases = false
|
|
31
|
-
@warned_aliases = {}
|
|
32
11
|
@warned_valid_answer = false
|
|
33
12
|
|
|
34
13
|
class << self
|
|
35
|
-
attr_accessor :emit_deprecation_warnings
|
|
14
|
+
attr_accessor :emit_deprecation_warnings
|
|
36
15
|
end
|
|
37
16
|
|
|
38
17
|
# Create an engine instance based on the model name
|
|
@@ -42,57 +21,40 @@ module Boxcars
|
|
|
42
21
|
def self.engine(model: nil, **kw_args)
|
|
43
22
|
kw_args = Boxcars.configuration.default_model_options.merge(kw_args) if model.nil?
|
|
44
23
|
model ||= Boxcars.configuration.default_model || DEFAULT_MODEL
|
|
45
|
-
emit_alias_deprecation_warning(model)
|
|
46
24
|
Boxcars.logger&.info { "running api with #{model}" }
|
|
47
25
|
|
|
48
26
|
case model.to_s
|
|
27
|
+
when "gpt-oss-120b"
|
|
28
|
+
Boxcars::Cerebras.new(model:, **kw_args)
|
|
49
29
|
when /^(gpt-|o\d($|-))/
|
|
50
30
|
Boxcars::Openai.new(model:, **kw_args)
|
|
51
|
-
when "
|
|
52
|
-
Boxcars::Anthropic.new(model: "claude-sonnet-
|
|
53
|
-
when "opus"
|
|
54
|
-
Boxcars::Anthropic.new(model: "claude-opus-
|
|
31
|
+
when "sonnet"
|
|
32
|
+
Boxcars::Anthropic.new(model: "claude-sonnet-5", **kw_args)
|
|
33
|
+
when "opus"
|
|
34
|
+
Boxcars::Anthropic.new(model: "claude-opus-5", **kw_args)
|
|
55
35
|
when /claude-/
|
|
56
36
|
Boxcars::Anthropic.new(model:, **kw_args)
|
|
57
|
-
when "
|
|
37
|
+
when "llama-3.3-70b-versatile"
|
|
58
38
|
Boxcars::Groq.new(model: "llama-3.3-70b-versatile", **kw_args)
|
|
59
|
-
when "deepseek"
|
|
60
|
-
Boxcars::Groq.new(model: "deepseek-r1-distill-llama-70b", **kw_args)
|
|
61
|
-
when "mistral"
|
|
62
|
-
Boxcars::Groq.new(model: "mistral-saba-24b", **kw_args)
|
|
63
39
|
when /^mistral-/, %r{^meta-llama/}, /^deepseek-/
|
|
64
40
|
Boxcars::Groq.new(model:, **kw_args)
|
|
65
|
-
when "
|
|
41
|
+
when "sonar"
|
|
66
42
|
Boxcars::Perplexityai.new(model: "sonar", **kw_args)
|
|
67
|
-
when "
|
|
43
|
+
when "sonar-pro"
|
|
68
44
|
Boxcars::Perplexityai.new(model: "sonar-pro", **kw_args)
|
|
69
|
-
when
|
|
70
|
-
Boxcars::GeminiAi.new(model: "gemini-2.5-flash", **kw_args)
|
|
71
|
-
when "gemini-pro"
|
|
72
|
-
Boxcars::GeminiAi.new(model: "gemini-2.5-pro", **kw_args)
|
|
73
|
-
when /gemini-/
|
|
45
|
+
when /^gemini-(?!flash$|pro$)/
|
|
74
46
|
Boxcars::GeminiAi.new(model:, **kw_args)
|
|
75
47
|
when /-sonar-/
|
|
76
48
|
Boxcars::Perplexityai.new(model:, **kw_args)
|
|
77
49
|
when /^together-/
|
|
78
50
|
Boxcars::Together.new(model: model[9..-1], **kw_args)
|
|
79
|
-
when "
|
|
80
|
-
Boxcars::
|
|
81
|
-
when "qwen"
|
|
82
|
-
Boxcars::Together.new(model: "Qwen/Qwen2.5-VL-72B-Instruct", **kw_args)
|
|
51
|
+
when "Qwen/Qwen2.5-VL-72B-Instruct"
|
|
52
|
+
Boxcars::Together.new(model:, **kw_args)
|
|
83
53
|
else
|
|
84
54
|
raise Boxcars::ArgumentError, "Unknown model: #{model}"
|
|
85
55
|
end
|
|
86
56
|
end
|
|
87
57
|
|
|
88
|
-
def self.deprecated_alias_info(model)
|
|
89
|
-
DEPRECATED_MODEL_ALIASES[model.to_s]
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def self.deprecated_alias?(model)
|
|
93
|
-
!deprecated_alias_info(model).nil?
|
|
94
|
-
end
|
|
95
|
-
|
|
96
58
|
# Create an engine instance optimized for JSON responses
|
|
97
59
|
# @param model [String] The model name or alias
|
|
98
60
|
# @param kw_args [Hash] Additional arguments to pass to the engine
|
|
@@ -101,7 +63,7 @@ module Boxcars
|
|
|
101
63
|
default_options = { temperature: 0.1 }
|
|
102
64
|
effective_model = model || Boxcars.configuration.default_model || DEFAULT_MODEL
|
|
103
65
|
name = effective_model.to_s
|
|
104
|
-
blocked = name.start_with?("gpt-5", "llama") || name.match?(/sonnet|opus|haiku|sonar/)
|
|
66
|
+
blocked = name.start_with?("gpt-5", "llama", "claude-") || name.match?(/sonnet|opus|haiku|sonar/)
|
|
105
67
|
default_options[:response_format] = { type: "json_object" } unless blocked
|
|
106
68
|
options = default_options.merge(kw_args)
|
|
107
69
|
engine(model:, **options)
|
|
@@ -122,40 +84,10 @@ module Boxcars
|
|
|
122
84
|
Boxcars::Result.valid_conduct_payload?(answer)
|
|
123
85
|
end
|
|
124
86
|
|
|
125
|
-
def self.emit_alias_deprecation_warning(model)
|
|
126
|
-
model_name = model.to_s
|
|
127
|
-
info = deprecated_alias_info(model_name)
|
|
128
|
-
return unless info
|
|
129
|
-
|
|
130
|
-
replacement = info[:replacement]
|
|
131
|
-
remove_in = info[:remove_in]
|
|
132
|
-
reason = info[:reason]
|
|
133
|
-
message = "Deprecated model alias #{model_name.inspect} (#{reason}); use #{replacement.inspect} instead"
|
|
134
|
-
message += " (planned removal in v#{remove_in})" if remove_in
|
|
135
|
-
|
|
136
|
-
raise Boxcars::ArgumentError, "#{message}. Set an explicit model name." if strict_deprecated_aliases_enabled?
|
|
137
|
-
|
|
138
|
-
return unless deprecation_warnings_enabled?
|
|
139
|
-
return if @warned_aliases[model_name]
|
|
140
|
-
|
|
141
|
-
if Boxcars.logger
|
|
142
|
-
Boxcars.logger.warn(message)
|
|
143
|
-
else
|
|
144
|
-
warn(message)
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
@warned_aliases[model_name] = true
|
|
148
|
-
end
|
|
149
|
-
|
|
150
87
|
def self.reset_deprecation_warnings!
|
|
151
|
-
@warned_aliases = {}
|
|
152
88
|
@warned_valid_answer = false
|
|
153
89
|
end
|
|
154
90
|
|
|
155
|
-
def self.strict_deprecated_aliases_enabled?
|
|
156
|
-
strict_deprecated_aliases || Boxcars.configuration.strict_deprecated_model_aliases
|
|
157
|
-
end
|
|
158
|
-
|
|
159
91
|
def self.deprecation_warnings_enabled?
|
|
160
92
|
emit_deprecation_warnings && Boxcars.configuration.emit_deprecation_warnings
|
|
161
93
|
end
|
data/lib/boxcars/prompt.rb
CHANGED
|
@@ -19,11 +19,10 @@ module Boxcars
|
|
|
19
19
|
# compute the prompt parameters with input substitutions (used for chatGPT)
|
|
20
20
|
# @param inputs [Hash] The inputs to use for the prompt.
|
|
21
21
|
# @return [Hash] The formatted prompt { messages: ...}
|
|
22
|
-
# rubocop:disable Lint/UnusedMethodArgument
|
|
22
|
+
# rubocop:disable-next Lint/UnusedMethodArgument
|
|
23
23
|
def as_prompt(inputs: nil, prefixes: nil, show_roles: nil)
|
|
24
24
|
{ prompt: format(inputs) }
|
|
25
25
|
end
|
|
26
|
-
# rubocop:enable Lint/UnusedMethodArgument
|
|
27
26
|
|
|
28
27
|
# compute the prompt parameters with input substitutions
|
|
29
28
|
# @param inputs [Hash] The inputs to use for the prompt.
|
data/lib/boxcars/version.rb
CHANGED
data/lib/boxcars/x_node.rb
CHANGED
|
@@ -32,9 +32,8 @@ module Boxcars
|
|
|
32
32
|
doc = Nokogiri::XML.parse(xml)
|
|
33
33
|
if doc.errors.any?
|
|
34
34
|
Boxcars.debug("XML: #{xml}", :yellow)
|
|
35
|
-
# rubocop:disable Lint/Debugger
|
|
35
|
+
# rubocop:disable-next Lint/Debugger
|
|
36
36
|
debugger if ENV.fetch("DEBUG_XML", false)
|
|
37
|
-
# rubocop:enable Lint/Debugger
|
|
38
37
|
raise XmlError, "XML is not valid: #{doc.errors.map { |e| "#{e.line}:#{e.column} #{e.message}" }}"
|
|
39
38
|
end
|
|
40
39
|
XNode.new(doc.root)
|
|
@@ -53,9 +52,8 @@ module Boxcars
|
|
|
53
52
|
end
|
|
54
53
|
|
|
55
54
|
def xtext(path)
|
|
56
|
-
# rubocop:disable Style/SafeNavigationChainLength
|
|
55
|
+
# rubocop:disable-next Style/SafeNavigationChainLength
|
|
57
56
|
rv = xpath(path)&.text&.gsub(/[[:space:]]+/, " ")&.strip
|
|
58
|
-
# rubocop:enable Style/SafeNavigationChainLength
|
|
59
57
|
return nil if rv.empty?
|
|
60
58
|
|
|
61
59
|
rv
|
data/lib/boxcars.rb
CHANGED
|
@@ -29,7 +29,7 @@ module Boxcars
|
|
|
29
29
|
class Configuration
|
|
30
30
|
attr_writer :openai_access_token, :serpapi_api_key, :groq_api_key, :cerebras_api_key, :perplexity_api_key
|
|
31
31
|
attr_accessor :organization_id, :logger, :log_prompts, :log_generated, :default_train, :default_engine, :default_model,
|
|
32
|
-
:default_model_options, :observability_backend
|
|
32
|
+
:default_model_options, :observability_backend
|
|
33
33
|
|
|
34
34
|
def initialize
|
|
35
35
|
@organization_id = nil
|
|
@@ -37,7 +37,6 @@ module Boxcars
|
|
|
37
37
|
@log_prompts = ENV.fetch("LOG_PROMPTS", false)
|
|
38
38
|
@log_generated = ENV.fetch("LOG_GEN", false)
|
|
39
39
|
@default_model_options = {}
|
|
40
|
-
@strict_deprecated_model_aliases = false
|
|
41
40
|
self.emit_deprecation_warnings = true
|
|
42
41
|
self.openai_official_require_native = ENV.fetch("OPENAI_OFFICIAL_REQUIRE_NATIVE", false)
|
|
43
42
|
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.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Francis Sullivan
|
|
@@ -9,7 +9,21 @@ authors:
|
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
11
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
|
-
dependencies:
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: logger
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
13
27
|
description: You simply set an OpenAI key, give a number of Boxcars to a Train, and
|
|
14
28
|
magic ensues when you run it.
|
|
15
29
|
email:
|
|
@@ -131,7 +145,6 @@ homepage: https://github.com/BoxcarsAI/boxcars
|
|
|
131
145
|
licenses:
|
|
132
146
|
- MIT
|
|
133
147
|
metadata:
|
|
134
|
-
homepage_uri: https://github.com/BoxcarsAI/boxcars
|
|
135
148
|
source_code_uri: https://github.com/BoxcarsAI/boxcars
|
|
136
149
|
changelog_uri: https://github.com/BoxcarsAI/boxcars/blob/main/CHANGELOG.md
|
|
137
150
|
rubygems_mfa_required: 'true'
|
|
@@ -142,14 +155,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
142
155
|
requirements:
|
|
143
156
|
- - ">="
|
|
144
157
|
- !ruby/object:Gem::Version
|
|
145
|
-
version: 3.
|
|
158
|
+
version: 3.3.0
|
|
146
159
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
147
160
|
requirements:
|
|
148
161
|
- - ">="
|
|
149
162
|
- !ruby/object:Gem::Version
|
|
150
163
|
version: '0'
|
|
151
164
|
requirements: []
|
|
152
|
-
rubygems_version:
|
|
165
|
+
rubygems_version: 4.0.16
|
|
153
166
|
specification_version: 4
|
|
154
167
|
summary: Boxcars is a gem that enables you to create new systems with AI composability.
|
|
155
168
|
Inspired by python langchain.
|