ruby_llm-agents 0.5.0 → 1.0.0.beta.1

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 (190) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +189 -31
  3. data/app/controllers/ruby_llm/agents/agents_controller.rb +136 -16
  4. data/app/controllers/ruby_llm/agents/dashboard_controller.rb +29 -9
  5. data/app/controllers/ruby_llm/agents/workflows_controller.rb +355 -0
  6. data/app/helpers/ruby_llm/agents/application_helper.rb +25 -0
  7. data/app/models/ruby_llm/agents/execution.rb +3 -0
  8. data/app/models/ruby_llm/agents/tenant_budget.rb +58 -15
  9. data/app/services/ruby_llm/agents/agent_registry.rb +51 -12
  10. data/app/views/layouts/ruby_llm/agents/application.html.erb +2 -29
  11. data/app/views/ruby_llm/agents/agents/_agent.html.erb +13 -1
  12. data/app/views/ruby_llm/agents/agents/_config_agent.html.erb +235 -0
  13. data/app/views/ruby_llm/agents/agents/_config_embedder.html.erb +70 -0
  14. data/app/views/ruby_llm/agents/agents/_config_image_generator.html.erb +152 -0
  15. data/app/views/ruby_llm/agents/agents/_config_moderator.html.erb +63 -0
  16. data/app/views/ruby_llm/agents/agents/_config_speaker.html.erb +108 -0
  17. data/app/views/ruby_llm/agents/agents/_config_transcriber.html.erb +91 -0
  18. data/app/views/ruby_llm/agents/agents/_workflow.html.erb +1 -1
  19. data/app/views/ruby_llm/agents/agents/index.html.erb +74 -9
  20. data/app/views/ruby_llm/agents/agents/show.html.erb +18 -378
  21. data/app/views/ruby_llm/agents/dashboard/_agent_comparison.html.erb +269 -15
  22. data/app/views/ruby_llm/agents/executions/show.html.erb +16 -0
  23. data/app/views/ruby_llm/agents/shared/_agent_type_badge.html.erb +93 -0
  24. data/app/views/ruby_llm/agents/workflows/_step_performance.html.erb +236 -0
  25. data/app/views/ruby_llm/agents/workflows/_structure_parallel.html.erb +76 -0
  26. data/app/views/ruby_llm/agents/workflows/_structure_pipeline.html.erb +74 -0
  27. data/app/views/ruby_llm/agents/workflows/_structure_router.html.erb +108 -0
  28. data/app/views/ruby_llm/agents/workflows/show.html.erb +442 -0
  29. data/config/routes.rb +1 -0
  30. data/lib/generators/ruby_llm_agents/agent_generator.rb +56 -7
  31. data/lib/generators/ruby_llm_agents/background_remover_generator.rb +110 -0
  32. data/lib/generators/ruby_llm_agents/embedder_generator.rb +107 -0
  33. data/lib/generators/ruby_llm_agents/image_analyzer_generator.rb +115 -0
  34. data/lib/generators/ruby_llm_agents/image_editor_generator.rb +108 -0
  35. data/lib/generators/ruby_llm_agents/image_generator_generator.rb +116 -0
  36. data/lib/generators/ruby_llm_agents/image_pipeline_generator.rb +178 -0
  37. data/lib/generators/ruby_llm_agents/image_transformer_generator.rb +109 -0
  38. data/lib/generators/ruby_llm_agents/image_upscaler_generator.rb +103 -0
  39. data/lib/generators/ruby_llm_agents/image_variator_generator.rb +102 -0
  40. data/lib/generators/ruby_llm_agents/install_generator.rb +76 -4
  41. data/lib/generators/ruby_llm_agents/restructure_generator.rb +292 -0
  42. data/lib/generators/ruby_llm_agents/speaker_generator.rb +121 -0
  43. data/lib/generators/ruby_llm_agents/templates/add_execution_type_migration.rb.tt +8 -0
  44. data/lib/generators/ruby_llm_agents/templates/agent.rb.tt +99 -84
  45. data/lib/generators/ruby_llm_agents/templates/application_agent.rb.tt +42 -40
  46. data/lib/generators/ruby_llm_agents/templates/application_background_remover.rb.tt +26 -0
  47. data/lib/generators/ruby_llm_agents/templates/application_embedder.rb.tt +50 -0
  48. data/lib/generators/ruby_llm_agents/templates/application_image_analyzer.rb.tt +26 -0
  49. data/lib/generators/ruby_llm_agents/templates/application_image_editor.rb.tt +20 -0
  50. data/lib/generators/ruby_llm_agents/templates/application_image_generator.rb.tt +38 -0
  51. data/lib/generators/ruby_llm_agents/templates/application_image_pipeline.rb.tt +139 -0
  52. data/lib/generators/ruby_llm_agents/templates/application_image_transformer.rb.tt +21 -0
  53. data/lib/generators/ruby_llm_agents/templates/application_image_upscaler.rb.tt +20 -0
  54. data/lib/generators/ruby_llm_agents/templates/application_image_variator.rb.tt +20 -0
  55. data/lib/generators/ruby_llm_agents/templates/application_speaker.rb.tt +49 -0
  56. data/lib/generators/ruby_llm_agents/templates/application_transcriber.rb.tt +53 -0
  57. data/lib/generators/ruby_llm_agents/templates/background_remover.rb.tt +44 -0
  58. data/lib/generators/ruby_llm_agents/templates/embedder.rb.tt +41 -0
  59. data/lib/generators/ruby_llm_agents/templates/image_analyzer.rb.tt +45 -0
  60. data/lib/generators/ruby_llm_agents/templates/image_editor.rb.tt +35 -0
  61. data/lib/generators/ruby_llm_agents/templates/image_generator.rb.tt +47 -0
  62. data/lib/generators/ruby_llm_agents/templates/image_pipeline.rb.tt +50 -0
  63. data/lib/generators/ruby_llm_agents/templates/image_transformer.rb.tt +44 -0
  64. data/lib/generators/ruby_llm_agents/templates/image_upscaler.rb.tt +38 -0
  65. data/lib/generators/ruby_llm_agents/templates/image_variator.rb.tt +33 -0
  66. data/lib/generators/ruby_llm_agents/templates/skills/AGENTS.md.tt +228 -0
  67. data/lib/generators/ruby_llm_agents/templates/skills/BACKGROUND_REMOVERS.md.tt +131 -0
  68. data/lib/generators/ruby_llm_agents/templates/skills/EMBEDDERS.md.tt +255 -0
  69. data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_ANALYZERS.md.tt +120 -0
  70. data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_EDITORS.md.tt +102 -0
  71. data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_GENERATORS.md.tt +282 -0
  72. data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_PIPELINES.md.tt +228 -0
  73. data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_TRANSFORMERS.md.tt +120 -0
  74. data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_UPSCALERS.md.tt +110 -0
  75. data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_VARIATORS.md.tt +120 -0
  76. data/lib/generators/ruby_llm_agents/templates/skills/SPEAKERS.md.tt +212 -0
  77. data/lib/generators/ruby_llm_agents/templates/skills/TOOLS.md.tt +227 -0
  78. data/lib/generators/ruby_llm_agents/templates/skills/TRANSCRIBERS.md.tt +251 -0
  79. data/lib/generators/ruby_llm_agents/templates/skills/WORKFLOWS.md.tt +300 -0
  80. data/lib/generators/ruby_llm_agents/templates/speaker.rb.tt +56 -0
  81. data/lib/generators/ruby_llm_agents/templates/transcriber.rb.tt +51 -0
  82. data/lib/generators/ruby_llm_agents/transcriber_generator.rb +107 -0
  83. data/lib/generators/ruby_llm_agents/upgrade_generator.rb +152 -1
  84. data/lib/ruby_llm/agents/audio/speaker.rb +553 -0
  85. data/lib/ruby_llm/agents/audio/transcriber.rb +669 -0
  86. data/lib/ruby_llm/agents/base_agent.rb +675 -0
  87. data/lib/ruby_llm/agents/core/base/moderation_dsl.rb +181 -0
  88. data/lib/ruby_llm/agents/core/base/moderation_execution.rb +274 -0
  89. data/lib/ruby_llm/agents/core/base.rb +135 -0
  90. data/lib/ruby_llm/agents/core/configuration.rb +981 -0
  91. data/lib/ruby_llm/agents/core/errors.rb +150 -0
  92. data/lib/ruby_llm/agents/{instrumentation.rb → core/instrumentation.rb} +22 -1
  93. data/lib/ruby_llm/agents/core/llm_tenant.rb +358 -0
  94. data/lib/ruby_llm/agents/{version.rb → core/version.rb} +1 -1
  95. data/lib/ruby_llm/agents/dsl/base.rb +110 -0
  96. data/lib/ruby_llm/agents/dsl/caching.rb +142 -0
  97. data/lib/ruby_llm/agents/dsl/reliability.rb +307 -0
  98. data/lib/ruby_llm/agents/dsl.rb +41 -0
  99. data/lib/ruby_llm/agents/image/analyzer/dsl.rb +130 -0
  100. data/lib/ruby_llm/agents/image/analyzer/execution.rb +402 -0
  101. data/lib/ruby_llm/agents/image/analyzer.rb +90 -0
  102. data/lib/ruby_llm/agents/image/background_remover/dsl.rb +154 -0
  103. data/lib/ruby_llm/agents/image/background_remover/execution.rb +240 -0
  104. data/lib/ruby_llm/agents/image/background_remover.rb +89 -0
  105. data/lib/ruby_llm/agents/image/concerns/image_operation_dsl.rb +91 -0
  106. data/lib/ruby_llm/agents/image/concerns/image_operation_execution.rb +165 -0
  107. data/lib/ruby_llm/agents/image/editor/dsl.rb +56 -0
  108. data/lib/ruby_llm/agents/image/editor/execution.rb +207 -0
  109. data/lib/ruby_llm/agents/image/editor.rb +92 -0
  110. data/lib/ruby_llm/agents/image/generator/active_storage_support.rb +127 -0
  111. data/lib/ruby_llm/agents/image/generator/content_policy.rb +95 -0
  112. data/lib/ruby_llm/agents/image/generator/pricing.rb +353 -0
  113. data/lib/ruby_llm/agents/image/generator/templates.rb +124 -0
  114. data/lib/ruby_llm/agents/image/generator.rb +455 -0
  115. data/lib/ruby_llm/agents/image/pipeline/dsl.rb +213 -0
  116. data/lib/ruby_llm/agents/image/pipeline/execution.rb +382 -0
  117. data/lib/ruby_llm/agents/image/pipeline.rb +97 -0
  118. data/lib/ruby_llm/agents/image/transformer/dsl.rb +148 -0
  119. data/lib/ruby_llm/agents/image/transformer/execution.rb +223 -0
  120. data/lib/ruby_llm/agents/image/transformer.rb +95 -0
  121. data/lib/ruby_llm/agents/image/upscaler/dsl.rb +83 -0
  122. data/lib/ruby_llm/agents/image/upscaler/execution.rb +219 -0
  123. data/lib/ruby_llm/agents/image/upscaler.rb +81 -0
  124. data/lib/ruby_llm/agents/image/variator/dsl.rb +62 -0
  125. data/lib/ruby_llm/agents/image/variator/execution.rb +189 -0
  126. data/lib/ruby_llm/agents/image/variator.rb +80 -0
  127. data/lib/ruby_llm/agents/{alert_manager.rb → infrastructure/alert_manager.rb} +17 -22
  128. data/lib/ruby_llm/agents/infrastructure/budget/budget_query.rb +145 -0
  129. data/lib/ruby_llm/agents/infrastructure/budget/config_resolver.rb +149 -0
  130. data/lib/ruby_llm/agents/infrastructure/budget/forecaster.rb +68 -0
  131. data/lib/ruby_llm/agents/infrastructure/budget/spend_recorder.rb +279 -0
  132. data/lib/ruby_llm/agents/infrastructure/budget_tracker.rb +275 -0
  133. data/lib/ruby_llm/agents/{execution_logger_job.rb → infrastructure/execution_logger_job.rb} +17 -1
  134. data/lib/ruby_llm/agents/{reliability → infrastructure/reliability}/executor.rb +2 -1
  135. data/lib/ruby_llm/agents/{reliability → infrastructure/reliability}/retry_strategy.rb +9 -3
  136. data/lib/ruby_llm/agents/{reliability.rb → infrastructure/reliability.rb} +11 -21
  137. data/lib/ruby_llm/agents/pipeline/builder.rb +215 -0
  138. data/lib/ruby_llm/agents/pipeline/context.rb +255 -0
  139. data/lib/ruby_llm/agents/pipeline/executor.rb +86 -0
  140. data/lib/ruby_llm/agents/pipeline/middleware/base.rb +124 -0
  141. data/lib/ruby_llm/agents/pipeline/middleware/budget.rb +95 -0
  142. data/lib/ruby_llm/agents/pipeline/middleware/cache.rb +171 -0
  143. data/lib/ruby_llm/agents/pipeline/middleware/instrumentation.rb +415 -0
  144. data/lib/ruby_llm/agents/pipeline/middleware/reliability.rb +276 -0
  145. data/lib/ruby_llm/agents/pipeline/middleware/tenant.rb +196 -0
  146. data/lib/ruby_llm/agents/pipeline.rb +68 -0
  147. data/lib/ruby_llm/agents/{engine.rb → rails/engine.rb} +79 -11
  148. data/lib/ruby_llm/agents/results/background_removal_result.rb +286 -0
  149. data/lib/ruby_llm/agents/{result.rb → results/base.rb} +73 -1
  150. data/lib/ruby_llm/agents/results/embedding_result.rb +243 -0
  151. data/lib/ruby_llm/agents/results/image_analysis_result.rb +314 -0
  152. data/lib/ruby_llm/agents/results/image_edit_result.rb +250 -0
  153. data/lib/ruby_llm/agents/results/image_generation_result.rb +346 -0
  154. data/lib/ruby_llm/agents/results/image_pipeline_result.rb +399 -0
  155. data/lib/ruby_llm/agents/results/image_transform_result.rb +251 -0
  156. data/lib/ruby_llm/agents/results/image_upscale_result.rb +255 -0
  157. data/lib/ruby_llm/agents/results/image_variation_result.rb +237 -0
  158. data/lib/ruby_llm/agents/results/moderation_result.rb +158 -0
  159. data/lib/ruby_llm/agents/results/speech_result.rb +338 -0
  160. data/lib/ruby_llm/agents/results/transcription_result.rb +408 -0
  161. data/lib/ruby_llm/agents/text/embedder.rb +444 -0
  162. data/lib/ruby_llm/agents/text/moderator.rb +237 -0
  163. data/lib/ruby_llm/agents/workflow/async.rb +220 -0
  164. data/lib/ruby_llm/agents/workflow/async_executor.rb +156 -0
  165. data/lib/ruby_llm/agents/{workflow.rb → workflow/orchestrator.rb} +6 -5
  166. data/lib/ruby_llm/agents/workflow/parallel.rb +34 -17
  167. data/lib/ruby_llm/agents/workflow/thread_pool.rb +185 -0
  168. data/lib/ruby_llm/agents.rb +86 -20
  169. metadata +172 -34
  170. data/lib/ruby_llm/agents/base/caching.rb +0 -40
  171. data/lib/ruby_llm/agents/base/cost_calculation.rb +0 -105
  172. data/lib/ruby_llm/agents/base/dsl.rb +0 -324
  173. data/lib/ruby_llm/agents/base/execution.rb +0 -366
  174. data/lib/ruby_llm/agents/base/reliability_dsl.rb +0 -82
  175. data/lib/ruby_llm/agents/base/reliability_execution.rb +0 -136
  176. data/lib/ruby_llm/agents/base/response_building.rb +0 -86
  177. data/lib/ruby_llm/agents/base/tool_tracking.rb +0 -57
  178. data/lib/ruby_llm/agents/base.rb +0 -210
  179. data/lib/ruby_llm/agents/budget_tracker.rb +0 -733
  180. data/lib/ruby_llm/agents/configuration.rb +0 -394
  181. /data/lib/ruby_llm/agents/{deprecations.rb → core/deprecations.rb} +0 -0
  182. /data/lib/ruby_llm/agents/{inflections.rb → core/inflections.rb} +0 -0
  183. /data/lib/ruby_llm/agents/{resolved_config.rb → core/resolved_config.rb} +0 -0
  184. /data/lib/ruby_llm/agents/{attempt_tracker.rb → infrastructure/attempt_tracker.rb} +0 -0
  185. /data/lib/ruby_llm/agents/{cache_helper.rb → infrastructure/cache_helper.rb} +0 -0
  186. /data/lib/ruby_llm/agents/{circuit_breaker.rb → infrastructure/circuit_breaker.rb} +0 -0
  187. /data/lib/ruby_llm/agents/{redactor.rb → infrastructure/redactor.rb} +0 -0
  188. /data/lib/ruby_llm/agents/{reliability → infrastructure/reliability}/breaker_manager.rb +0 -0
  189. /data/lib/ruby_llm/agents/{reliability → infrastructure/reliability}/execution_constraints.rb +0 -0
  190. /data/lib/ruby_llm/agents/{reliability → infrastructure/reliability}/fallback_routing.rb +0 -0
@@ -0,0 +1,251 @@
1
+ # <%= @root_namespace %>::Audio Transcribers
2
+
3
+ This directory contains audio transcription services. All transcribers inherit from `ApplicationTranscriber`.
4
+
5
+ ## Creating a New Transcriber
6
+
7
+ Use the generator:
8
+ ```bash
9
+ rails generate ruby_llm_agents:transcriber TranscriberName
10
+ rails generate ruby_llm_agents:transcriber Meeting --model whisper-1 --language en
11
+ rails generate ruby_llm_agents:transcriber Podcast --output_format srt
12
+ ```
13
+
14
+ Or create manually:
15
+ ```ruby
16
+ module <%= @root_namespace %>
17
+ module Audio
18
+ class MeetingTranscriber < ApplicationTranscriber
19
+ model "whisper-1"
20
+ language "en"
21
+ output_format :text
22
+ include_timestamps :segment
23
+ end
24
+ end
25
+ end
26
+ ```
27
+
28
+ ## DSL Reference
29
+
30
+ ### Model Configuration
31
+
32
+ | Method | Description | Example |
33
+ |--------|-------------|---------|
34
+ | `model` | Transcription model | `model "whisper-1"` |
35
+ | `language` | Audio language (nil = auto-detect) | `language "en"` |
36
+ | `output_format` | Output format | `output_format :text` |
37
+ | `include_timestamps` | Timestamp granularity | `include_timestamps :segment` |
38
+
39
+ ### Output Formats
40
+
41
+ - `:text` - Plain text transcription
42
+ - `:json` - JSON with metadata
43
+ - `:srt` - SubRip subtitle format
44
+ - `:vtt` - WebVTT subtitle format
45
+
46
+ ### Timestamp Options
47
+
48
+ - `:none` - No timestamps
49
+ - `:segment` - Timestamps per segment
50
+ - `:word` - Word-level timestamps
51
+
52
+ ### Caching
53
+
54
+ ```ruby
55
+ cache_for 30.days # Cache transcriptions
56
+ ```
57
+
58
+ ## Optional Methods
59
+
60
+ ### `prompt`
61
+ Provide context to improve accuracy:
62
+
63
+ ```ruby
64
+ def prompt
65
+ "Technical discussion about Ruby programming and software development"
66
+ end
67
+ ```
68
+
69
+ ### `postprocess_text`
70
+ Clean up transcription output:
71
+
72
+ ```ruby
73
+ def postprocess_text(text)
74
+ text
75
+ .gsub(/\bum\b/i, '') # Remove filler words
76
+ .gsub(/\buh\b/i, '')
77
+ .gsub(/\blike\b/i, '') # Remove verbal tics
78
+ .squeeze(' ') # Remove extra spaces
79
+ .strip
80
+ end
81
+ ```
82
+
83
+ ## Using Transcribers
84
+
85
+ ### Basic Transcription
86
+
87
+ ```ruby
88
+ result = <%= @root_namespace %>::Audio::MeetingTranscriber.call(audio: "meeting.mp3")
89
+
90
+ result.text # The transcribed text
91
+ result.duration # Audio duration in seconds
92
+ result.language # Detected language
93
+ result.total_cost # Cost in USD
94
+ ```
95
+
96
+ ### From File Path
97
+
98
+ ```ruby
99
+ result = <%= @root_namespace %>::Audio::MeetingTranscriber.call(audio: "/path/to/audio.mp3")
100
+ ```
101
+
102
+ ### From Binary Data
103
+
104
+ ```ruby
105
+ audio_data = File.read("recording.wav", mode: "rb")
106
+ result = <%= @root_namespace %>::Audio::MeetingTranscriber.call(audio: audio_data)
107
+ ```
108
+
109
+ ### From URL
110
+
111
+ ```ruby
112
+ result = <%= @root_namespace %>::Audio::MeetingTranscriber.call(
113
+ audio: "https://example.com/audio.mp3"
114
+ )
115
+ ```
116
+
117
+ ### With Timestamps (SRT)
118
+
119
+ ```ruby
120
+ module <%= @root_namespace %>
121
+ module Audio
122
+ class SubtitleTranscriber < ApplicationTranscriber
123
+ model "whisper-1"
124
+ output_format :srt
125
+ include_timestamps :segment
126
+ end
127
+ end
128
+ end
129
+
130
+ result = <%= @root_namespace %>::Audio::SubtitleTranscriber.call(audio: "video.mp4")
131
+ File.write("subtitles.srt", result.text)
132
+ ```
133
+
134
+ ## Use Cases
135
+
136
+ ### Meeting Transcription
137
+
138
+ ```ruby
139
+ module <%= @root_namespace %>
140
+ module Audio
141
+ class MeetingTranscriber < ApplicationTranscriber
142
+ model "whisper-1"
143
+ language "en"
144
+ cache_for 90.days
145
+
146
+ def prompt
147
+ "Business meeting with technical discussions"
148
+ end
149
+
150
+ def postprocess_text(text)
151
+ text
152
+ .gsub(/\bum\b/i, '')
153
+ .gsub(/\buh\b/i, '')
154
+ .squeeze(' ')
155
+ end
156
+ end
157
+ end
158
+ end
159
+ ```
160
+
161
+ ### Podcast Subtitles
162
+
163
+ ```ruby
164
+ module <%= @root_namespace %>
165
+ module Audio
166
+ class PodcastTranscriber < ApplicationTranscriber
167
+ model "whisper-1"
168
+ output_format :vtt
169
+ include_timestamps :segment
170
+
171
+ def prompt
172
+ "Technology podcast discussing software development"
173
+ end
174
+ end
175
+ end
176
+ end
177
+ ```
178
+
179
+ ### Voice Note Processing
180
+
181
+ ```ruby
182
+ module <%= @root_namespace %>
183
+ module Audio
184
+ class VoiceNoteTranscriber < ApplicationTranscriber
185
+ model "whisper-1"
186
+ output_format :json
187
+
188
+ def postprocess_text(text)
189
+ # Clean up casual speech
190
+ text
191
+ .gsub(/\byou know\b/i, '')
192
+ .gsub(/\bi mean\b/i, '')
193
+ .gsub(/\bkind of\b/i, '')
194
+ .squeeze(' ')
195
+ end
196
+ end
197
+ end
198
+ end
199
+ ```
200
+
201
+ ### Multilingual Transcription
202
+
203
+ ```ruby
204
+ module <%= @root_namespace %>
205
+ module Audio
206
+ class MultilingualTranscriber < ApplicationTranscriber
207
+ model "whisper-1"
208
+ # Leave language nil for auto-detection
209
+ output_format :json
210
+ end
211
+ end
212
+ end
213
+
214
+ result = <%= @root_namespace %>::Audio::MultilingualTranscriber.call(audio: "spanish.mp3")
215
+ result.language # => "es"
216
+ ```
217
+
218
+ ## Testing Transcribers
219
+
220
+ ```ruby
221
+ RSpec.describe <%= @root_namespace %>::Audio::MeetingTranscriber do
222
+ describe ".call" do
223
+ it "transcribes audio file" do
224
+ result = described_class.call(audio: fixture_file("sample.mp3"))
225
+
226
+ expect(result.text).to be_present
227
+ expect(result.duration).to be > 0
228
+ end
229
+ end
230
+
231
+ describe "#postprocess_text" do
232
+ it "removes filler words" do
233
+ transcriber = described_class.new(audio: "test.mp3")
234
+ text = "So um I think uh we should proceed"
235
+
236
+ result = transcriber.postprocess_text(text)
237
+
238
+ expect(result).to eq("So I think we should proceed")
239
+ end
240
+ end
241
+ end
242
+ ```
243
+
244
+ ## Best Practices
245
+
246
+ 1. **Provide context via prompt** - Helps with technical terms and names
247
+ 2. **Use caching** - Same audio produces same transcription
248
+ 3. **Choose appropriate format** - SRT/VTT for subtitles, text for processing
249
+ 4. **Post-process for quality** - Remove filler words, fix formatting
250
+ 5. **Specify language when known** - Improves accuracy over auto-detect
251
+ 6. **Consider file formats** - MP3, WAV, M4A, MP4 audio track supported
@@ -0,0 +1,300 @@
1
+ # <%= @root_namespace %> Workflows
2
+
3
+ This directory contains workflow orchestration classes that compose multiple agents. Workflows provide patterns for sequential, parallel, and conditional agent execution.
4
+
5
+ ## Workflow Types
6
+
7
+ | Type | Class | Description |
8
+ |------|-------|-------------|
9
+ | Pipeline | `Workflow::Pipeline` | Sequential execution, data flows between steps |
10
+ | Parallel | `Workflow::Parallel` | Concurrent execution with aggregation |
11
+ | Router | `Workflow::Router` | Conditional dispatch based on classification |
12
+
13
+ ## Creating Workflows
14
+
15
+ ### Pipeline (Sequential)
16
+
17
+ Execute agents in order, passing each step's output to the next:
18
+
19
+ ```ruby
20
+ module <%= @root_namespace %>
21
+ class ContentPipeline < RubyLLM::Agents::Workflow::Pipeline
22
+ version "1.0"
23
+ timeout 120
24
+ max_cost 0.50
25
+
26
+ step :extract, agent: ExtractorAgent
27
+ step :validate, agent: ValidatorAgent
28
+ step :format, agent: FormatterAgent
29
+ end
30
+ end
31
+ ```
32
+
33
+ ### Parallel (Concurrent)
34
+
35
+ Execute multiple agents simultaneously:
36
+
37
+ ```ruby
38
+ module <%= @root_namespace %>
39
+ class ReviewAnalyzer < RubyLLM::Agents::Workflow::Parallel
40
+ version "1.0"
41
+
42
+ branch :sentiment, agent: SentimentAgent
43
+ branch :summary, agent: SummaryAgent
44
+ branch :categories, agent: CategoryAgent
45
+
46
+ def aggregate(results)
47
+ {
48
+ sentiment: results[:sentiment]&.content,
49
+ summary: results[:summary]&.content,
50
+ categories: results[:categories]&.content
51
+ }
52
+ end
53
+ end
54
+ end
55
+ ```
56
+
57
+ ### Router (Conditional)
58
+
59
+ Route to different agents based on classification:
60
+
61
+ ```ruby
62
+ module <%= @root_namespace %>
63
+ class SupportRouter < RubyLLM::Agents::Workflow::Router
64
+ version "1.0"
65
+ classifier ClassificationAgent
66
+
67
+ route :billing, agent: BillingAgent
68
+ route :technical, agent: TechnicalAgent
69
+ route :general, agent: GeneralAgent
70
+
71
+ default_route :general
72
+ end
73
+ end
74
+ ```
75
+
76
+ ## DSL Reference
77
+
78
+ ### Shared Options
79
+
80
+ ```ruby
81
+ version "1.0" # Version for tracking changes
82
+ timeout 120 # Total workflow timeout in seconds
83
+ max_cost 0.50 # Maximum allowed cost in USD
84
+ description "..." # Human-readable description
85
+ ```
86
+
87
+ ### Pipeline DSL
88
+
89
+ ```ruby
90
+ # Define steps
91
+ step :name, agent: AgentClass
92
+
93
+ # Conditional skipping
94
+ step :validate, agent: Validator, skip_on: ->(ctx) { ctx[:skip_validation] }
95
+
96
+ # Optional steps (failures won't stop pipeline)
97
+ step :enrich, agent: Enricher, optional: true
98
+
99
+ # Continue on error
100
+ step :notify, agent: Notifier, continue_on_error: true
101
+ ```
102
+
103
+ ### Parallel DSL
104
+
105
+ ```ruby
106
+ # Define branches
107
+ branch :name, agent: AgentClass
108
+
109
+ # Optional branches
110
+ branch :extra, agent: ExtraAgent, optional: true
111
+
112
+ # Custom input transformation
113
+ branch :process, agent: Processor, input: ->(opts) { { text: opts[:content] } }
114
+
115
+ # Fail-fast (stop all on first required failure)
116
+ fail_fast true
117
+
118
+ # Limit concurrency
119
+ concurrency 3
120
+ ```
121
+
122
+ ### Router DSL
123
+
124
+ ```ruby
125
+ # Set classifier
126
+ classifier ClassificationAgent
127
+
128
+ # Define routes
129
+ route :category, agent: AgentClass
130
+
131
+ # Default route
132
+ default_route :fallback
133
+
134
+ # Custom routing logic
135
+ def select_route(classification)
136
+ case classification[:type]
137
+ when "urgent" then :priority
138
+ else :standard
139
+ end
140
+ end
141
+ ```
142
+
143
+ ## Using Workflows
144
+
145
+ ### Basic Execution
146
+
147
+ ```ruby
148
+ result = <%= @root_namespace %>::ContentPipeline.call(text: "raw input")
149
+
150
+ result.success? # All steps succeeded
151
+ result.partial? # Some steps succeeded
152
+ result.error? # Workflow failed
153
+ result.content # Final output
154
+ result.total_cost # Combined cost
155
+ result.duration_ms # Total duration
156
+ ```
157
+
158
+ ### Pipeline Results
159
+
160
+ ```ruby
161
+ result = <%= @root_namespace %>::ContentPipeline.call(text: "input")
162
+
163
+ result.steps # Hash of all step results
164
+ result.steps[:extract].content # Specific step output
165
+ result.errors # Hash of step errors
166
+ ```
167
+
168
+ ### Parallel Results
169
+
170
+ ```ruby
171
+ result = <%= @root_namespace %>::ReviewAnalyzer.call(text: "Great product!")
172
+
173
+ result.branches # Hash of branch results
174
+ result.branches[:sentiment].content # Specific branch output
175
+ ```
176
+
177
+ ### Router Results
178
+
179
+ ```ruby
180
+ result = <%= @root_namespace %>::SupportRouter.call(question: "How do I pay?")
181
+
182
+ result.classification # What route was selected
183
+ result.route # Which agent handled it
184
+ result.content # Response from routed agent
185
+ ```
186
+
187
+ ## Advanced Patterns
188
+
189
+ ### Input Transformation
190
+
191
+ Transform data between pipeline steps:
192
+
193
+ ```ruby
194
+ module <%= @root_namespace %>
195
+ class TransformPipeline < RubyLLM::Agents::Workflow::Pipeline
196
+ step :analyze, agent: AnalyzerAgent
197
+ step :enrich, agent: EnricherAgent
198
+
199
+ # Called before :enrich step
200
+ def before_enrich(context)
201
+ {
202
+ data: context[:analyze].content,
203
+ extra_field: "additional context"
204
+ }
205
+ end
206
+ end
207
+ end
208
+ ```
209
+
210
+ ### Custom Aggregation
211
+
212
+ Combine parallel results:
213
+
214
+ ```ruby
215
+ module <%= @root_namespace %>
216
+ class SafetyChecker < RubyLLM::Agents::Workflow::Parallel
217
+ branch :toxicity, agent: ToxicityAgent
218
+ branch :spam, agent: SpamAgent
219
+ branch :pii, agent: PiiAgent
220
+
221
+ def aggregate(results)
222
+ {
223
+ is_safe: results.values.none? { |r| r&.content == "flagged" },
224
+ flags: results.select { |_, r| r&.content == "flagged" }.keys
225
+ }
226
+ end
227
+ end
228
+ end
229
+ ```
230
+
231
+ ### Error Handling
232
+
233
+ Handle step failures:
234
+
235
+ ```ruby
236
+ module <%= @root_namespace %>
237
+ class ResilientPipeline < RubyLLM::Agents::Workflow::Pipeline
238
+ step :primary, agent: PrimaryAgent
239
+ step :backup, agent: BackupAgent, optional: true
240
+
241
+ # Called when :primary fails
242
+ def on_primary_failure(error, context)
243
+ Rails.logger.warn("Primary failed: #{error.message}")
244
+ :skip # Continue to backup
245
+ # :abort would stop the pipeline
246
+ end
247
+ end
248
+ end
249
+ ```
250
+
251
+ ### Cost Limits
252
+
253
+ Stop workflow if cost exceeds threshold:
254
+
255
+ ```ruby
256
+ module <%= @root_namespace %>
257
+ class BudgetedWorkflow < RubyLLM::Agents::Workflow::Pipeline
258
+ max_cost 1.00 # Stop if workflow exceeds $1.00
259
+
260
+ step :expensive_analysis, agent: DeepAnalysisAgent
261
+ step :synthesis, agent: SynthesisAgent
262
+ end
263
+ end
264
+ ```
265
+
266
+ ## Testing Workflows
267
+
268
+ ```ruby
269
+ RSpec.describe <%= @root_namespace %>::ContentPipeline do
270
+ describe ".call" do
271
+ it "processes content through all steps" do
272
+ result = described_class.call(text: "test input")
273
+
274
+ expect(result.success?).to be true
275
+ expect(result.steps.keys).to eq([:extract, :validate, :format])
276
+ end
277
+
278
+ it "handles step failures" do
279
+ allow(ExtractorAgent).to receive(:call).and_raise("Network error")
280
+
281
+ result = described_class.call(text: "test")
282
+
283
+ expect(result.error?).to be true
284
+ expect(result.errors[:extract]).to be_present
285
+ end
286
+ end
287
+ end
288
+ ```
289
+
290
+ ## Best Practices
291
+
292
+ 1. **Version your workflows** - Track changes for debugging
293
+ 2. **Set timeouts** - Prevent runaway workflows
294
+ 3. **Use max_cost** - Control spending on expensive operations
295
+ 4. **Handle errors gracefully** - Use optional steps and error handlers
296
+ 5. **Keep workflows focused** - One workflow, one purpose
297
+ 6. **Test step interactions** - Unit test agents, integration test workflows
298
+ 7. **Log workflow execution** - Track step timing and costs
299
+ 8. **Use parallel for independent operations** - Improve throughput
300
+ 9. **Use router for classification** - Keep routing logic centralized
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module <%= @root_namespace %>
4
+ module Audio
5
+ <%- if class_name.include?("::") -%>
6
+ <%- class_name.split("::")[0..-2].each_with_index do |mod, i| -%>
7
+ <%= " " * (i + 2) %>module <%= mod %>
8
+ <%- end -%>
9
+ <%= " " * (class_name.split("::").length + 1) %>class <%= class_name.split("::").last %>Speaker < ApplicationSpeaker
10
+ <%- else -%>
11
+ class <%= class_name %>Speaker < ApplicationSpeaker
12
+ <%- end -%>
13
+ # Provider configuration
14
+ provider :<%= options[:provider] %>
15
+ <% if options[:model] -%>
16
+ model "<%= options[:model] %>"
17
+ <% end -%>
18
+ voice "<%= options[:voice] %>"
19
+ <% if options[:speed] != 1.0 -%>
20
+ speed <%= options[:speed] %>
21
+ <% end -%>
22
+ <% if options[:format] != "mp3" -%>
23
+ output_format :<%= options[:format] %>
24
+ <% end -%>
25
+ <% if options[:cache] -%>
26
+
27
+ # Caching
28
+ cache_for <%= options[:cache] %>
29
+ <% end -%>
30
+
31
+ # Optional: Custom pronunciation lexicon
32
+ # Define how specific words should be pronounced
33
+ #
34
+ # lexicon do
35
+ # pronounce 'API', 'A P I'
36
+ # pronounce 'SQL', 'sequel'
37
+ # end
38
+ <% if options[:provider].to_s == "elevenlabs" -%>
39
+
40
+ # ElevenLabs voice settings
41
+ # voice_settings do
42
+ # stability 0.5
43
+ # similarity_boost 0.75
44
+ # style 0.5
45
+ # speaker_boost true
46
+ # end
47
+ <% end -%>
48
+ <%- if class_name.include?("::") -%>
49
+ <%- (class_name.split("::").length + 1).times do |i| -%>
50
+ <%= " " * (class_name.split("::").length + 1 - i) %>end
51
+ <%- end -%>
52
+ <%- else -%>
53
+ end
54
+ <%- end -%>
55
+ end
56
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module <%= @root_namespace %>
4
+ module Audio
5
+ <%- if class_name.include?("::") -%>
6
+ <%- class_name.split("::")[0..-2].each_with_index do |mod, i| -%>
7
+ <%= " " * (i + 2) %>module <%= mod %>
8
+ <%- end -%>
9
+ <%= " " * (class_name.split("::").length + 1) %>class <%= class_name.split("::").last %>Transcriber < ApplicationTranscriber
10
+ <%- else -%>
11
+ class <%= class_name %>Transcriber < ApplicationTranscriber
12
+ <%- end -%>
13
+ # Model configuration
14
+ model "<%= options[:model] %>"
15
+ <% if options[:language] -%>
16
+ language "<%= options[:language] %>"
17
+ <% end -%>
18
+ <% if options[:output_format] != "text" -%>
19
+ output_format :<%= options[:output_format] %>
20
+ <% end -%>
21
+ <% if options[:cache] -%>
22
+
23
+ # Caching
24
+ cache_for <%= options[:cache] %>
25
+ <% end -%>
26
+
27
+ # Optional: Provide context to improve accuracy
28
+ # Override this method to give the model hints about the audio content
29
+ #
30
+ # def prompt
31
+ # "Technical discussion about Ruby programming"
32
+ # end
33
+
34
+ # Optional: Clean up transcription text
35
+ # Override this method to post-process the transcribed text
36
+ #
37
+ # def postprocess_text(text)
38
+ # text
39
+ # .gsub(/\bum\b/i, '') # Remove filler words
40
+ # .gsub(/\buh\b/i, '')
41
+ # .squeeze(' ') # Remove extra spaces
42
+ # end
43
+ <%- if class_name.include?("::") -%>
44
+ <%- (class_name.split("::").length + 1).times do |i| -%>
45
+ <%= " " * (class_name.split("::").length + 1 - i) %>end
46
+ <%- end -%>
47
+ <%- else -%>
48
+ end
49
+ <%- end -%>
50
+ end
51
+ end