sorbet-baml 0.0.1 → 0.2.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/.idea/.gitignore +8 -0
- data/.idea/inspectionProfiles/Project_Default.xml +5 -0
- data/CLAUDE.md +94 -0
- data/README.md +491 -65
- data/examples/description_parameters.rb +49 -0
- data/lib/sorbet_baml/comment_extractor.rb +170 -0
- data/lib/sorbet_baml/converter.rb +153 -3
- data/lib/sorbet_baml/dependency_resolver.rb +99 -0
- data/lib/sorbet_baml/description_extension.rb +34 -0
- data/lib/sorbet_baml/description_extractor.rb +36 -0
- data/lib/sorbet_baml/enum_extensions.rb +23 -0
- data/lib/sorbet_baml/struct_extensions.rb +23 -0
- data/lib/sorbet_baml/type_mapper.rb +49 -12
- data/lib/sorbet_baml/version.rb +1 -1
- data/lib/sorbet_baml.rb +10 -0
- metadata +11 -6
- data/docs/README.md +0 -67
- data/docs/advanced-usage.md +0 -85
- data/docs/getting-started.md +0 -54
- data/docs/troubleshooting.md +0 -81
- data/docs/type-mapping.md +0 -65
data/lib/sorbet_baml.rb
CHANGED
|
@@ -5,6 +5,12 @@ require "sorbet-runtime"
|
|
|
5
5
|
require_relative "sorbet_baml/version"
|
|
6
6
|
require_relative "sorbet_baml/converter"
|
|
7
7
|
require_relative "sorbet_baml/type_mapper"
|
|
8
|
+
require_relative "sorbet_baml/dependency_resolver"
|
|
9
|
+
require_relative "sorbet_baml/comment_extractor"
|
|
10
|
+
require_relative "sorbet_baml/description_extractor"
|
|
11
|
+
require_relative "sorbet_baml/description_extension"
|
|
12
|
+
require_relative "sorbet_baml/struct_extensions"
|
|
13
|
+
require_relative "sorbet_baml/enum_extensions"
|
|
8
14
|
|
|
9
15
|
module SorbetBaml
|
|
10
16
|
class Error < StandardError; end
|
|
@@ -23,3 +29,7 @@ module SorbetBaml
|
|
|
23
29
|
Converter.from_structs(klasses, options)
|
|
24
30
|
end
|
|
25
31
|
end
|
|
32
|
+
|
|
33
|
+
# Extend T::Struct and T::Enum with BAML conversion methods
|
|
34
|
+
T::Struct.extend(SorbetBaml::StructExtensions)
|
|
35
|
+
T::Enum.extend(SorbetBaml::EnumExtensions)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sorbet-baml
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Vicente Reig Rincon de Arellano
|
|
@@ -32,18 +32,23 @@ executables: []
|
|
|
32
32
|
extensions: []
|
|
33
33
|
extra_rdoc_files: []
|
|
34
34
|
files:
|
|
35
|
+
- ".idea/.gitignore"
|
|
36
|
+
- ".idea/inspectionProfiles/Project_Default.xml"
|
|
35
37
|
- ".rspec"
|
|
36
38
|
- CHANGELOG.md
|
|
39
|
+
- CLAUDE.md
|
|
37
40
|
- LICENSE.txt
|
|
38
41
|
- README.md
|
|
39
42
|
- Rakefile
|
|
40
|
-
-
|
|
41
|
-
- docs/advanced-usage.md
|
|
42
|
-
- docs/getting-started.md
|
|
43
|
-
- docs/troubleshooting.md
|
|
44
|
-
- docs/type-mapping.md
|
|
43
|
+
- examples/description_parameters.rb
|
|
45
44
|
- lib/sorbet_baml.rb
|
|
45
|
+
- lib/sorbet_baml/comment_extractor.rb
|
|
46
46
|
- lib/sorbet_baml/converter.rb
|
|
47
|
+
- lib/sorbet_baml/dependency_resolver.rb
|
|
48
|
+
- lib/sorbet_baml/description_extension.rb
|
|
49
|
+
- lib/sorbet_baml/description_extractor.rb
|
|
50
|
+
- lib/sorbet_baml/enum_extensions.rb
|
|
51
|
+
- lib/sorbet_baml/struct_extensions.rb
|
|
47
52
|
- lib/sorbet_baml/type_mapper.rb
|
|
48
53
|
- lib/sorbet_baml/version.rb
|
|
49
54
|
- sig/sorbet/baml.rbs
|
data/docs/README.md
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
# sorbet-baml Documentation
|
|
2
|
-
|
|
3
|
-
Developer documentation for the sorbet-baml gem.
|
|
4
|
-
|
|
5
|
-
## For Users
|
|
6
|
-
|
|
7
|
-
If you want to use this gem in your project:
|
|
8
|
-
|
|
9
|
-
1. **[Getting Started](./getting-started.md)** - Installation and basic usage
|
|
10
|
-
2. **[Type Mapping Reference](./type-mapping.md)** - Complete type conversion table
|
|
11
|
-
3. **[Advanced Usage](./advanced-usage.md)** - Complex scenarios and integrations
|
|
12
|
-
4. **[Troubleshooting](./troubleshooting.md)** - Common issues and solutions
|
|
13
|
-
|
|
14
|
-
## For Contributors
|
|
15
|
-
|
|
16
|
-
If you want to contribute to this gem:
|
|
17
|
-
|
|
18
|
-
1. **[Architecture](./architecture.md)** - How the gem is structured
|
|
19
|
-
2. **[Adding Type Support](./adding-types.md)** - How to add new type mappings
|
|
20
|
-
3. **[Testing Guide](./testing.md)** - How to write and run tests
|
|
21
|
-
|
|
22
|
-
## Quick Example
|
|
23
|
-
|
|
24
|
-
```ruby
|
|
25
|
-
# Define a Sorbet struct
|
|
26
|
-
class User < T::Struct
|
|
27
|
-
const :name, String
|
|
28
|
-
const :age, Integer
|
|
29
|
-
const :email, T.nilable(String)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# Convert to BAML
|
|
33
|
-
require 'sorbet-baml'
|
|
34
|
-
puts SorbetBaml.from_struct(User)
|
|
35
|
-
|
|
36
|
-
# Output:
|
|
37
|
-
# class User {
|
|
38
|
-
# name string
|
|
39
|
-
# age int
|
|
40
|
-
# email string?
|
|
41
|
-
# }
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
## Design Goals
|
|
45
|
-
|
|
46
|
-
1. **Simplicity** - Easy to understand and use
|
|
47
|
-
2. **Accuracy** - Correct type mappings
|
|
48
|
-
3. **Efficiency** - Minimal token usage in output
|
|
49
|
-
4. **Compatibility** - Works with existing Sorbet codebases
|
|
50
|
-
|
|
51
|
-
## What This Is Not
|
|
52
|
-
|
|
53
|
-
- Not a BAML runtime or executor
|
|
54
|
-
- Not a JSON Schema generator (use sorbet-schema for that)
|
|
55
|
-
- Not a Sorbet type checker
|
|
56
|
-
- Not a serialization library
|
|
57
|
-
|
|
58
|
-
## Why BAML?
|
|
59
|
-
|
|
60
|
-
BAML (Boundary AI Markup Language) provides a concise way to define types for LLM consumption. Compared to JSON Schema:
|
|
61
|
-
|
|
62
|
-
- ~60% fewer tokens
|
|
63
|
-
- More readable
|
|
64
|
-
- Better LLM comprehension
|
|
65
|
-
- Simpler syntax
|
|
66
|
-
|
|
67
|
-
Perfect for prompt engineering and structured output generation.
|
data/docs/advanced-usage.md
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
# Advanced Usage
|
|
2
|
-
|
|
3
|
-
## Converting Multiple Structs
|
|
4
|
-
|
|
5
|
-
When you have related structs, convert them together to maintain relationships:
|
|
6
|
-
|
|
7
|
-
```ruby
|
|
8
|
-
class Address < T::Struct
|
|
9
|
-
const :street, String
|
|
10
|
-
const :city, String
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
class Company < T::Struct
|
|
14
|
-
const :name, String
|
|
15
|
-
const :address, Address
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
class User < T::Struct
|
|
19
|
-
const :name, String
|
|
20
|
-
const :company, Company
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
# Convert all at once
|
|
24
|
-
baml = SorbetBaml.from_structs([Address, Company, User])
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## Options
|
|
28
|
-
|
|
29
|
-
### Include Descriptions
|
|
30
|
-
|
|
31
|
-
```ruby
|
|
32
|
-
# Future feature - not yet implemented
|
|
33
|
-
baml = SorbetBaml.from_struct(User,
|
|
34
|
-
include_descriptions: true # Will add @description annotations
|
|
35
|
-
)
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
### Custom Indentation
|
|
39
|
-
|
|
40
|
-
```ruby
|
|
41
|
-
# Future feature - not yet implemented
|
|
42
|
-
baml = SorbetBaml.from_struct(User,
|
|
43
|
-
indent_size: 4 # Use 4 spaces instead of 2
|
|
44
|
-
)
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## Working with Existing BAML Projects
|
|
48
|
-
|
|
49
|
-
If you're already using BAML, you can generate type definitions to include in your `.baml` files:
|
|
50
|
-
|
|
51
|
-
```ruby
|
|
52
|
-
# Generate just the class definition
|
|
53
|
-
definition = SorbetBaml.from_struct(MyStruct)
|
|
54
|
-
|
|
55
|
-
# Write to a BAML file
|
|
56
|
-
File.write("types/my_struct.baml", definition)
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
## Integration with LLM Libraries
|
|
60
|
-
|
|
61
|
-
### With OpenAI
|
|
62
|
-
|
|
63
|
-
```ruby
|
|
64
|
-
require 'openai'
|
|
65
|
-
require 'sorbet-baml'
|
|
66
|
-
|
|
67
|
-
schema = SorbetBaml.from_struct(ResponseFormat)
|
|
68
|
-
|
|
69
|
-
response = client.chat(
|
|
70
|
-
model: "gpt-4",
|
|
71
|
-
messages: [{
|
|
72
|
-
role: "system",
|
|
73
|
-
content: "You must respond with data matching this BAML schema:\n\n#{schema}"
|
|
74
|
-
}, {
|
|
75
|
-
role: "user",
|
|
76
|
-
content: "Generate a sample user"
|
|
77
|
-
}]
|
|
78
|
-
)
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
### With DSPy.rb
|
|
82
|
-
|
|
83
|
-
```ruby
|
|
84
|
-
# Coming soon - integration with DSPy.rb for automatic schema generation
|
|
85
|
-
```
|
data/docs/getting-started.md
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
# Getting Started
|
|
2
|
-
|
|
3
|
-
## Prerequisites
|
|
4
|
-
|
|
5
|
-
- Ruby 3.2+
|
|
6
|
-
- Sorbet installed in your project
|
|
7
|
-
- Basic familiarity with T::Struct
|
|
8
|
-
|
|
9
|
-
## Quick Start
|
|
10
|
-
|
|
11
|
-
### 1. Define your Sorbet types
|
|
12
|
-
|
|
13
|
-
```ruby
|
|
14
|
-
class User < T::Struct
|
|
15
|
-
const :id, Integer
|
|
16
|
-
const :name, String
|
|
17
|
-
const :email, T.nilable(String)
|
|
18
|
-
end
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
### 2. Convert to BAML
|
|
22
|
-
|
|
23
|
-
```ruby
|
|
24
|
-
require 'sorbet-baml'
|
|
25
|
-
|
|
26
|
-
baml = SorbetBaml.from_struct(User)
|
|
27
|
-
puts baml
|
|
28
|
-
# Output:
|
|
29
|
-
# class User {
|
|
30
|
-
# id int
|
|
31
|
-
# name string
|
|
32
|
-
# email string?
|
|
33
|
-
# }
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
### 3. Use with your LLM
|
|
37
|
-
|
|
38
|
-
Include the BAML definition in your prompt:
|
|
39
|
-
|
|
40
|
-
```ruby
|
|
41
|
-
prompt = <<~PROMPT
|
|
42
|
-
Generate sample data matching this schema:
|
|
43
|
-
|
|
44
|
-
#{baml}
|
|
45
|
-
|
|
46
|
-
Return 3 examples.
|
|
47
|
-
PROMPT
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
## Next Steps
|
|
51
|
-
|
|
52
|
-
- [Type Mapping Reference](./type-mapping.md)
|
|
53
|
-
- [Advanced Usage](./advanced-usage.md)
|
|
54
|
-
- [Troubleshooting](./troubleshooting.md)
|
data/docs/troubleshooting.md
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
# Troubleshooting
|
|
2
|
-
|
|
3
|
-
## Common Issues
|
|
4
|
-
|
|
5
|
-
### "undefined method `props' for Class"
|
|
6
|
-
|
|
7
|
-
**Problem**: The class you're trying to convert is not a T::Struct.
|
|
8
|
-
|
|
9
|
-
**Solution**: Ensure your class inherits from `T::Struct`:
|
|
10
|
-
|
|
11
|
-
```ruby
|
|
12
|
-
# ❌ Wrong
|
|
13
|
-
class User
|
|
14
|
-
attr_reader :name
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
# ✅ Correct
|
|
18
|
-
class User < T::Struct
|
|
19
|
-
const :name, String
|
|
20
|
-
end
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
### Empty output
|
|
24
|
-
|
|
25
|
-
**Problem**: The struct has no properties defined.
|
|
26
|
-
|
|
27
|
-
**Solution**: Define at least one property using `const` or `prop`:
|
|
28
|
-
|
|
29
|
-
```ruby
|
|
30
|
-
class User < T::Struct
|
|
31
|
-
const :name, String # Add properties
|
|
32
|
-
end
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
### Circular dependency detected
|
|
36
|
-
|
|
37
|
-
**Problem**: Two structs reference each other creating an infinite loop.
|
|
38
|
-
|
|
39
|
-
**Solution**: This is not yet supported. Consider flattening the structure or using a different approach.
|
|
40
|
-
|
|
41
|
-
## Type-Specific Issues
|
|
42
|
-
|
|
43
|
-
### Arrays not converting correctly
|
|
44
|
-
|
|
45
|
-
Ensure you're using the Sorbet array syntax:
|
|
46
|
-
|
|
47
|
-
```ruby
|
|
48
|
-
# ❌ Wrong
|
|
49
|
-
const :items, Array
|
|
50
|
-
|
|
51
|
-
# ✅ Correct
|
|
52
|
-
const :items, T::Array[String]
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
### Optional fields showing as required
|
|
56
|
-
|
|
57
|
-
Make sure to use `T.nilable`:
|
|
58
|
-
|
|
59
|
-
```ruby
|
|
60
|
-
# ❌ Wrong - will be required
|
|
61
|
-
const :email, String
|
|
62
|
-
|
|
63
|
-
# ✅ Correct - will be optional
|
|
64
|
-
const :email, T.nilable(String)
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
## Getting Help
|
|
68
|
-
|
|
69
|
-
1. Check the [Type Mapping Reference](./type-mapping.md)
|
|
70
|
-
2. Review the examples in [Getting Started](./getting-started.md)
|
|
71
|
-
3. File an issue at https://github.com/vicentereig/sorbet-baml/issues
|
|
72
|
-
|
|
73
|
-
## Debug Mode
|
|
74
|
-
|
|
75
|
-
To see detailed conversion information:
|
|
76
|
-
|
|
77
|
-
```ruby
|
|
78
|
-
# Future feature - not yet implemented
|
|
79
|
-
SorbetBaml.debug = true
|
|
80
|
-
baml = SorbetBaml.from_struct(MyStruct)
|
|
81
|
-
```
|
data/docs/type-mapping.md
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
# Type Mapping Reference
|
|
2
|
-
|
|
3
|
-
Complete mapping between Sorbet types and BAML output.
|
|
4
|
-
|
|
5
|
-
## Basic Types
|
|
6
|
-
|
|
7
|
-
| Sorbet Type | BAML Output | Example |
|
|
8
|
-
|-------------|-------------|---------|
|
|
9
|
-
| `String` | `string` | `name string` |
|
|
10
|
-
| `Integer` | `int` | `age int` |
|
|
11
|
-
| `Float` | `float` | `price float` |
|
|
12
|
-
| `T::Boolean` | `bool` | `active bool` |
|
|
13
|
-
| `NilClass` | `null` | `null` |
|
|
14
|
-
| `Symbol` | `string` | `status string` |
|
|
15
|
-
|
|
16
|
-
## Optional Types
|
|
17
|
-
|
|
18
|
-
| Sorbet Type | BAML Output | Example |
|
|
19
|
-
|-------------|-------------|---------|
|
|
20
|
-
| `T.nilable(String)` | `string?` | `email string?` |
|
|
21
|
-
| `T.nilable(Integer)` | `int?` | `age int?` |
|
|
22
|
-
|
|
23
|
-
## Collection Types
|
|
24
|
-
|
|
25
|
-
| Sorbet Type | BAML Output | Example |
|
|
26
|
-
|-------------|-------------|---------|
|
|
27
|
-
| `T::Array[String]` | `string[]` | `tags string[]` |
|
|
28
|
-
| `T::Array[T::Struct]` | `StructName[]` | `addresses Address[]` |
|
|
29
|
-
|
|
30
|
-
## Nested Structures
|
|
31
|
-
|
|
32
|
-
```ruby
|
|
33
|
-
# Input
|
|
34
|
-
class Address < T::Struct
|
|
35
|
-
const :street, String
|
|
36
|
-
const :city, String
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
class User < T::Struct
|
|
40
|
-
const :name, String
|
|
41
|
-
const :address, Address
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
# Output
|
|
45
|
-
class Address {
|
|
46
|
-
street string
|
|
47
|
-
city string
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
class User {
|
|
51
|
-
name string
|
|
52
|
-
address Address
|
|
53
|
-
}
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## Not Yet Supported
|
|
57
|
-
|
|
58
|
-
These types will be added in future versions:
|
|
59
|
-
|
|
60
|
-
- `T::Hash[K, V]` → `map<K, V>`
|
|
61
|
-
- `T.any(Type1, Type2)` → `Type1 | Type2`
|
|
62
|
-
- `T::Enum` subclasses → `enum Name { ... }`
|
|
63
|
-
- `T.type_alias` → `type Name = ...`
|
|
64
|
-
- `T::Set[T]` → `T[]` (with uniqueness note)
|
|
65
|
-
- `T::Range[T]` → Will need special handling
|