sorbet-baml 0.2.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/README.md +123 -2
- 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 +16 -16
- data/lib/sorbet_baml/comment_extractor.rb +31 -39
- data/lib/sorbet_baml/converter.rb +66 -32
- data/lib/sorbet_baml/dependency_resolver.rb +11 -11
- data/lib/sorbet_baml/description_extension.rb +5 -5
- data/lib/sorbet_baml/description_extractor.rb +8 -10
- 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 -13
- 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 +63 -2
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: doc
|
|
3
|
+
title: "Type Mapping Reference"
|
|
4
|
+
description: "Complete mapping between Sorbet types and BAML output. Learn how every Sorbet type converts to efficient BAML format."
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Type Mapping Reference
|
|
8
|
+
|
|
9
|
+
Complete mapping between Sorbet types and BAML output for autonomous LLM workflows. All listed types are **fully supported** with automatic field descriptions.
|
|
10
|
+
|
|
11
|
+
## Basic Types
|
|
12
|
+
|
|
13
|
+
| Sorbet Type | BAML Output | Example |
|
|
14
|
+
|-------------|-------------|---------|
|
|
15
|
+
| `String` | `string` | `name string` |
|
|
16
|
+
| `Integer` | `int` | `age int` |
|
|
17
|
+
| `Float` | `float` | `price float` |
|
|
18
|
+
| `T::Boolean` | `bool` | `active bool` |
|
|
19
|
+
| `NilClass` | `null` | `null` |
|
|
20
|
+
| `Symbol` | `string` | `status string` |
|
|
21
|
+
| `Date/DateTime/Time` | `string` | `created_at string` |
|
|
22
|
+
|
|
23
|
+
## Optional Types (T.nilable)
|
|
24
|
+
|
|
25
|
+
| Sorbet Type | BAML Output | Example |
|
|
26
|
+
|-------------|-------------|---------|
|
|
27
|
+
| `T.nilable(String)` | `string?` | `email string?` |
|
|
28
|
+
| `T.nilable(Integer)` | `int?` | `age int?` |
|
|
29
|
+
| `T.nilable(MyStruct)` | `MyStruct?` | `address Address?` |
|
|
30
|
+
|
|
31
|
+
## Collection Types
|
|
32
|
+
|
|
33
|
+
| Sorbet Type | BAML Output | Example |
|
|
34
|
+
|-------------|-------------|---------|
|
|
35
|
+
| `T::Array[String]` | `string[]` | `tags string[]` |
|
|
36
|
+
| `T::Array[Integer]` | `int[]` | `scores int[]` |
|
|
37
|
+
| `T::Array[MyStruct]` | `MyStruct[]` | `addresses Address[]` |
|
|
38
|
+
|
|
39
|
+
## Hash/Map Types
|
|
40
|
+
|
|
41
|
+
| Sorbet Type | BAML Output | Example |
|
|
42
|
+
|-------------|-------------|---------|
|
|
43
|
+
| `T::Hash[String, String]` | `map<string, string>` | `metadata map<string, string>` |
|
|
44
|
+
| `T::Hash[String, Integer]` | `map<string, int>` | `counts map<string, int>` |
|
|
45
|
+
| `T::Hash[Symbol, String]` | `map<string, string>` | `config map<string, string>` |
|
|
46
|
+
|
|
47
|
+
## Union Types (T.any)
|
|
48
|
+
|
|
49
|
+
| Sorbet Type | BAML Output | Example |
|
|
50
|
+
|-------------|-------------|---------|
|
|
51
|
+
| `T.any(String, Integer)` | `string \| int` | `value string \| int` |
|
|
52
|
+
| `T.any(String, Integer, Float)` | `string \| int \| float` | `mixed string \| int \| float` |
|
|
53
|
+
| `T.nilable(T.any(String, Integer))` | `(string \| int)?` | `optional (string \| int)?` |
|
|
54
|
+
|
|
55
|
+
## Complex Collection Types
|
|
56
|
+
|
|
57
|
+
| Sorbet Type | BAML Output | Example |
|
|
58
|
+
|-------------|-------------|---------|
|
|
59
|
+
| `T::Array[T.any(String, Integer)]` | `(string \| int)[]` | `mixed_array (string \| int)[]` |
|
|
60
|
+
| `T::Hash[String, T.any(String, Integer)]` | `map<string, string \| int>` | `settings map<string, string \| int>` |
|
|
61
|
+
| `T::Hash[String, T::Array[String]]` | `map<string, string[]>` | `labels map<string, string[]>` |
|
|
62
|
+
|
|
63
|
+
## Structured Types
|
|
64
|
+
|
|
65
|
+
### T::Struct to BAML Classes (Research Workflow Example)
|
|
66
|
+
|
|
67
|
+
```ruby
|
|
68
|
+
class ConfidenceLevel < T::Enum
|
|
69
|
+
enums do
|
|
70
|
+
# Low confidence, requires further verification
|
|
71
|
+
Low = new('low')
|
|
72
|
+
# High confidence, strongly supported by evidence
|
|
73
|
+
High = new('high')
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
class ResearchFindings < T::Struct
|
|
78
|
+
# Detailed research findings and analysis
|
|
79
|
+
const :findings, String
|
|
80
|
+
# Key actionable insights extracted
|
|
81
|
+
const :key_insights, T::Array[String]
|
|
82
|
+
# Assessment of evidence quality
|
|
83
|
+
const :evidence_quality, ConfidenceLevel
|
|
84
|
+
# Confidence score (1-10 scale)
|
|
85
|
+
const :confidence_score, Integer
|
|
86
|
+
end
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
```ruby
|
|
90
|
+
ResearchFindings.to_baml
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Generated BAML:**
|
|
94
|
+
```baml
|
|
95
|
+
enum ConfidenceLevel {
|
|
96
|
+
"low" @description("Low confidence, requires further verification")
|
|
97
|
+
"high" @description("High confidence, strongly supported by evidence")
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
class ResearchFindings {
|
|
101
|
+
findings string @description("Detailed research findings and analysis")
|
|
102
|
+
key_insights string[] @description("Key actionable insights extracted")
|
|
103
|
+
evidence_quality ConfidenceLevel @description("Assessment of evidence quality")
|
|
104
|
+
confidence_score int @description("Confidence score (1-10 scale)")
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### T::Enum to BAML Enums (Task Classification)
|
|
109
|
+
|
|
110
|
+
```ruby
|
|
111
|
+
class TaskType < T::Enum
|
|
112
|
+
enums do
|
|
113
|
+
# Literature review and information gathering
|
|
114
|
+
Research = new('research')
|
|
115
|
+
# Data analysis and statistical interpretation
|
|
116
|
+
Analysis = new('analysis')
|
|
117
|
+
# Combining multiple sources into coherent insights
|
|
118
|
+
Synthesis = new('synthesis')
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
class ResearchTask < T::Struct
|
|
123
|
+
# Clear description of the research objective
|
|
124
|
+
const :objective, String
|
|
125
|
+
# Type of research task to be performed
|
|
126
|
+
const :task_type, TaskType
|
|
127
|
+
end
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
```ruby
|
|
131
|
+
[TaskType, ResearchTask].map(&:to_baml).join("\n\n")
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**Generated BAML:**
|
|
135
|
+
```baml
|
|
136
|
+
enum TaskType {
|
|
137
|
+
"research" @description("Literature review and information gathering")
|
|
138
|
+
"analysis" @description("Data analysis and statistical interpretation")
|
|
139
|
+
"synthesis" @description("Combining multiple sources into coherent insights")
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
class ResearchTask {
|
|
143
|
+
objective string @description("Clear description of the research objective")
|
|
144
|
+
task_type TaskType @description("Type of research task to be performed")
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Complex Real-World Example (Autonomous Research Agent)
|
|
149
|
+
|
|
150
|
+
```ruby
|
|
151
|
+
class ComplexityLevel < T::Enum
|
|
152
|
+
enums do
|
|
153
|
+
# Basic analysis requiring straightforward research
|
|
154
|
+
Basic = new('basic')
|
|
155
|
+
# Advanced analysis requiring deep domain expertise
|
|
156
|
+
Advanced = new('advanced')
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
class TaskDecomposition < T::Struct
|
|
161
|
+
# The main research topic being investigated
|
|
162
|
+
const :research_topic, String
|
|
163
|
+
# Target complexity level for the decomposition
|
|
164
|
+
const :complexity_level, ComplexityLevel
|
|
165
|
+
# Autonomously generated list of research subtasks
|
|
166
|
+
const :subtasks, T::Array[String]
|
|
167
|
+
# Strategic priority rankings (1-5 scale) for each subtask
|
|
168
|
+
const :priority_order, T::Array[Integer]
|
|
169
|
+
# Task dependency relationships for optimal sequencing
|
|
170
|
+
const :dependencies, T::Array[String]
|
|
171
|
+
# Key-value metadata for agent coordination
|
|
172
|
+
const :agent_metadata, T::Hash[String, T.any(String, Integer)]
|
|
173
|
+
end
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
```ruby
|
|
177
|
+
[ComplexityLevel, TaskDecomposition].map(&:to_baml).join("\n\n")
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**Generated BAML:**
|
|
181
|
+
```baml
|
|
182
|
+
enum ComplexityLevel {
|
|
183
|
+
"basic" @description("Basic analysis requiring straightforward research")
|
|
184
|
+
"advanced" @description("Advanced analysis requiring deep domain expertise")
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
class TaskDecomposition {
|
|
188
|
+
research_topic string @description("The main research topic being investigated")
|
|
189
|
+
complexity_level ComplexityLevel @description("Target complexity level for the decomposition")
|
|
190
|
+
subtasks string[] @description("Autonomously generated list of research subtasks")
|
|
191
|
+
priority_order int[] @description("Strategic priority rankings (1-5 scale) for each subtask")
|
|
192
|
+
dependencies string[] @description("Task dependency relationships for optimal sequencing")
|
|
193
|
+
agent_metadata map<string, string | int> @description("Key-value metadata for agent coordination")
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Advanced Features
|
|
198
|
+
|
|
199
|
+
### Dependency Management
|
|
200
|
+
|
|
201
|
+
```ruby
|
|
202
|
+
# Dependencies automatically included with smart defaults
|
|
203
|
+
TaskDecomposition.to_baml
|
|
204
|
+
# Outputs ComplexityLevel enum, then TaskDecomposition class in correct order
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Field Descriptions (Included by Default)
|
|
208
|
+
|
|
209
|
+
```ruby
|
|
210
|
+
# Smart defaults extract field descriptions from comments
|
|
211
|
+
TaskDecomposition.to_baml
|
|
212
|
+
# Outputs BAML with @description() annotations for LLM context
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Custom Formatting
|
|
216
|
+
|
|
217
|
+
```ruby
|
|
218
|
+
# Smart defaults include dependencies and descriptions automatically
|
|
219
|
+
TaskDecomposition.to_baml(indent_size: 4)
|
|
220
|
+
# Disable features if needed:
|
|
221
|
+
TaskDecomposition.to_baml(include_descriptions: false)
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## ✅ Completed Features
|
|
225
|
+
|
|
226
|
+
- ✅ `T::Struct` → `class Name { ... }` with field descriptions
|
|
227
|
+
- ✅ `T::Enum` → `enum Name { ... }` with value descriptions
|
|
228
|
+
- ✅ Automatic dependency resolution and ordering
|
|
229
|
+
- ✅ Smart defaults (descriptions and dependencies enabled)
|
|
230
|
+
- ✅ Full type safety with Sorbet type checking
|
|
231
|
+
|
|
232
|
+
## Future Enhancements (Optional)
|
|
233
|
+
|
|
234
|
+
- `T.type_alias` → `type Name = ...`
|
|
235
|
+
- Custom naming strategies (snake_case ↔ camelCase)
|
|
236
|
+
- Self-referential type handling
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/** @type {import('tailwindcss').Config} */
|
|
2
|
+
module.exports = {
|
|
3
|
+
content: [
|
|
4
|
+
"./src/**/*.{html,md,liquid,erb}",
|
|
5
|
+
"./frontend/**/*.js",
|
|
6
|
+
],
|
|
7
|
+
darkMode: 'media',
|
|
8
|
+
theme: {
|
|
9
|
+
extend: {
|
|
10
|
+
typography: {
|
|
11
|
+
DEFAULT: {
|
|
12
|
+
css: {
|
|
13
|
+
maxWidth: 'none',
|
|
14
|
+
color: 'inherit',
|
|
15
|
+
a: {
|
|
16
|
+
color: '#3b82f6',
|
|
17
|
+
textDecoration: 'underline',
|
|
18
|
+
fontWeight: '500',
|
|
19
|
+
},
|
|
20
|
+
'[class~="lead"]': {
|
|
21
|
+
color: 'inherit',
|
|
22
|
+
},
|
|
23
|
+
strong: {
|
|
24
|
+
color: 'inherit',
|
|
25
|
+
},
|
|
26
|
+
'ol > li::before': {
|
|
27
|
+
color: 'inherit',
|
|
28
|
+
},
|
|
29
|
+
'ul > li::before': {
|
|
30
|
+
backgroundColor: 'currentColor',
|
|
31
|
+
},
|
|
32
|
+
hr: {
|
|
33
|
+
borderColor: 'currentColor',
|
|
34
|
+
opacity: 0.3,
|
|
35
|
+
},
|
|
36
|
+
blockquote: {
|
|
37
|
+
color: 'inherit',
|
|
38
|
+
borderLeftColor: 'currentColor',
|
|
39
|
+
opacity: 0.8,
|
|
40
|
+
},
|
|
41
|
+
h1: {
|
|
42
|
+
color: 'inherit',
|
|
43
|
+
},
|
|
44
|
+
h2: {
|
|
45
|
+
color: 'inherit',
|
|
46
|
+
},
|
|
47
|
+
h3: {
|
|
48
|
+
color: 'inherit',
|
|
49
|
+
},
|
|
50
|
+
h4: {
|
|
51
|
+
color: 'inherit',
|
|
52
|
+
},
|
|
53
|
+
'figure figcaption': {
|
|
54
|
+
color: 'inherit',
|
|
55
|
+
},
|
|
56
|
+
code: {
|
|
57
|
+
color: 'inherit',
|
|
58
|
+
backgroundColor: 'rgba(156, 163, 175, 0.2)',
|
|
59
|
+
padding: '0.125rem 0.25rem',
|
|
60
|
+
borderRadius: '0.25rem',
|
|
61
|
+
fontWeight: '400',
|
|
62
|
+
},
|
|
63
|
+
'code::before': {
|
|
64
|
+
content: '""',
|
|
65
|
+
},
|
|
66
|
+
'code::after': {
|
|
67
|
+
content: '""',
|
|
68
|
+
},
|
|
69
|
+
pre: {
|
|
70
|
+
backgroundColor: '#1f2937',
|
|
71
|
+
color: '#f9fafb',
|
|
72
|
+
},
|
|
73
|
+
'pre code': {
|
|
74
|
+
backgroundColor: 'transparent',
|
|
75
|
+
color: 'inherit',
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
plugins: [
|
|
83
|
+
require('@tailwindcss/typography'),
|
|
84
|
+
],
|
|
85
|
+
}
|
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
|
|
4
4
|
require_relative '../lib/sorbet_baml'
|
|
5
5
|
|
|
6
|
-
puts
|
|
7
|
-
puts
|
|
6
|
+
puts '🎯 Description Parameter Support Demo'
|
|
7
|
+
puts '=' * 50
|
|
8
8
|
|
|
9
9
|
# Example 1: Basic description parameters
|
|
10
10
|
class User < T::Struct
|
|
11
11
|
const :name, String, description: "User's full legal name"
|
|
12
|
-
prop :age, Integer, description:
|
|
13
|
-
const :email, T.nilable(String), description:
|
|
14
|
-
const :interests, T::Array[String], description:
|
|
12
|
+
prop :age, Integer, description: 'Age in years'
|
|
13
|
+
const :email, T.nilable(String), description: 'Optional email address for notifications'
|
|
14
|
+
const :interests, T::Array[String], description: 'List of user hobbies and interests'
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
puts "\n1. Basic T::Struct with description parameters:"
|
|
@@ -21,13 +21,13 @@ puts User.to_baml
|
|
|
21
21
|
class Product < T::Struct
|
|
22
22
|
# This comment will be used as fallback
|
|
23
23
|
const :id, String
|
|
24
|
-
|
|
25
|
-
const :name, String, description:
|
|
26
|
-
|
|
24
|
+
|
|
25
|
+
const :name, String, description: 'Product name for display'
|
|
26
|
+
|
|
27
27
|
# Price in USD cents
|
|
28
28
|
prop :price_cents, Integer
|
|
29
|
-
|
|
30
|
-
const :category, String, description:
|
|
29
|
+
|
|
30
|
+
const :category, String, description: 'Product category classification'
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
puts "\n2. Mixed description sources (parameters take priority):"
|
|
@@ -35,15 +35,15 @@ puts Product.to_baml
|
|
|
35
35
|
|
|
36
36
|
# Example 3: Complex nested types with descriptions
|
|
37
37
|
class Order < T::Struct
|
|
38
|
-
const :id, String, description:
|
|
39
|
-
const :customer, User, description:
|
|
40
|
-
const :items, T::Array[Product], description:
|
|
41
|
-
const :total_cents, Integer, description:
|
|
42
|
-
const :status, String, description:
|
|
38
|
+
const :id, String, description: 'Unique order identifier'
|
|
39
|
+
const :customer, User, description: 'Customer who placed the order'
|
|
40
|
+
const :items, T::Array[Product], description: 'List of ordered products'
|
|
41
|
+
const :total_cents, Integer, description: 'Total order value in USD cents'
|
|
42
|
+
const :status, String, description: 'Current order processing status'
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
puts "\n3. Complex nested types with dependencies:"
|
|
46
46
|
puts Order.to_baml
|
|
47
47
|
|
|
48
48
|
puts "\n✨ Beautiful, readable, and LLM-friendly!"
|
|
49
|
-
puts
|
|
49
|
+
puts '🚀 Perfect for DSPy.rb, autonomous agents, and structured LLM outputs'
|
|
@@ -10,67 +10,63 @@ module SorbetBaml
|
|
|
10
10
|
def self.extract_field_comments(klass)
|
|
11
11
|
# First try to get descriptions from the description extractor (extra field)
|
|
12
12
|
descriptions = DescriptionExtractor.extract_prop_descriptions(klass)
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
# Then fall back to comment-based extraction for any missing descriptions
|
|
15
15
|
comments = {}
|
|
16
16
|
source_file = find_source_file(klass)
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
if source_file && File.exist?(source_file)
|
|
19
19
|
lines = File.readlines(source_file)
|
|
20
20
|
extract_comments_from_lines(lines, T.must(T.must(klass.name).split('::').last), comments)
|
|
21
21
|
end
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
# Merge with priority: description parameters > comments
|
|
24
|
-
descriptions.merge(comments) { |
|
|
24
|
+
descriptions.merge(comments) { |_key, desc_param, _comment| desc_param }
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
sig { params(klass: T.class_of(T::Enum)).returns(T::Hash[String, T.nilable(String)]) }
|
|
28
28
|
def self.extract_enum_comments(klass)
|
|
29
29
|
comments = {}
|
|
30
30
|
source_file = find_source_file(klass)
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
return comments unless source_file && File.exist?(source_file)
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
lines = File.readlines(source_file)
|
|
35
35
|
extract_enum_comments_from_lines(lines, T.must(T.must(klass.name).split('::').last), comments)
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
comments
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
private
|
|
41
|
-
|
|
42
40
|
sig { params(klass: T::Class[T.anything]).returns(T.nilable(String)) }
|
|
43
41
|
def self.find_source_file(klass)
|
|
44
42
|
# Try to find where the class was defined
|
|
45
43
|
# This is a heuristic approach since Ruby doesn't provide reliable source location for classes
|
|
46
|
-
|
|
44
|
+
|
|
47
45
|
# Method 1: Check if any methods have source location
|
|
48
46
|
begin
|
|
49
47
|
if klass.respond_to?(:new) && klass.method(:new).respond_to?(:source_location)
|
|
50
48
|
location = klass.method(:new).source_location
|
|
51
49
|
return location[0] if location
|
|
52
50
|
end
|
|
53
|
-
rescue
|
|
51
|
+
rescue StandardError
|
|
54
52
|
# Ignore errors
|
|
55
53
|
end
|
|
56
|
-
|
|
54
|
+
|
|
57
55
|
# Method 2: Look at the current call stack for files that might contain the class
|
|
58
56
|
caller_locations.each do |location|
|
|
59
57
|
file_path = location.absolute_path || location.path
|
|
60
58
|
next unless file_path && File.exist?(file_path)
|
|
61
|
-
|
|
59
|
+
|
|
62
60
|
# Read the file and check if it contains the class definition
|
|
63
61
|
begin
|
|
64
62
|
content = File.read(file_path)
|
|
65
63
|
class_name = T.must(klass.name).split('::').last
|
|
66
|
-
if content.match(/class\s+#{Regexp.escape(T.must(class_name))}\s*</)
|
|
67
|
-
|
|
68
|
-
end
|
|
69
|
-
rescue
|
|
64
|
+
return file_path if content.match(/class\s+#{Regexp.escape(T.must(class_name))}\s*</)
|
|
65
|
+
rescue StandardError
|
|
70
66
|
# Ignore file read errors
|
|
71
67
|
end
|
|
72
68
|
end
|
|
73
|
-
|
|
69
|
+
|
|
74
70
|
nil
|
|
75
71
|
end
|
|
76
72
|
|
|
@@ -79,28 +75,26 @@ module SorbetBaml
|
|
|
79
75
|
in_target_class = T.let(false, T::Boolean)
|
|
80
76
|
current_comment = T.let(nil, T.nilable(String))
|
|
81
77
|
brace_depth = 0
|
|
82
|
-
|
|
78
|
+
|
|
83
79
|
lines.each do |line|
|
|
84
80
|
stripped = line.strip
|
|
85
|
-
|
|
81
|
+
|
|
86
82
|
# Check if we're entering the target class
|
|
87
83
|
if stripped.match(/^class\s+#{Regexp.escape(class_name)}\s*<\s*T::Struct/)
|
|
88
84
|
in_target_class = true
|
|
89
85
|
brace_depth = 0
|
|
90
86
|
next
|
|
91
87
|
end
|
|
92
|
-
|
|
88
|
+
|
|
93
89
|
next unless in_target_class
|
|
94
|
-
|
|
90
|
+
|
|
95
91
|
# Track brace depth to handle nested classes
|
|
96
92
|
brace_depth += stripped.count('{')
|
|
97
93
|
brace_depth -= stripped.count('}')
|
|
98
|
-
|
|
94
|
+
|
|
99
95
|
# Exit when we reach the end of the class
|
|
100
|
-
if stripped == 'end' && brace_depth == 0
|
|
101
|
-
|
|
102
|
-
end
|
|
103
|
-
|
|
96
|
+
break if stripped == 'end' && brace_depth == 0
|
|
97
|
+
|
|
104
98
|
# Extract comment
|
|
105
99
|
if stripped.start_with?('#')
|
|
106
100
|
comment_text = T.must(stripped[1..-1]).strip
|
|
@@ -121,37 +115,35 @@ module SorbetBaml
|
|
|
121
115
|
in_target_class = T.let(false, T::Boolean)
|
|
122
116
|
in_enums_block = T.let(false, T::Boolean)
|
|
123
117
|
current_comment = T.let(nil, T.nilable(String))
|
|
124
|
-
|
|
118
|
+
|
|
125
119
|
lines.each do |line|
|
|
126
120
|
stripped = line.strip
|
|
127
|
-
|
|
121
|
+
|
|
128
122
|
# Check if we're entering the target enum class
|
|
129
123
|
if stripped.match(/^class\s+#{Regexp.escape(class_name)}\s*<\s*T::Enum/)
|
|
130
124
|
in_target_class = true
|
|
131
125
|
next
|
|
132
126
|
end
|
|
133
|
-
|
|
127
|
+
|
|
134
128
|
next unless in_target_class
|
|
135
|
-
|
|
129
|
+
|
|
136
130
|
# Check if we're in the enums block
|
|
137
131
|
if stripped == 'enums do'
|
|
138
132
|
in_enums_block = true
|
|
139
133
|
next
|
|
140
134
|
end
|
|
141
|
-
|
|
135
|
+
|
|
142
136
|
# Exit enums block
|
|
143
137
|
if in_enums_block && stripped == 'end'
|
|
144
138
|
in_enums_block = false
|
|
145
139
|
next
|
|
146
140
|
end
|
|
147
|
-
|
|
141
|
+
|
|
148
142
|
# Exit class
|
|
149
|
-
if stripped == 'end' && !in_enums_block
|
|
150
|
-
|
|
151
|
-
end
|
|
152
|
-
|
|
143
|
+
break if stripped == 'end' && !in_enums_block
|
|
144
|
+
|
|
153
145
|
next unless in_enums_block
|
|
154
|
-
|
|
146
|
+
|
|
155
147
|
# Extract comment
|
|
156
148
|
if stripped.start_with?('#')
|
|
157
149
|
comment_text = T.must(stripped[1..-1]).strip
|
|
@@ -167,4 +159,4 @@ module SorbetBaml
|
|
|
167
159
|
end
|
|
168
160
|
end
|
|
169
161
|
end
|
|
170
|
-
end
|
|
162
|
+
end
|