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,27 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
{% render "head", title: title, description: description %}
|
|
5
|
+
</head>
|
|
6
|
+
<body class="bg-gray-50 min-h-screen">
|
|
7
|
+
{% render "nav" %}
|
|
8
|
+
|
|
9
|
+
<main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
|
10
|
+
{{ content }}
|
|
11
|
+
</main>
|
|
12
|
+
|
|
13
|
+
<footer class="bg-white border-t border-gray-200 mt-16">
|
|
14
|
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
|
15
|
+
<div class="text-center text-gray-600">
|
|
16
|
+
<p>© 2025 <a href="mailto:hey@vicente.services" class="text-blue-600 hover:text-blue-800">Vicente Reig</a>. MIT License.</p>
|
|
17
|
+
<p class="mt-2">
|
|
18
|
+
<a href="https://github.com/vicentereig/sorbet-baml" class="text-blue-600 hover:text-blue-800">GitHub</a> •
|
|
19
|
+
<a href="https://rubygems.org/gems/sorbet-baml" class="text-blue-600 hover:text-blue-800">RubyGems</a>
|
|
20
|
+
</p>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
</footer>
|
|
24
|
+
|
|
25
|
+
<script src="{% asset_path js %}" defer></script>
|
|
26
|
+
</body>
|
|
27
|
+
</html>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
<div class="lg:grid lg:grid-cols-4 lg:gap-8">
|
|
6
|
+
<!-- Sidebar navigation -->
|
|
7
|
+
<div class="lg:col-span-1">
|
|
8
|
+
<div class="sticky top-20">
|
|
9
|
+
<nav class="space-y-1">
|
|
10
|
+
<h3 class="text-lg font-semibold text-gray-900 mb-4">Documentation</h3>
|
|
11
|
+
<a href="{{ '/getting-started/' | relative_url }}" class="nav-link {% if page.url contains 'getting-started' %}active{% endif %}">
|
|
12
|
+
Getting Started
|
|
13
|
+
</a>
|
|
14
|
+
<a href="{{ '/type-mapping/' | relative_url }}" class="nav-link {% if page.url contains 'type-mapping' %}active{% endif %}">
|
|
15
|
+
Type Mapping
|
|
16
|
+
</a>
|
|
17
|
+
<a href="{{ '/advanced-usage/' | relative_url }}" class="nav-link {% if page.url contains 'advanced-usage' %}active{% endif %}">
|
|
18
|
+
Advanced Usage
|
|
19
|
+
</a>
|
|
20
|
+
<a href="{{ '/troubleshooting/' | relative_url }}" class="nav-link {% if page.url contains 'troubleshooting' %}active{% endif %}">
|
|
21
|
+
Troubleshooting
|
|
22
|
+
</a>
|
|
23
|
+
</nav>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<!-- Main content -->
|
|
28
|
+
<div class="lg:col-span-3 mt-8 lg:mt-0">
|
|
29
|
+
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-8">
|
|
30
|
+
{% if title %}
|
|
31
|
+
<h1 class="text-3xl font-bold text-gray-900 mb-6">{{ title }}</h1>
|
|
32
|
+
{% endif %}
|
|
33
|
+
|
|
34
|
+
<div class="prose prose-lg max-w-none">
|
|
35
|
+
{{ content }}
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: doc
|
|
3
|
+
title: "Advanced Usage"
|
|
4
|
+
description: "Advanced features including dependency management, field descriptions, custom formatting, and complex type scenarios."
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Advanced Usage
|
|
8
|
+
|
|
9
|
+
## Ruby-Idiomatic API
|
|
10
|
+
|
|
11
|
+
The gem automatically extends all T::Struct and T::Enum classes with conversion methods:
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
# Define complex autonomous research workflow types
|
|
15
|
+
class ResearchAgent < T::Struct
|
|
16
|
+
# Agent's specialized domain of expertise
|
|
17
|
+
const :domain_expertise, String
|
|
18
|
+
# Current confidence level in assigned research
|
|
19
|
+
const :confidence_level, Integer
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Ruby-idiomatic - just call the method!
|
|
23
|
+
ResearchAgent.to_baml
|
|
24
|
+
ResearchAgent.baml_type_definition # Same as to_baml
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Automatic Dependency Management
|
|
28
|
+
|
|
29
|
+
The most powerful feature is automatic dependency resolution:
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
# Autonomous research workflow with complex dependencies
|
|
33
|
+
class TaskType < T::Enum
|
|
34
|
+
enums do
|
|
35
|
+
# Literature review and information gathering
|
|
36
|
+
Research = new('research')
|
|
37
|
+
# Combining multiple sources into coherent insights
|
|
38
|
+
Synthesis = new('synthesis')
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class ResearchSubtask < T::Struct
|
|
43
|
+
# Clear description of the research objective
|
|
44
|
+
const :objective, String
|
|
45
|
+
# Type of research task to be performed
|
|
46
|
+
const :task_type, TaskType
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class ResearchPlan < T::Struct
|
|
50
|
+
# Main research topic being investigated
|
|
51
|
+
const :research_topic, String
|
|
52
|
+
# Collection of research subtasks
|
|
53
|
+
const :subtasks, T::Array[ResearchSubtask]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Dependencies included automatically with smart defaults!
|
|
57
|
+
ResearchPlan.to_baml
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Generated BAML (with correct ordering and descriptions):**
|
|
61
|
+
```baml
|
|
62
|
+
enum TaskType {
|
|
63
|
+
"research" @description("Literature review and information gathering")
|
|
64
|
+
"synthesis" @description("Combining multiple sources into coherent insights")
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
class ResearchSubtask {
|
|
68
|
+
objective string @description("Clear description of the research objective")
|
|
69
|
+
task_type TaskType @description("Type of research task to be performed")
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
class ResearchPlan {
|
|
73
|
+
research_topic string @description("Main research topic being investigated")
|
|
74
|
+
subtasks ResearchSubtask[] @description("Collection of research subtasks")
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Converting Multiple Types
|
|
79
|
+
|
|
80
|
+
### Manual Collection
|
|
81
|
+
|
|
82
|
+
```ruby
|
|
83
|
+
# Convert multiple autonomous research types manually
|
|
84
|
+
types = [TaskType, ResearchSubtask, ResearchPlan]
|
|
85
|
+
baml_output = types.map(&:to_baml).join("\n\n")
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Legacy API (still supported)
|
|
89
|
+
|
|
90
|
+
```ruby
|
|
91
|
+
# Legacy API for multiple structs (no smart defaults)
|
|
92
|
+
SorbetBaml.from_structs([TaskType, ResearchSubtask, ResearchPlan])
|
|
93
|
+
|
|
94
|
+
# Legacy API for single struct (no smart defaults)
|
|
95
|
+
SorbetBaml.from_struct(ResearchPlan)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Advanced Type Examples
|
|
99
|
+
|
|
100
|
+
### Complex Autonomous Research Workflows
|
|
101
|
+
|
|
102
|
+
```ruby
|
|
103
|
+
class ConfidenceLevel < T::Enum
|
|
104
|
+
enums do
|
|
105
|
+
# Low confidence, requires further verification
|
|
106
|
+
Low = new('low')
|
|
107
|
+
# Medium confidence, reasonably supported by evidence
|
|
108
|
+
Medium = new('medium')
|
|
109
|
+
# High confidence, strongly supported by multiple sources
|
|
110
|
+
High = new('high')
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
class ResearchFindings < T::Struct
|
|
115
|
+
# Detailed research findings and analysis
|
|
116
|
+
const :findings, String
|
|
117
|
+
# Key actionable insights extracted
|
|
118
|
+
const :key_insights, T::Array[String]
|
|
119
|
+
# Confidence score for findings (1-10 scale)
|
|
120
|
+
const :confidence_score, Integer
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
class ResearchSynthesis < T::Struct
|
|
124
|
+
# Unique identifier for the research synthesis
|
|
125
|
+
const :id, String
|
|
126
|
+
# Assessment of evidence quality
|
|
127
|
+
const :evidence_quality, ConfidenceLevel
|
|
128
|
+
# Collection of research findings
|
|
129
|
+
const :findings_collection, T::Array[ResearchFindings]
|
|
130
|
+
# Agent coordination metadata
|
|
131
|
+
const :agent_metadata, T::Hash[String, T.any(String, Integer, Float)]
|
|
132
|
+
# Optional peer review notes
|
|
133
|
+
const :peer_review, T.nilable(String)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Generate complete type definitions
|
|
137
|
+
[ConfidenceLevel, ResearchFindings, ResearchSynthesis].map(&:to_baml).join("\n\n")
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Generated BAML:**
|
|
141
|
+
```baml
|
|
142
|
+
enum ConfidenceLevel {
|
|
143
|
+
"low" @description("Low confidence, requires further verification")
|
|
144
|
+
"medium" @description("Medium confidence, reasonably supported by evidence")
|
|
145
|
+
"high" @description("High confidence, strongly supported by multiple sources")
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
class ResearchFindings {
|
|
149
|
+
findings string @description("Detailed research findings and analysis")
|
|
150
|
+
key_insights string[] @description("Key actionable insights extracted")
|
|
151
|
+
confidence_score int @description("Confidence score for findings (1-10 scale)")
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
class ResearchSynthesis {
|
|
155
|
+
id string @description("Unique identifier for the research synthesis")
|
|
156
|
+
evidence_quality ConfidenceLevel @description("Assessment of evidence quality")
|
|
157
|
+
findings_collection ResearchFindings[] @description("Collection of research findings")
|
|
158
|
+
agent_metadata map<string, string | int | float> @description("Agent coordination metadata")
|
|
159
|
+
peer_review string? @description("Optional peer review notes")
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Self-Referential Types
|
|
164
|
+
|
|
165
|
+
```ruby
|
|
166
|
+
class Category < T::Struct
|
|
167
|
+
const :name, String
|
|
168
|
+
const :parent, T.nilable(Category)
|
|
169
|
+
const :children, T::Array[Category]
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
Category.to_baml
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**Generated BAML:**
|
|
176
|
+
```baml
|
|
177
|
+
class Category {
|
|
178
|
+
name string
|
|
179
|
+
parent Category?
|
|
180
|
+
children Category[]
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Tool Definitions
|
|
185
|
+
|
|
186
|
+
Generate BAML tool specifications for agentic workflows, function calling, and structured LLM interactions:
|
|
187
|
+
|
|
188
|
+
### T::Struct-based Tools
|
|
189
|
+
|
|
190
|
+
```ruby
|
|
191
|
+
# Define tool parameter structures for agent interactions
|
|
192
|
+
class SearchTool < T::Struct
|
|
193
|
+
# The search query to execute
|
|
194
|
+
const :query, String
|
|
195
|
+
# Maximum number of results to return
|
|
196
|
+
const :limit, T.nilable(Integer)
|
|
197
|
+
# Optional filter criteria for search results
|
|
198
|
+
const :filters, T::Hash[String, String]
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
class FileWriteTool < T::Struct
|
|
202
|
+
# Path where the file should be written
|
|
203
|
+
const :file_path, String
|
|
204
|
+
# Content to write to the file
|
|
205
|
+
const :content, String
|
|
206
|
+
# Whether to overwrite existing file
|
|
207
|
+
const :overwrite, T::Boolean
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Generate BAML tool definitions
|
|
211
|
+
SearchTool.to_baml_tool
|
|
212
|
+
FileWriteTool.to_baml_tool
|
|
213
|
+
|
|
214
|
+
# Module API also available
|
|
215
|
+
SorbetBaml.from_tool(SearchTool)
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
**Generated BAML Tool Specifications:**
|
|
219
|
+
```baml
|
|
220
|
+
class SearchTool {
|
|
221
|
+
query string @description("The search query to execute")
|
|
222
|
+
limit int? @description("Maximum number of results to return")
|
|
223
|
+
filters map<string, string> @description("Optional filter criteria for search results")
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
class FileWriteTool {
|
|
227
|
+
file_path string @description("Path where the file should be written")
|
|
228
|
+
content string @description("Content to write to the file")
|
|
229
|
+
overwrite bool @description("Whether to overwrite existing file")
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### DSPy-style Tools (Optional)
|
|
234
|
+
|
|
235
|
+
When `dspy.rb` is available, automatically convert DSPy tools with rich metadata:
|
|
236
|
+
|
|
237
|
+
```ruby
|
|
238
|
+
class CalculatorTool < DSPy::Tools::Base
|
|
239
|
+
extend T::Sig
|
|
240
|
+
|
|
241
|
+
tool_name 'calculator'
|
|
242
|
+
tool_description 'Performs basic arithmetic operations with error handling'
|
|
243
|
+
|
|
244
|
+
sig { params(operation: String, num1: Float, num2: Float).returns(T.any(Float, String)) }
|
|
245
|
+
def call(operation:, num1:, num2:)
|
|
246
|
+
case operation.downcase
|
|
247
|
+
when 'add' then num1 + num2
|
|
248
|
+
when 'subtract' then num1 - num2
|
|
249
|
+
when 'multiply' then num1 * num2
|
|
250
|
+
when 'divide'
|
|
251
|
+
return "Error: Cannot divide by zero" if num2 == 0
|
|
252
|
+
num1 / num2
|
|
253
|
+
else
|
|
254
|
+
"Error: Unknown operation"
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# Automatic extraction of tool metadata and parameter types
|
|
260
|
+
CalculatorTool.to_baml
|
|
261
|
+
# =>
|
|
262
|
+
# // Performs basic arithmetic operations with error handling
|
|
263
|
+
# class calculator {
|
|
264
|
+
# operation string @description("Parameter operation")
|
|
265
|
+
# num1 float @description("Parameter num1")
|
|
266
|
+
# num2 float @description("Parameter num2")
|
|
267
|
+
# }
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Tool Collections for Agentic Workflows
|
|
271
|
+
|
|
272
|
+
```ruby
|
|
273
|
+
# Define a complete set of tools for a research agent
|
|
274
|
+
RESEARCH_TOOLS = [
|
|
275
|
+
SearchTool,
|
|
276
|
+
FileWriteTool,
|
|
277
|
+
CalculatorTool
|
|
278
|
+
].freeze
|
|
279
|
+
|
|
280
|
+
# Generate complete tool specifications
|
|
281
|
+
tool_specs = RESEARCH_TOOLS.map(&:to_baml_tool).join("\n\n")
|
|
282
|
+
|
|
283
|
+
# Use in agent prompts
|
|
284
|
+
def build_agent_prompt(task, tools_baml)
|
|
285
|
+
<<~PROMPT
|
|
286
|
+
You are a research agent with access to these tools:
|
|
287
|
+
|
|
288
|
+
#{tools_baml}
|
|
289
|
+
|
|
290
|
+
Task: #{task}
|
|
291
|
+
|
|
292
|
+
Use the appropriate tools to complete this task efficiently.
|
|
293
|
+
PROMPT
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
prompt = build_agent_prompt("Research AI trends", tool_specs)
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Function Calling Integration
|
|
300
|
+
|
|
301
|
+
Perfect for modern LLM function calling APIs:
|
|
302
|
+
|
|
303
|
+
```ruby
|
|
304
|
+
# OpenAI function calling with BAML tools
|
|
305
|
+
tools = [SearchTool, FileWriteTool].map do |tool_class|
|
|
306
|
+
{
|
|
307
|
+
type: "function",
|
|
308
|
+
function: {
|
|
309
|
+
name: tool_class.name.downcase,
|
|
310
|
+
description: "#{tool_class.name} functionality",
|
|
311
|
+
parameters: tool_class.to_baml_tool
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
response = openai_client.chat(
|
|
317
|
+
model: "gpt-4",
|
|
318
|
+
messages: messages,
|
|
319
|
+
tools: tools,
|
|
320
|
+
tool_choice: "auto"
|
|
321
|
+
)
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
## Configuration Options
|
|
325
|
+
|
|
326
|
+
### Custom Indentation
|
|
327
|
+
|
|
328
|
+
```ruby
|
|
329
|
+
User.to_baml(indent_size: 4)
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
**Generated BAML:**
|
|
333
|
+
```baml
|
|
334
|
+
class User {
|
|
335
|
+
name string
|
|
336
|
+
age int
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Field Descriptions (Included by Default)
|
|
341
|
+
|
|
342
|
+
Extract documentation from Ruby comments to provide crucial LLM context for autonomous agents:
|
|
343
|
+
|
|
344
|
+
```ruby
|
|
345
|
+
class AgentCapabilities < T::Struct
|
|
346
|
+
# Specialized domain knowledge for research tasks
|
|
347
|
+
const :domain_expertise, String
|
|
348
|
+
|
|
349
|
+
# Maximum concurrent research tasks the agent can handle
|
|
350
|
+
const :task_capacity, Integer
|
|
351
|
+
|
|
352
|
+
# Current workload as percentage of total capacity (0-100)
|
|
353
|
+
const :current_workload, Integer
|
|
354
|
+
|
|
355
|
+
# List of research methodologies the agent can employ
|
|
356
|
+
const :research_methods, T::Array[String]
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
# Field descriptions included by default!
|
|
360
|
+
AgentCapabilities.to_baml
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
**Generated BAML with descriptions:**
|
|
364
|
+
```baml
|
|
365
|
+
class AgentCapabilities {
|
|
366
|
+
domain_expertise string @description("Specialized domain knowledge for research tasks")
|
|
367
|
+
task_capacity int @description("Maximum concurrent research tasks the agent can handle")
|
|
368
|
+
current_workload int @description("Current workload as percentage of total capacity (0-100)")
|
|
369
|
+
research_methods string[] @description("List of research methodologies the agent can employ")
|
|
370
|
+
}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Combining Options
|
|
374
|
+
|
|
375
|
+
```ruby
|
|
376
|
+
# Smart defaults: dependencies and descriptions already included!
|
|
377
|
+
ResearchSynthesis.to_baml(indent_size: 4)
|
|
378
|
+
|
|
379
|
+
# Or disable features if needed for specific use cases
|
|
380
|
+
ResearchSynthesis.to_baml(
|
|
381
|
+
include_dependencies: false,
|
|
382
|
+
include_descriptions: false,
|
|
383
|
+
indent_size: 4
|
|
384
|
+
)
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
## File Generation
|
|
388
|
+
|
|
389
|
+
### Single File Output
|
|
390
|
+
|
|
391
|
+
```ruby
|
|
392
|
+
# Generate and write autonomous research schema to file
|
|
393
|
+
baml_content = ResearchSynthesis.to_baml
|
|
394
|
+
File.write("schemas/research_workflow.baml", baml_content)
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### Multiple Files
|
|
398
|
+
|
|
399
|
+
```ruby
|
|
400
|
+
# Generate separate files for each research workflow type
|
|
401
|
+
[ConfidenceLevel, ResearchFindings, ResearchSynthesis].each do |type|
|
|
402
|
+
filename = type.name.downcase.gsub('::', '_')
|
|
403
|
+
File.write("schemas/#{filename}.baml", type.to_baml)
|
|
404
|
+
end
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
### Build Process Integration
|
|
408
|
+
|
|
409
|
+
```ruby
|
|
410
|
+
# Rakefile
|
|
411
|
+
desc "Generate BAML schemas for autonomous agents"
|
|
412
|
+
task :generate_baml do
|
|
413
|
+
require 'sorbet-baml'
|
|
414
|
+
require_relative 'lib/research_types'
|
|
415
|
+
|
|
416
|
+
# Your autonomous research workflow types
|
|
417
|
+
types = [TaskType, ResearchSubtask, ResearchFindings, ResearchSynthesis]
|
|
418
|
+
baml_content = types.map(&:to_baml).join("\n\n")
|
|
419
|
+
|
|
420
|
+
File.write("schemas/research_agents.baml", baml_content)
|
|
421
|
+
puts "Generated BAML schemas for research agents in schemas/research_agents.baml"
|
|
422
|
+
end
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
## LLM Integration Patterns
|
|
426
|
+
|
|
427
|
+
### With OpenAI Structured Outputs
|
|
428
|
+
|
|
429
|
+
```ruby
|
|
430
|
+
require 'openai'
|
|
431
|
+
require 'sorbet-baml'
|
|
432
|
+
|
|
433
|
+
# Define your response format
|
|
434
|
+
class AnalysisResult < T::Struct
|
|
435
|
+
const :sentiment, String
|
|
436
|
+
const :confidence, Float
|
|
437
|
+
const :key_phrases, T::Array[String]
|
|
438
|
+
const :metadata, T::Hash[String, String]
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
# Generate schema for LLM
|
|
442
|
+
schema = AnalysisResult.to_baml
|
|
443
|
+
|
|
444
|
+
client = OpenAI::Client.new
|
|
445
|
+
response = client.chat(
|
|
446
|
+
parameters: {
|
|
447
|
+
model: "gpt-4o",
|
|
448
|
+
messages: [
|
|
449
|
+
{
|
|
450
|
+
role: "system",
|
|
451
|
+
content: "Analyze text and respond with data matching this BAML schema:\n\n#{schema}"
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
role: "user",
|
|
455
|
+
content: "Analyze: 'I love this new product!'"
|
|
456
|
+
}
|
|
457
|
+
]
|
|
458
|
+
}
|
|
459
|
+
)
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### With Anthropic Claude
|
|
463
|
+
|
|
464
|
+
```ruby
|
|
465
|
+
require 'anthropic'
|
|
466
|
+
require 'sorbet-baml'
|
|
467
|
+
|
|
468
|
+
schema = UserProfile.to_baml(include_dependencies: true)
|
|
469
|
+
|
|
470
|
+
client = Anthropic::Client.new
|
|
471
|
+
response = client.messages(
|
|
472
|
+
model: "claude-3-5-sonnet-20241022",
|
|
473
|
+
max_tokens: 1000,
|
|
474
|
+
messages: [
|
|
475
|
+
{
|
|
476
|
+
role: "user",
|
|
477
|
+
content: "Generate a realistic user profile matching this schema:\n\n#{schema}"
|
|
478
|
+
}
|
|
479
|
+
]
|
|
480
|
+
)
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### With DSPy.rb Integration
|
|
484
|
+
|
|
485
|
+
```ruby
|
|
486
|
+
require 'dspy'
|
|
487
|
+
require 'sorbet-baml'
|
|
488
|
+
|
|
489
|
+
# Your T::Struct automatically works with DSPy signatures
|
|
490
|
+
class UserAnalysis < DSPy::Signature
|
|
491
|
+
input { const :user_data, String }
|
|
492
|
+
output { const :analysis, AnalysisResult } # Uses your T::Struct
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
# The BAML schema is automatically generated for LLM prompts
|
|
496
|
+
predictor = DSPy::Predict.new(UserAnalysis)
|
|
497
|
+
result = predictor.call(user_data: "John, 25, loves hiking")
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
### Prompt Engineering
|
|
501
|
+
|
|
502
|
+
```ruby
|
|
503
|
+
# Template for complex prompts
|
|
504
|
+
def build_analysis_prompt(data, schema)
|
|
505
|
+
<<~PROMPT
|
|
506
|
+
You are a data analyst. Analyze the following data and return results
|
|
507
|
+
in the exact format specified by this BAML schema:
|
|
508
|
+
|
|
509
|
+
#{schema}
|
|
510
|
+
|
|
511
|
+
Data to analyze:
|
|
512
|
+
#{data}
|
|
513
|
+
|
|
514
|
+
Requirements:
|
|
515
|
+
- Follow the schema exactly
|
|
516
|
+
- Provide confidence scores between 0.0 and 1.0
|
|
517
|
+
- Extract meaningful insights
|
|
518
|
+
PROMPT
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
schema = AnalysisResult.to_baml
|
|
522
|
+
prompt = build_analysis_prompt(user_input, schema)
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
## Rails Integration
|
|
526
|
+
|
|
527
|
+
### Model Integration
|
|
528
|
+
|
|
529
|
+
```ruby
|
|
530
|
+
# app/models/user.rb
|
|
531
|
+
class User < ApplicationRecord
|
|
532
|
+
# Your ActiveRecord model...
|
|
533
|
+
|
|
534
|
+
# Add Sorbet types for API schemas
|
|
535
|
+
class UserAPI < T::Struct
|
|
536
|
+
const :id, Integer
|
|
537
|
+
const :name, String
|
|
538
|
+
const :email, String
|
|
539
|
+
const :created_at, String
|
|
540
|
+
end
|
|
541
|
+
|
|
542
|
+
def to_api_schema
|
|
543
|
+
UserAPI.to_baml
|
|
544
|
+
end
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
# Usage in controllers
|
|
548
|
+
class UsersController < ApplicationController
|
|
549
|
+
def schema
|
|
550
|
+
render json: { schema: User::UserAPI.to_baml }
|
|
551
|
+
end
|
|
552
|
+
end
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
### API Documentation
|
|
556
|
+
|
|
557
|
+
```ruby
|
|
558
|
+
# Generate API docs automatically
|
|
559
|
+
class ApiDocsGenerator
|
|
560
|
+
API_TYPES = [
|
|
561
|
+
User::UserAPI,
|
|
562
|
+
Order::OrderAPI,
|
|
563
|
+
Product::ProductAPI
|
|
564
|
+
].freeze
|
|
565
|
+
|
|
566
|
+
def self.generate
|
|
567
|
+
schema = API_TYPES.map(&:to_baml).join("\n\n")
|
|
568
|
+
File.write("docs/api_schema.baml", schema)
|
|
569
|
+
end
|
|
570
|
+
end
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
## Performance Considerations
|
|
574
|
+
|
|
575
|
+
### Caching Generated BAML
|
|
576
|
+
|
|
577
|
+
```ruby
|
|
578
|
+
class CachedTypeConverter
|
|
579
|
+
def self.to_baml(type)
|
|
580
|
+
@cache ||= {}
|
|
581
|
+
@cache[type] ||= type.to_baml
|
|
582
|
+
end
|
|
583
|
+
end
|
|
584
|
+
|
|
585
|
+
# Use in production for frequently accessed types
|
|
586
|
+
schema = CachedTypeConverter.to_baml(User)
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
### Lazy Loading
|
|
590
|
+
|
|
591
|
+
```ruby
|
|
592
|
+
# Only generate BAML when needed (smart defaults apply)
|
|
593
|
+
class ApiResponse
|
|
594
|
+
def schema
|
|
595
|
+
@schema ||= self.class.to_baml
|
|
596
|
+
end
|
|
597
|
+
end
|
|
598
|
+
```
|