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.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/CLAUDE.md +94 -0
  3. data/README.md +315 -122
  4. data/Rakefile +2 -2
  5. data/docs-site/.gitignore +48 -0
  6. data/docs-site/Gemfile +5 -0
  7. data/docs-site/Gemfile.lock +140 -0
  8. data/docs-site/Rakefile +3 -0
  9. data/docs-site/bridgetown.config.yml +15 -0
  10. data/docs-site/config/initializers.rb +9 -0
  11. data/docs-site/config/puma.rb +9 -0
  12. data/docs-site/config.ru +5 -0
  13. data/docs-site/esbuild.config.js +11 -0
  14. data/docs-site/frontend/javascript/index.js +22 -0
  15. data/docs-site/frontend/styles/index.css +61 -0
  16. data/docs-site/package.json +18 -0
  17. data/docs-site/postcss.config.js +6 -0
  18. data/docs-site/server/roda_app.rb +9 -0
  19. data/docs-site/src/_components/head.liquid +26 -0
  20. data/docs-site/src/_components/nav.liquid +68 -0
  21. data/docs-site/src/_layouts/default.liquid +27 -0
  22. data/docs-site/src/_layouts/doc.liquid +39 -0
  23. data/docs-site/src/advanced-usage.md +598 -0
  24. data/docs-site/src/getting-started.md +170 -0
  25. data/docs-site/src/index.md +183 -0
  26. data/docs-site/src/troubleshooting.md +317 -0
  27. data/docs-site/src/type-mapping.md +236 -0
  28. data/docs-site/tailwind.config.js +85 -0
  29. data/examples/description_parameters.rb +49 -0
  30. data/lib/sorbet_baml/comment_extractor.rb +51 -54
  31. data/lib/sorbet_baml/converter.rb +69 -35
  32. data/lib/sorbet_baml/dependency_resolver.rb +11 -11
  33. data/lib/sorbet_baml/description_extension.rb +34 -0
  34. data/lib/sorbet_baml/description_extractor.rb +34 -0
  35. data/lib/sorbet_baml/dspy_tool_converter.rb +97 -0
  36. data/lib/sorbet_baml/dspy_tool_extensions.rb +23 -0
  37. data/lib/sorbet_baml/enum_extensions.rb +2 -2
  38. data/lib/sorbet_baml/struct_extensions.rb +2 -2
  39. data/lib/sorbet_baml/tool_extensions.rb +23 -0
  40. data/lib/sorbet_baml/type_mapper.rb +35 -37
  41. data/lib/sorbet_baml/version.rb +1 -1
  42. data/lib/sorbet_baml.rb +41 -10
  43. data/sorbet/config +2 -0
  44. data/sorbet/rbi/gems/anthropic@1.5.0.rbi +21252 -0
  45. data/sorbet/rbi/gems/async@2.27.3.rbi +9 -0
  46. data/sorbet/rbi/gems/bigdecimal@3.2.2.rbi +9 -0
  47. data/sorbet/rbi/gems/concurrent-ruby@1.3.5.rbi +424 -0
  48. data/sorbet/rbi/gems/connection_pool@2.5.3.rbi +9 -0
  49. data/sorbet/rbi/gems/console@1.33.0.rbi +9 -0
  50. data/sorbet/rbi/gems/dry-configurable@1.3.0.rbi +672 -0
  51. data/sorbet/rbi/gems/dry-core@1.1.0.rbi +1729 -0
  52. data/sorbet/rbi/gems/dry-logger@1.1.0.rbi +1317 -0
  53. data/sorbet/rbi/gems/dspy@0.19.1.rbi +6677 -0
  54. data/sorbet/rbi/gems/ffi@1.17.2.rbi +2174 -0
  55. data/sorbet/rbi/gems/fiber-annotation@0.2.0.rbi +9 -0
  56. data/sorbet/rbi/gems/fiber-local@1.1.0.rbi +9 -0
  57. data/sorbet/rbi/gems/fiber-storage@1.0.1.rbi +9 -0
  58. data/sorbet/rbi/gems/google-protobuf@4.32.0.rbi +9 -0
  59. data/sorbet/rbi/gems/googleapis-common-protos-types@1.20.0.rbi +9 -0
  60. data/sorbet/rbi/gems/informers@1.2.1.rbi +1875 -0
  61. data/sorbet/rbi/gems/io-event@1.12.1.rbi +9 -0
  62. data/sorbet/rbi/gems/metrics@0.13.0.rbi +9 -0
  63. data/sorbet/rbi/gems/onnxruntime@0.10.0.rbi +304 -0
  64. data/sorbet/rbi/gems/openai@0.16.0.rbi +68055 -0
  65. data/sorbet/rbi/gems/opentelemetry-api@1.6.0.rbi +9 -0
  66. data/sorbet/rbi/gems/opentelemetry-common@0.22.0.rbi +9 -0
  67. data/sorbet/rbi/gems/opentelemetry-exporter-otlp@0.30.0.rbi +9 -0
  68. data/sorbet/rbi/gems/opentelemetry-registry@0.4.0.rbi +9 -0
  69. data/sorbet/rbi/gems/opentelemetry-sdk@1.8.1.rbi +9 -0
  70. data/sorbet/rbi/gems/opentelemetry-semantic_conventions@1.11.0.rbi +9 -0
  71. data/sorbet/rbi/gems/polars-df@0.20.0.rbi +9 -0
  72. data/sorbet/rbi/gems/sorbet-result@1.4.0.rbi +242 -0
  73. data/sorbet/rbi/gems/sorbet-schema@0.9.2.rbi +743 -0
  74. data/sorbet/rbi/gems/sorbet-struct-comparable@1.3.0.rbi +48 -0
  75. data/sorbet/rbi/gems/tokenizers@0.5.5.rbi +754 -0
  76. data/sorbet/rbi/gems/traces@0.17.0.rbi +9 -0
  77. data/sorbet/rbi/gems/zeitwerk@2.7.3.rbi +1429 -0
  78. metadata +67 -7
  79. data/docs/README.md +0 -117
  80. data/docs/advanced-usage.md +0 -427
  81. data/docs/getting-started.md +0 -91
  82. data/docs/troubleshooting.md +0 -291
  83. data/docs/type-mapping.md +0 -192
@@ -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
+ ```