sorbet-baml 0.1.0 → 0.3.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/CLAUDE.md +94 -0
- data/README.md +315 -122
- data/Rakefile +2 -2
- data/docs-site/.gitignore +48 -0
- data/docs-site/Gemfile +5 -0
- data/docs-site/Gemfile.lock +140 -0
- data/docs-site/Rakefile +3 -0
- data/docs-site/bridgetown.config.yml +15 -0
- data/docs-site/config/initializers.rb +9 -0
- data/docs-site/config/puma.rb +9 -0
- data/docs-site/config.ru +5 -0
- data/docs-site/esbuild.config.js +11 -0
- data/docs-site/frontend/javascript/index.js +22 -0
- data/docs-site/frontend/styles/index.css +61 -0
- data/docs-site/package.json +18 -0
- data/docs-site/postcss.config.js +6 -0
- data/docs-site/server/roda_app.rb +9 -0
- data/docs-site/src/_components/head.liquid +26 -0
- data/docs-site/src/_components/nav.liquid +68 -0
- data/docs-site/src/_layouts/default.liquid +27 -0
- data/docs-site/src/_layouts/doc.liquid +39 -0
- data/docs-site/src/advanced-usage.md +598 -0
- data/docs-site/src/getting-started.md +170 -0
- data/docs-site/src/index.md +183 -0
- data/docs-site/src/troubleshooting.md +317 -0
- data/docs-site/src/type-mapping.md +236 -0
- data/docs-site/tailwind.config.js +85 -0
- data/examples/description_parameters.rb +49 -0
- data/lib/sorbet_baml/comment_extractor.rb +51 -54
- data/lib/sorbet_baml/converter.rb +69 -35
- data/lib/sorbet_baml/dependency_resolver.rb +11 -11
- data/lib/sorbet_baml/description_extension.rb +34 -0
- data/lib/sorbet_baml/description_extractor.rb +34 -0
- data/lib/sorbet_baml/dspy_tool_converter.rb +97 -0
- data/lib/sorbet_baml/dspy_tool_extensions.rb +23 -0
- data/lib/sorbet_baml/enum_extensions.rb +2 -2
- data/lib/sorbet_baml/struct_extensions.rb +2 -2
- data/lib/sorbet_baml/tool_extensions.rb +23 -0
- data/lib/sorbet_baml/type_mapper.rb +35 -37
- data/lib/sorbet_baml/version.rb +1 -1
- data/lib/sorbet_baml.rb +41 -10
- data/sorbet/config +2 -0
- data/sorbet/rbi/gems/anthropic@1.5.0.rbi +21252 -0
- data/sorbet/rbi/gems/async@2.27.3.rbi +9 -0
- data/sorbet/rbi/gems/bigdecimal@3.2.2.rbi +9 -0
- data/sorbet/rbi/gems/concurrent-ruby@1.3.5.rbi +424 -0
- data/sorbet/rbi/gems/connection_pool@2.5.3.rbi +9 -0
- data/sorbet/rbi/gems/console@1.33.0.rbi +9 -0
- data/sorbet/rbi/gems/dry-configurable@1.3.0.rbi +672 -0
- data/sorbet/rbi/gems/dry-core@1.1.0.rbi +1729 -0
- data/sorbet/rbi/gems/dry-logger@1.1.0.rbi +1317 -0
- data/sorbet/rbi/gems/dspy@0.19.1.rbi +6677 -0
- data/sorbet/rbi/gems/ffi@1.17.2.rbi +2174 -0
- data/sorbet/rbi/gems/fiber-annotation@0.2.0.rbi +9 -0
- data/sorbet/rbi/gems/fiber-local@1.1.0.rbi +9 -0
- data/sorbet/rbi/gems/fiber-storage@1.0.1.rbi +9 -0
- data/sorbet/rbi/gems/google-protobuf@4.32.0.rbi +9 -0
- data/sorbet/rbi/gems/googleapis-common-protos-types@1.20.0.rbi +9 -0
- data/sorbet/rbi/gems/informers@1.2.1.rbi +1875 -0
- data/sorbet/rbi/gems/io-event@1.12.1.rbi +9 -0
- data/sorbet/rbi/gems/metrics@0.13.0.rbi +9 -0
- data/sorbet/rbi/gems/onnxruntime@0.10.0.rbi +304 -0
- data/sorbet/rbi/gems/openai@0.16.0.rbi +68055 -0
- data/sorbet/rbi/gems/opentelemetry-api@1.6.0.rbi +9 -0
- data/sorbet/rbi/gems/opentelemetry-common@0.22.0.rbi +9 -0
- data/sorbet/rbi/gems/opentelemetry-exporter-otlp@0.30.0.rbi +9 -0
- data/sorbet/rbi/gems/opentelemetry-registry@0.4.0.rbi +9 -0
- data/sorbet/rbi/gems/opentelemetry-sdk@1.8.1.rbi +9 -0
- data/sorbet/rbi/gems/opentelemetry-semantic_conventions@1.11.0.rbi +9 -0
- data/sorbet/rbi/gems/polars-df@0.20.0.rbi +9 -0
- data/sorbet/rbi/gems/sorbet-result@1.4.0.rbi +242 -0
- data/sorbet/rbi/gems/sorbet-schema@0.9.2.rbi +743 -0
- data/sorbet/rbi/gems/sorbet-struct-comparable@1.3.0.rbi +48 -0
- data/sorbet/rbi/gems/tokenizers@0.5.5.rbi +754 -0
- data/sorbet/rbi/gems/traces@0.17.0.rbi +9 -0
- data/sorbet/rbi/gems/zeitwerk@2.7.3.rbi +1429 -0
- metadata +67 -7
- data/docs/README.md +0 -117
- data/docs/advanced-usage.md +0 -427
- data/docs/getting-started.md +0 -91
- data/docs/troubleshooting.md +0 -291
- data/docs/type-mapping.md +0 -192
data/README.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# sorbet-baml
|
|
2
2
|
|
|
3
|
+
[](https://rubygems.org/gems/sorbet-baml)
|
|
4
|
+
[](https://rubygems.org/gems/sorbet-baml)
|
|
5
|
+
[](https://github.com/vicentereig/sorbet-baml/blob/main/LICENSE.txt)
|
|
6
|
+
[](https://sorbet.org)
|
|
7
|
+
|
|
3
8
|
Ruby-idiomatic conversion from Sorbet types to BAML (Boundary AI Markup Language) for efficient LLM prompting.
|
|
4
9
|
|
|
5
10
|
## What is this?
|
|
@@ -15,25 +20,43 @@ When working with LLMs, token efficiency directly impacts:
|
|
|
15
20
|
|
|
16
21
|
BAML provides the perfect balance: concise, readable, and LLM-friendly.
|
|
17
22
|
|
|
18
|
-
### Example
|
|
23
|
+
### Example: Autonomous Research Workflow
|
|
19
24
|
|
|
20
25
|
```ruby
|
|
21
|
-
#
|
|
22
|
-
class
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
# Complex LLM workflow types for autonomous research
|
|
27
|
+
class ComplexityLevel < T::Enum
|
|
28
|
+
enums do
|
|
29
|
+
# Basic analysis requiring straightforward research
|
|
30
|
+
Basic = new('basic')
|
|
31
|
+
# Advanced analysis requiring deep domain expertise
|
|
32
|
+
Advanced = new('advanced')
|
|
33
|
+
end
|
|
27
34
|
end
|
|
28
35
|
|
|
29
|
-
|
|
30
|
-
|
|
36
|
+
class TaskDecomposition < T::Struct
|
|
37
|
+
# The main research topic being investigated
|
|
38
|
+
const :research_topic, String
|
|
39
|
+
# Target complexity level for the decomposition
|
|
40
|
+
const :complexity_level, ComplexityLevel
|
|
41
|
+
# Autonomously generated list of research subtasks
|
|
42
|
+
const :subtasks, T::Array[String]
|
|
43
|
+
# Strategic priority rankings for each subtask
|
|
44
|
+
const :priority_order, T::Array[Integer]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Ruby-idiomatic conversion with dependencies
|
|
48
|
+
TaskDecomposition.to_baml
|
|
31
49
|
# =>
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
50
|
+
# enum ComplexityLevel {
|
|
51
|
+
# "basic" @description("Basic analysis requiring straightforward research")
|
|
52
|
+
# "advanced" @description("Advanced analysis requiring deep domain expertise")
|
|
53
|
+
# }
|
|
54
|
+
#
|
|
55
|
+
# class TaskDecomposition {
|
|
56
|
+
# research_topic string @description("The main research topic being investigated")
|
|
57
|
+
# complexity_level ComplexityLevel @description("Target complexity level for the decomposition")
|
|
58
|
+
# subtasks string[] @description("Autonomously generated list of research subtasks")
|
|
59
|
+
# priority_order int[] @description("Strategic priority rankings for each subtask")
|
|
37
60
|
# }
|
|
38
61
|
```
|
|
39
62
|
|
|
@@ -56,111 +79,231 @@ gem install sorbet-baml
|
|
|
56
79
|
```ruby
|
|
57
80
|
require 'sorbet-baml'
|
|
58
81
|
|
|
59
|
-
# 🎯 Ruby-idiomatic API
|
|
82
|
+
# 🎯 Ruby-idiomatic API for complex LLM workflows
|
|
60
83
|
|
|
61
|
-
class
|
|
84
|
+
class ConfidenceLevel < T::Enum
|
|
62
85
|
enums do
|
|
63
|
-
|
|
64
|
-
|
|
86
|
+
# Low confidence, requires further verification
|
|
87
|
+
Low = new('low')
|
|
88
|
+
# High confidence, strongly supported by multiple sources
|
|
89
|
+
High = new('high')
|
|
65
90
|
end
|
|
66
91
|
end
|
|
67
92
|
|
|
68
|
-
class
|
|
69
|
-
|
|
70
|
-
const :
|
|
71
|
-
|
|
93
|
+
class ResearchFindings < T::Struct
|
|
94
|
+
# Detailed findings and analysis results
|
|
95
|
+
const :findings, String
|
|
96
|
+
# Key actionable insights extracted
|
|
97
|
+
const :key_insights, T::Array[String]
|
|
98
|
+
# Assessment of evidence quality and reliability
|
|
99
|
+
const :evidence_quality, ConfidenceLevel
|
|
100
|
+
# Confidence score for the findings (1-10 scale)
|
|
101
|
+
const :confidence_score, Integer
|
|
72
102
|
end
|
|
73
103
|
|
|
74
|
-
class
|
|
75
|
-
|
|
76
|
-
const :
|
|
77
|
-
|
|
78
|
-
const :
|
|
79
|
-
|
|
104
|
+
class ResearchSynthesis < T::Struct
|
|
105
|
+
# High-level executive summary of all findings
|
|
106
|
+
const :executive_summary, String
|
|
107
|
+
# Primary conclusions drawn from the research
|
|
108
|
+
const :key_conclusions, T::Array[String]
|
|
109
|
+
# Collection of research findings
|
|
110
|
+
const :findings_collection, T::Array[ResearchFindings]
|
|
80
111
|
end
|
|
81
112
|
|
|
82
113
|
# Convert with smart defaults (dependencies + descriptions included!)
|
|
83
|
-
|
|
84
|
-
Status.to_baml
|
|
85
|
-
Address.to_baml
|
|
114
|
+
ResearchSynthesis.to_baml
|
|
86
115
|
|
|
87
116
|
# 🚀 Smart defaults include dependencies and descriptions automatically
|
|
88
117
|
# =>
|
|
89
|
-
# enum
|
|
90
|
-
# "
|
|
91
|
-
# "
|
|
118
|
+
# enum ConfidenceLevel {
|
|
119
|
+
# "low" @description("Low confidence, requires further verification")
|
|
120
|
+
# "high" @description("High confidence, strongly supported by multiple sources")
|
|
92
121
|
# }
|
|
93
122
|
#
|
|
94
|
-
# class
|
|
95
|
-
#
|
|
96
|
-
#
|
|
97
|
-
#
|
|
123
|
+
# class ResearchFindings {
|
|
124
|
+
# findings string @description("Detailed findings and analysis results")
|
|
125
|
+
# key_insights string[] @description("Key actionable insights extracted")
|
|
126
|
+
# evidence_quality ConfidenceLevel @description("Assessment of evidence quality and reliability")
|
|
127
|
+
# confidence_score int @description("Confidence score for the findings (1-10 scale)")
|
|
98
128
|
# }
|
|
99
129
|
#
|
|
100
|
-
# class
|
|
101
|
-
#
|
|
102
|
-
#
|
|
103
|
-
#
|
|
104
|
-
# tags string[]
|
|
105
|
-
# metadata map<string, string | int>
|
|
130
|
+
# class ResearchSynthesis {
|
|
131
|
+
# executive_summary string @description("High-level executive summary of all findings")
|
|
132
|
+
# key_conclusions string[] @description("Primary conclusions drawn from the research")
|
|
133
|
+
# findings_collection ResearchFindings[] @description("Collection of research findings")
|
|
106
134
|
# }
|
|
107
135
|
|
|
108
136
|
# 🎯 Disable features if needed
|
|
109
|
-
|
|
110
|
-
|
|
137
|
+
ResearchSynthesis.to_baml(include_descriptions: false)
|
|
138
|
+
ResearchSynthesis.to_baml(include_dependencies: false)
|
|
111
139
|
|
|
112
140
|
# 🚀 Customize formatting (smart defaults still apply)
|
|
113
|
-
|
|
141
|
+
ResearchSynthesis.to_baml(indent_size: 4)
|
|
114
142
|
|
|
115
143
|
# Legacy API (no smart defaults, for backwards compatibility)
|
|
116
|
-
SorbetBaml.from_struct(
|
|
117
|
-
SorbetBaml.from_structs([
|
|
144
|
+
SorbetBaml.from_struct(ResearchSynthesis)
|
|
145
|
+
SorbetBaml.from_structs([ResearchSynthesis, ResearchFindings])
|
|
118
146
|
```
|
|
119
147
|
|
|
120
|
-
## 🎯 Field Descriptions
|
|
148
|
+
## 🎯 Field Descriptions for LLM Context
|
|
121
149
|
|
|
122
|
-
Add context to your BAML types by documenting fields with comments:
|
|
150
|
+
Add crucial context to your BAML types by documenting fields with comments - essential for autonomous agents and complex workflows:
|
|
123
151
|
|
|
124
152
|
```ruby
|
|
125
|
-
class
|
|
126
|
-
|
|
127
|
-
|
|
153
|
+
class TaskType < T::Enum
|
|
154
|
+
enums do
|
|
155
|
+
# Literature review and information gathering
|
|
156
|
+
Research = new('research')
|
|
157
|
+
# Combining multiple sources into coherent insights
|
|
158
|
+
Synthesis = new('synthesis')
|
|
159
|
+
# Evaluating options or making recommendations
|
|
160
|
+
Evaluation = new('evaluation')
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
class ResearchSubtask < T::Struct
|
|
165
|
+
# Clear description of the specific research objective
|
|
166
|
+
const :objective, String
|
|
167
|
+
|
|
168
|
+
# Type of research task to be performed
|
|
169
|
+
const :task_type, TaskType
|
|
170
|
+
|
|
171
|
+
# Strategic priority ranking for task sequencing (1-5 scale)
|
|
172
|
+
const :priority, Integer
|
|
128
173
|
|
|
129
|
-
#
|
|
130
|
-
const :
|
|
174
|
+
# Estimated effort required in hours
|
|
175
|
+
const :estimated_hours, Integer
|
|
131
176
|
|
|
132
|
-
#
|
|
133
|
-
const :
|
|
177
|
+
# Suggested agent capabilities needed for optimal execution
|
|
178
|
+
const :required_capabilities, T::Array[String]
|
|
134
179
|
end
|
|
135
180
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
181
|
+
# Generate BAML (descriptions included by default!)
|
|
182
|
+
ResearchSubtask.to_baml
|
|
183
|
+
# =>
|
|
184
|
+
# enum TaskType {
|
|
185
|
+
# "research" @description("Literature review and information gathering")
|
|
186
|
+
# "synthesis" @description("Combining multiple sources into coherent insights")
|
|
187
|
+
# "evaluation" @description("Evaluating options or making recommendations")
|
|
188
|
+
# }
|
|
189
|
+
#
|
|
190
|
+
# class ResearchSubtask {
|
|
191
|
+
# objective string @description("Clear description of the specific research objective")
|
|
192
|
+
# task_type TaskType @description("Type of research task to be performed")
|
|
193
|
+
# priority int @description("Strategic priority ranking for task sequencing (1-5 scale)")
|
|
194
|
+
# estimated_hours int @description("Estimated effort required in hours")
|
|
195
|
+
# required_capabilities string[] @description("Suggested agent capabilities needed for optimal execution")
|
|
196
|
+
# }
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**Why descriptions matter**: LLMs use field descriptions to understand context and generate more accurate, meaningful data. This is crucial for complex domains where field names alone aren't sufficient.
|
|
200
|
+
|
|
201
|
+
## 🛠️ Tool Type Definitions
|
|
202
|
+
|
|
203
|
+
Generate BAML tool specifications for agentic workflows, function calling, and structured LLM interactions:
|
|
204
|
+
|
|
205
|
+
### T::Struct-based Tools
|
|
206
|
+
|
|
207
|
+
```ruby
|
|
208
|
+
class ReplyTool < T::Struct
|
|
209
|
+
# The response message to send back to the user
|
|
210
|
+
const :response, String
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
class SearchTool < T::Struct
|
|
214
|
+
# The search query to execute
|
|
215
|
+
const :query, String
|
|
216
|
+
# Maximum number of results to return
|
|
217
|
+
const :limit, T.nilable(Integer)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Generate BAML tool definitions
|
|
221
|
+
ReplyTool.to_baml_tool
|
|
222
|
+
# =>
|
|
223
|
+
# class ReplyTool {
|
|
224
|
+
# response string @description("The response message to send back to the user")
|
|
225
|
+
# }
|
|
226
|
+
|
|
227
|
+
SearchTool.to_baml_tool
|
|
228
|
+
# =>
|
|
229
|
+
# class SearchTool {
|
|
230
|
+
# query string @description("The search query to execute")
|
|
231
|
+
# limit int? @description("Maximum number of results to return")
|
|
232
|
+
# }
|
|
233
|
+
|
|
234
|
+
# Module API also available
|
|
235
|
+
SorbetBaml.from_tool(ReplyTool)
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### DSPy-style Tools (Optional)
|
|
239
|
+
|
|
240
|
+
When `dspy.rb` is available, automatically convert DSPy tools with rich metadata:
|
|
241
|
+
|
|
242
|
+
```ruby
|
|
243
|
+
class CalculatorTool < DSPy::Tools::Base
|
|
244
|
+
extend T::Sig
|
|
245
|
+
|
|
246
|
+
tool_name 'calculator'
|
|
247
|
+
tool_description 'Performs basic arithmetic operations'
|
|
248
|
+
|
|
249
|
+
sig { params(operation: String, num1: Float, num2: Float).returns(T.any(Float, String)) }
|
|
250
|
+
def call(operation:, num1:, num2:)
|
|
251
|
+
case operation.downcase
|
|
252
|
+
when 'add' then num1 + num2
|
|
253
|
+
when 'subtract' then num1 - num2
|
|
254
|
+
when 'multiply' then num1 * num2
|
|
255
|
+
when 'divide'
|
|
256
|
+
return "Error: Cannot divide by zero" if num2 == 0
|
|
257
|
+
num1 / num2
|
|
258
|
+
else
|
|
259
|
+
"Error: Unknown operation '#{operation}'. Use add, subtract, multiply, or divide"
|
|
260
|
+
end
|
|
143
261
|
end
|
|
144
262
|
end
|
|
145
263
|
|
|
146
|
-
#
|
|
147
|
-
|
|
264
|
+
# Automatic extraction of tool metadata and parameter types
|
|
265
|
+
CalculatorTool.to_baml
|
|
148
266
|
# =>
|
|
149
|
-
#
|
|
150
|
-
#
|
|
151
|
-
#
|
|
152
|
-
#
|
|
267
|
+
# // Performs basic arithmetic operations
|
|
268
|
+
# class calculator {
|
|
269
|
+
# operation string @description("Parameter operation")
|
|
270
|
+
# num1 float @description("Parameter num1")
|
|
271
|
+
# num2 float @description("Parameter num2")
|
|
153
272
|
# }
|
|
154
273
|
|
|
155
|
-
|
|
274
|
+
# Optional parameters handled correctly
|
|
275
|
+
class SearchTool < DSPy::Tools::Base
|
|
276
|
+
extend T::Sig
|
|
277
|
+
|
|
278
|
+
tool_name 'search'
|
|
279
|
+
tool_description 'Search for information'
|
|
280
|
+
|
|
281
|
+
sig { params(query: String, limit: T.nilable(Integer)).returns(T::Array[String]) }
|
|
282
|
+
def call(query:, limit: nil)
|
|
283
|
+
# Implementation...
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
SearchTool.to_baml
|
|
156
288
|
# =>
|
|
157
|
-
#
|
|
158
|
-
#
|
|
159
|
-
#
|
|
289
|
+
# // Search for information
|
|
290
|
+
# class search {
|
|
291
|
+
# query string @description("Parameter query")
|
|
292
|
+
# limit int? @description("Parameter limit (optional)")
|
|
160
293
|
# }
|
|
294
|
+
|
|
295
|
+
# Module API also available
|
|
296
|
+
SorbetBaml.from_dspy_tool(CalculatorTool)
|
|
161
297
|
```
|
|
162
298
|
|
|
163
|
-
**
|
|
299
|
+
**Tool Features:**
|
|
300
|
+
- ✅ **T::Struct tools**: Convert any struct to BAML tool definition
|
|
301
|
+
- ✅ **DSPy integration**: Automatic extraction from DSPy::Tools::Base classes
|
|
302
|
+
- ✅ **Parameter types**: Full Sorbet type support (string, int, float, arrays, maps, etc.)
|
|
303
|
+
- ✅ **Optional parameters**: Automatically detect and mark with `?`
|
|
304
|
+
- ✅ **Descriptions**: Extract from comments (T::Struct) or automatic generation (DSPy)
|
|
305
|
+
- ✅ **Tool metadata**: Names, descriptions, and parameter documentation
|
|
306
|
+
- ✅ **Ruby-idiomatic**: `.to_baml_tool()` and `.to_baml()` methods
|
|
164
307
|
|
|
165
308
|
## 🎯 Complete Type Support
|
|
166
309
|
|
|
@@ -191,6 +334,8 @@ Status.to_baml
|
|
|
191
334
|
### 🚀 Advanced Features
|
|
192
335
|
|
|
193
336
|
- **Ruby-idiomatic API**: Every T::Struct and T::Enum gets `.to_baml` method
|
|
337
|
+
- **Tool definitions**: Generate BAML tool specs for function calling and agentic workflows
|
|
338
|
+
- **DSPy integration**: Automatic tool conversion from DSPy::Tools::Base classes
|
|
194
339
|
- **Smart defaults**: Field descriptions and dependencies included automatically
|
|
195
340
|
- **Field descriptions**: Extracts comments from source code for LLM context
|
|
196
341
|
- **Dependency management**: Automatically includes all referenced types
|
|
@@ -218,53 +363,72 @@ Status.to_baml
|
|
|
218
363
|
|
|
219
364
|
## 🏁 Production Ready
|
|
220
365
|
|
|
221
|
-
This gem has reached **feature completeness** for core BAML conversion needs. The Ruby-idiomatic API is stable and thoroughly tested with **
|
|
366
|
+
This gem has reached **feature completeness** for core BAML conversion needs. The Ruby-idiomatic API is stable and thoroughly tested with **80+ test cases** covering all type combinations, tool definitions, and edge cases.
|
|
222
367
|
|
|
223
368
|
### 📊 Quality Metrics
|
|
224
369
|
|
|
225
370
|
- ✅ **100% Test Coverage** - All features comprehensively tested
|
|
226
371
|
- ✅ **Full Sorbet Type Safety** - Zero type errors throughout codebase
|
|
227
|
-
- ✅ **
|
|
372
|
+
- ✅ **80+ Test Cases** - Covering basic types, complex combinations, tool definitions, and edge cases
|
|
228
373
|
- ✅ **TDD Development** - All features built test-first
|
|
374
|
+
- ✅ **Field Descriptions** - Automatic comment extraction for LLM context
|
|
375
|
+
- ✅ **Tool Definitions** - BAML tool specifications for function calling and agentic workflows
|
|
376
|
+
- ✅ **DSPy Integration** - Automatic tool conversion from DSPy::Tools::Base classes
|
|
377
|
+
- ✅ **Smart Defaults** - Dependencies and descriptions included by default
|
|
229
378
|
- ✅ **Zero Breaking Changes** - Maintains backward compatibility
|
|
230
379
|
|
|
231
|
-
###
|
|
380
|
+
### ✅ Complete Feature Set
|
|
381
|
+
|
|
382
|
+
- ✅ **Ruby-idiomatic API**: Every T::Struct and T::Enum gets `.to_baml` method
|
|
383
|
+
- ✅ **Tool definitions**: Generate BAML tool specifications from T::Struct classes
|
|
384
|
+
- ✅ **DSPy integration**: Automatic tool conversion from DSPy::Tools::Base classes
|
|
385
|
+
- ✅ **Smart defaults**: Field descriptions and dependencies included automatically
|
|
386
|
+
- ✅ **Field descriptions**: Extract documentation from comments for LLM context
|
|
387
|
+
- ✅ **Dependency management**: Automatically includes all referenced types
|
|
388
|
+
- ✅ **Proper ordering**: Dependencies are sorted topologically
|
|
389
|
+
- ✅ **Type safety**: Full Sorbet type checking throughout
|
|
232
390
|
|
|
233
|
-
|
|
391
|
+
### 🗺️ Future Enhancements (Optional)
|
|
234
392
|
|
|
393
|
+
- [ ] **DSPy-independent tool API**: Tools shouldn't require DSPy, just follow the same API pattern
|
|
235
394
|
- [ ] **Type aliases**: `T.type_alias { String }` → `type Alias = string`
|
|
236
|
-
- [ ] **Field descriptions**: Extract documentation from comments
|
|
237
395
|
- [ ] **Custom naming**: Convert between snake_case ↔ camelCase
|
|
238
|
-
- [ ] **CLI tool**: `sorbet-baml convert
|
|
396
|
+
- [ ] **CLI tool**: `sorbet-baml convert MyStruct` command
|
|
239
397
|
- [ ] **Validation**: Verify generated BAML syntax
|
|
240
398
|
- [ ] **Self-referential types**: `Employee` with `manager: T.nilable(Employee)`
|
|
241
399
|
|
|
242
400
|
### 📈 Version History
|
|
243
401
|
|
|
244
402
|
- **v0.0.1** - Initial implementation with basic type support
|
|
245
|
-
- **v0.1.0**
|
|
403
|
+
- **v0.1.0** - Complete type system + Ruby-idiomatic API + field descriptions + smart defaults
|
|
404
|
+
- **v0.2.0** - Description parameter support and enhanced field extraction
|
|
405
|
+
- **v0.3.0** - Tool type definitions + DSPy integration + 80+ test cases + comprehensive documentation
|
|
246
406
|
|
|
247
|
-
## 🌟 Real-World Usage
|
|
407
|
+
## 🌟 Real-World Usage: Autonomous Research Agents
|
|
248
408
|
|
|
249
|
-
Perfect for
|
|
409
|
+
Perfect for agentic workflows, deep research systems, and complex LLM applications:
|
|
250
410
|
|
|
251
411
|
```ruby
|
|
252
|
-
#
|
|
253
|
-
class
|
|
254
|
-
# Your
|
|
412
|
+
# Define your autonomous research workflow types
|
|
413
|
+
class TaskDecomposition < T::Struct
|
|
414
|
+
# Your complex research schema...
|
|
255
415
|
end
|
|
256
416
|
|
|
257
|
-
# Generate BAML for LLM
|
|
417
|
+
# Generate BAML for LLM agents
|
|
258
418
|
prompt = <<~PROMPT
|
|
259
|
-
|
|
419
|
+
You are an autonomous research agent. Analyze this topic and decompose it into strategic subtasks.
|
|
260
420
|
|
|
261
|
-
|
|
421
|
+
Schema for your output:
|
|
422
|
+
#{TaskDecomposition.to_baml}
|
|
262
423
|
|
|
263
|
-
|
|
424
|
+
Topic: "Impact of AI on healthcare delivery systems"
|
|
425
|
+
|
|
426
|
+
Provide a comprehensive task decomposition in JSON format.
|
|
264
427
|
PROMPT
|
|
265
428
|
|
|
266
429
|
# Use with OpenAI, Anthropic, or any LLM provider
|
|
267
|
-
response =
|
|
430
|
+
response = llm_client.chat(prompt)
|
|
431
|
+
result = TaskDecomposition.from_json(response.content)
|
|
268
432
|
```
|
|
269
433
|
|
|
270
434
|
## 🔗 Integration Examples
|
|
@@ -292,43 +456,65 @@ api_types = [User, Order, Product].map(&:to_baml).join("\n\n")
|
|
|
292
456
|
|
|
293
457
|
Here's a real-world comparison using a complex agentic workflow from production DSPy.rb usage:
|
|
294
458
|
|
|
295
|
-
### Complex T::Struct Types (
|
|
459
|
+
### Complex T::Struct Types (Production Agentic Workflow)
|
|
296
460
|
|
|
297
461
|
```ruby
|
|
462
|
+
# Real autonomous research workflow from production DSPy.rb usage
|
|
298
463
|
class ComplexityLevel < T::Enum
|
|
299
464
|
enums do
|
|
465
|
+
# Basic analysis requiring straightforward research
|
|
300
466
|
Basic = new('basic')
|
|
467
|
+
# Intermediate analysis requiring synthesis of multiple sources
|
|
301
468
|
Intermediate = new('intermediate')
|
|
469
|
+
# Advanced analysis requiring deep domain expertise and complex reasoning
|
|
302
470
|
Advanced = new('advanced')
|
|
303
471
|
end
|
|
304
472
|
end
|
|
305
473
|
|
|
306
474
|
class TaskDecomposition < T::Struct
|
|
307
|
-
|
|
475
|
+
# The main research topic being investigated
|
|
476
|
+
const :research_topic, String
|
|
477
|
+
# Additional context or constraints for the research
|
|
308
478
|
const :context, String
|
|
479
|
+
# Target complexity level for the decomposition
|
|
309
480
|
const :complexity_level, ComplexityLevel
|
|
481
|
+
# Autonomously generated list of research subtasks
|
|
310
482
|
const :subtasks, T::Array[String]
|
|
483
|
+
# Type classification for each task (analysis, synthesis, investigation, etc.)
|
|
311
484
|
const :task_types, T::Array[String]
|
|
485
|
+
# Strategic priority rankings (1-5 scale) for each subtask
|
|
312
486
|
const :priority_order, T::Array[Integer]
|
|
487
|
+
# Effort estimates in hours for each subtask
|
|
313
488
|
const :estimated_effort, T::Array[Integer]
|
|
489
|
+
# Task dependency relationships for optimal sequencing
|
|
314
490
|
const :dependencies, T::Array[String]
|
|
491
|
+
# Suggested agent types/skills needed for each task
|
|
315
492
|
const :agent_requirements, T::Array[String]
|
|
316
493
|
end
|
|
317
494
|
|
|
318
495
|
class ResearchExecution < T::Struct
|
|
496
|
+
# The specific research subtask to execute
|
|
319
497
|
const :subtask, String
|
|
498
|
+
# Accumulated context from previous research steps
|
|
320
499
|
const :context, String
|
|
500
|
+
# Any specific constraints or focus areas for this research
|
|
321
501
|
const :constraints, String
|
|
502
|
+
# Detailed research findings and analysis
|
|
322
503
|
const :findings, String
|
|
504
|
+
# Key actionable insights extracted from the research
|
|
323
505
|
const :key_insights, T::Array[String]
|
|
506
|
+
# Confidence in findings quality (1-10 scale)
|
|
324
507
|
const :confidence_level, Integer
|
|
508
|
+
# Assessment of evidence quality and reliability
|
|
325
509
|
const :evidence_quality, String
|
|
510
|
+
# Recommended next steps based on these findings
|
|
326
511
|
const :next_steps, T::Array[String]
|
|
512
|
+
# Identified gaps in knowledge or areas needing further research
|
|
327
513
|
const :knowledge_gaps, T::Array[String]
|
|
328
514
|
end
|
|
329
515
|
```
|
|
330
516
|
|
|
331
|
-
### 📊 **BAML Output (Ruby-idiomatic)**
|
|
517
|
+
### 📊 **BAML Output (Ruby-idiomatic with descriptions)**
|
|
332
518
|
|
|
333
519
|
```ruby
|
|
334
520
|
[ComplexityLevel, TaskDecomposition, ResearchExecution].map(&:to_baml).join("\n\n")
|
|
@@ -336,37 +522,37 @@ end
|
|
|
336
522
|
|
|
337
523
|
```baml
|
|
338
524
|
enum ComplexityLevel {
|
|
339
|
-
"basic"
|
|
340
|
-
"intermediate"
|
|
341
|
-
"advanced"
|
|
525
|
+
"basic" @description("Basic analysis requiring straightforward research")
|
|
526
|
+
"intermediate" @description("Intermediate analysis requiring synthesis of multiple sources")
|
|
527
|
+
"advanced" @description("Advanced analysis requiring deep domain expertise and complex reasoning")
|
|
342
528
|
}
|
|
343
529
|
|
|
344
530
|
class TaskDecomposition {
|
|
345
|
-
topic
|
|
346
|
-
context string
|
|
347
|
-
complexity_level ComplexityLevel
|
|
348
|
-
subtasks string[]
|
|
349
|
-
task_types string[]
|
|
350
|
-
priority_order int[]
|
|
351
|
-
estimated_effort int[]
|
|
352
|
-
dependencies string[]
|
|
353
|
-
agent_requirements string[]
|
|
531
|
+
research_topic string @description("The main research topic being investigated")
|
|
532
|
+
context string @description("Additional context or constraints for the research")
|
|
533
|
+
complexity_level ComplexityLevel @description("Target complexity level for the decomposition")
|
|
534
|
+
subtasks string[] @description("Autonomously generated list of research subtasks")
|
|
535
|
+
task_types string[] @description("Type classification for each task (analysis, synthesis, investigation, etc.)")
|
|
536
|
+
priority_order int[] @description("Strategic priority rankings (1-5 scale) for each subtask")
|
|
537
|
+
estimated_effort int[] @description("Effort estimates in hours for each subtask")
|
|
538
|
+
dependencies string[] @description("Task dependency relationships for optimal sequencing")
|
|
539
|
+
agent_requirements string[] @description("Suggested agent types/skills needed for each task")
|
|
354
540
|
}
|
|
355
541
|
|
|
356
542
|
class ResearchExecution {
|
|
357
|
-
subtask string
|
|
358
|
-
context string
|
|
359
|
-
constraints string
|
|
360
|
-
findings string
|
|
361
|
-
key_insights string[]
|
|
362
|
-
confidence_level int
|
|
363
|
-
evidence_quality string
|
|
364
|
-
next_steps string[]
|
|
365
|
-
knowledge_gaps string[]
|
|
543
|
+
subtask string @description("The specific research subtask to execute")
|
|
544
|
+
context string @description("Accumulated context from previous research steps")
|
|
545
|
+
constraints string @description("Any specific constraints or focus areas for this research")
|
|
546
|
+
findings string @description("Detailed research findings and analysis")
|
|
547
|
+
key_insights string[] @description("Key actionable insights extracted from the research")
|
|
548
|
+
confidence_level int @description("Confidence in findings quality (1-10 scale)")
|
|
549
|
+
evidence_quality string @description("Assessment of evidence quality and reliability")
|
|
550
|
+
next_steps string[] @description("Recommended next steps based on these findings")
|
|
551
|
+
knowledge_gaps string[] @description("Identified gaps in knowledge or areas needing further research")
|
|
366
552
|
}
|
|
367
553
|
```
|
|
368
554
|
|
|
369
|
-
**BAML Token Count: ~
|
|
555
|
+
**BAML Token Count: ~320 tokens**
|
|
370
556
|
|
|
371
557
|
### 📊 **JSON Schema Equivalent**
|
|
372
558
|
|
|
@@ -439,22 +625,29 @@ class ResearchExecution {
|
|
|
439
625
|
}
|
|
440
626
|
```
|
|
441
627
|
|
|
442
|
-
**JSON Schema Token Count: ~
|
|
628
|
+
**JSON Schema Token Count: ~680 tokens**
|
|
629
|
+
|
|
630
|
+
### 🎯 **Results: 53% Token Reduction (with descriptions)**
|
|
443
631
|
|
|
444
|
-
|
|
632
|
+
| Format | Tokens | Reduction |
|
|
633
|
+
|--------|--------|-----------|
|
|
634
|
+
| JSON Schema | ~680 | baseline |
|
|
635
|
+
| **BAML** | **~320** | **🔥 53% fewer** |
|
|
445
636
|
|
|
637
|
+
**Without descriptions:**
|
|
446
638
|
| Format | Tokens | Reduction |
|
|
447
639
|
|--------|--------|-----------|
|
|
448
640
|
| JSON Schema | ~450 | baseline |
|
|
449
641
|
| **BAML** | **~180** | **🔥 60% fewer** |
|
|
450
642
|
|
|
451
643
|
**Real Impact:**
|
|
452
|
-
- **Cost Savings**: 60% reduction in prompt tokens =
|
|
644
|
+
- **Cost Savings**: 53-60% reduction in prompt tokens = significant LLM API cost savings
|
|
453
645
|
- **Performance**: Smaller prompts = faster LLM response times
|
|
454
646
|
- **Context Efficiency**: More room for actual content vs. type definitions
|
|
647
|
+
- **LLM Understanding**: Descriptions provide crucial context for autonomous agents
|
|
455
648
|
- **Readability**: BAML is human-readable and maintainable
|
|
456
649
|
|
|
457
|
-
*This example represents actual agentic workflows from production DSPy.rb applications using complex nested types, enums, and arrays - exactly the scenarios where token efficiency
|
|
650
|
+
*This example represents actual agentic workflows from production DSPy.rb applications using complex nested types, enums, and arrays - exactly the scenarios where token efficiency and LLM understanding matter most.*
|
|
458
651
|
|
|
459
652
|
## Credits
|
|
460
653
|
|