ruby_llm-agents 0.4.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.
- checksums.yaml +4 -4
- data/README.md +225 -34
- data/app/controllers/ruby_llm/agents/agents_controller.rb +136 -16
- data/app/controllers/ruby_llm/agents/api_configurations_controller.rb +214 -0
- data/app/controllers/ruby_llm/agents/dashboard_controller.rb +29 -9
- data/app/controllers/ruby_llm/agents/{settings_controller.rb → system_config_controller.rb} +3 -3
- data/app/controllers/ruby_llm/agents/tenants_controller.rb +109 -0
- data/app/controllers/ruby_llm/agents/workflows_controller.rb +355 -0
- data/app/helpers/ruby_llm/agents/application_helper.rb +25 -0
- data/app/models/ruby_llm/agents/api_configuration.rb +386 -0
- data/app/models/ruby_llm/agents/execution.rb +3 -0
- data/app/models/ruby_llm/agents/tenant_budget.rb +112 -14
- data/app/services/ruby_llm/agents/agent_registry.rb +51 -12
- data/app/views/layouts/ruby_llm/agents/application.html.erb +5 -30
- data/app/views/ruby_llm/agents/agents/_agent.html.erb +13 -1
- data/app/views/ruby_llm/agents/agents/_config_agent.html.erb +235 -0
- data/app/views/ruby_llm/agents/agents/_config_embedder.html.erb +70 -0
- data/app/views/ruby_llm/agents/agents/_config_image_generator.html.erb +152 -0
- data/app/views/ruby_llm/agents/agents/_config_moderator.html.erb +63 -0
- data/app/views/ruby_llm/agents/agents/_config_speaker.html.erb +108 -0
- data/app/views/ruby_llm/agents/agents/_config_transcriber.html.erb +91 -0
- data/app/views/ruby_llm/agents/agents/_workflow.html.erb +1 -1
- data/app/views/ruby_llm/agents/agents/index.html.erb +74 -9
- data/app/views/ruby_llm/agents/agents/show.html.erb +18 -378
- data/app/views/ruby_llm/agents/api_configurations/_api_key_field.html.erb +34 -0
- data/app/views/ruby_llm/agents/api_configurations/_form.html.erb +288 -0
- data/app/views/ruby_llm/agents/api_configurations/edit.html.erb +95 -0
- data/app/views/ruby_llm/agents/api_configurations/edit_tenant.html.erb +97 -0
- data/app/views/ruby_llm/agents/api_configurations/show.html.erb +211 -0
- data/app/views/ruby_llm/agents/api_configurations/tenant.html.erb +179 -0
- data/app/views/ruby_llm/agents/dashboard/_action_center.html.erb +1 -1
- data/app/views/ruby_llm/agents/dashboard/_agent_comparison.html.erb +269 -15
- data/app/views/ruby_llm/agents/executions/show.html.erb +98 -0
- data/app/views/ruby_llm/agents/shared/_agent_type_badge.html.erb +93 -0
- data/app/views/ruby_llm/agents/{settings → system_config}/show.html.erb +1 -1
- data/app/views/ruby_llm/agents/tenants/_form.html.erb +150 -0
- data/app/views/ruby_llm/agents/tenants/edit.html.erb +13 -0
- data/app/views/ruby_llm/agents/tenants/index.html.erb +129 -0
- data/app/views/ruby_llm/agents/tenants/show.html.erb +374 -0
- data/app/views/ruby_llm/agents/workflows/_step_performance.html.erb +236 -0
- data/app/views/ruby_llm/agents/workflows/_structure_parallel.html.erb +76 -0
- data/app/views/ruby_llm/agents/workflows/_structure_pipeline.html.erb +74 -0
- data/app/views/ruby_llm/agents/workflows/_structure_router.html.erb +108 -0
- data/app/views/ruby_llm/agents/workflows/show.html.erb +442 -0
- data/config/routes.rb +13 -1
- data/lib/generators/ruby_llm_agents/agent_generator.rb +56 -7
- data/lib/generators/ruby_llm_agents/api_configuration_generator.rb +100 -0
- data/lib/generators/ruby_llm_agents/background_remover_generator.rb +110 -0
- data/lib/generators/ruby_llm_agents/embedder_generator.rb +107 -0
- data/lib/generators/ruby_llm_agents/image_analyzer_generator.rb +115 -0
- data/lib/generators/ruby_llm_agents/image_editor_generator.rb +108 -0
- data/lib/generators/ruby_llm_agents/image_generator_generator.rb +116 -0
- data/lib/generators/ruby_llm_agents/image_pipeline_generator.rb +178 -0
- data/lib/generators/ruby_llm_agents/image_transformer_generator.rb +109 -0
- data/lib/generators/ruby_llm_agents/image_upscaler_generator.rb +103 -0
- data/lib/generators/ruby_llm_agents/image_variator_generator.rb +102 -0
- data/lib/generators/ruby_llm_agents/install_generator.rb +76 -4
- data/lib/generators/ruby_llm_agents/restructure_generator.rb +292 -0
- data/lib/generators/ruby_llm_agents/speaker_generator.rb +121 -0
- data/lib/generators/ruby_llm_agents/templates/add_execution_type_migration.rb.tt +8 -0
- data/lib/generators/ruby_llm_agents/templates/agent.rb.tt +99 -84
- data/lib/generators/ruby_llm_agents/templates/application_agent.rb.tt +42 -40
- data/lib/generators/ruby_llm_agents/templates/application_background_remover.rb.tt +26 -0
- data/lib/generators/ruby_llm_agents/templates/application_embedder.rb.tt +50 -0
- data/lib/generators/ruby_llm_agents/templates/application_image_analyzer.rb.tt +26 -0
- data/lib/generators/ruby_llm_agents/templates/application_image_editor.rb.tt +20 -0
- data/lib/generators/ruby_llm_agents/templates/application_image_generator.rb.tt +38 -0
- data/lib/generators/ruby_llm_agents/templates/application_image_pipeline.rb.tt +139 -0
- data/lib/generators/ruby_llm_agents/templates/application_image_transformer.rb.tt +21 -0
- data/lib/generators/ruby_llm_agents/templates/application_image_upscaler.rb.tt +20 -0
- data/lib/generators/ruby_llm_agents/templates/application_image_variator.rb.tt +20 -0
- data/lib/generators/ruby_llm_agents/templates/application_speaker.rb.tt +49 -0
- data/lib/generators/ruby_llm_agents/templates/application_transcriber.rb.tt +53 -0
- data/lib/generators/ruby_llm_agents/templates/background_remover.rb.tt +44 -0
- data/lib/generators/ruby_llm_agents/templates/create_api_configurations_migration.rb.tt +90 -0
- data/lib/generators/ruby_llm_agents/templates/embedder.rb.tt +41 -0
- data/lib/generators/ruby_llm_agents/templates/image_analyzer.rb.tt +45 -0
- data/lib/generators/ruby_llm_agents/templates/image_editor.rb.tt +35 -0
- data/lib/generators/ruby_llm_agents/templates/image_generator.rb.tt +47 -0
- data/lib/generators/ruby_llm_agents/templates/image_pipeline.rb.tt +50 -0
- data/lib/generators/ruby_llm_agents/templates/image_transformer.rb.tt +44 -0
- data/lib/generators/ruby_llm_agents/templates/image_upscaler.rb.tt +38 -0
- data/lib/generators/ruby_llm_agents/templates/image_variator.rb.tt +33 -0
- data/lib/generators/ruby_llm_agents/templates/skills/AGENTS.md.tt +228 -0
- data/lib/generators/ruby_llm_agents/templates/skills/BACKGROUND_REMOVERS.md.tt +131 -0
- data/lib/generators/ruby_llm_agents/templates/skills/EMBEDDERS.md.tt +255 -0
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_ANALYZERS.md.tt +120 -0
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_EDITORS.md.tt +102 -0
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_GENERATORS.md.tt +282 -0
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_PIPELINES.md.tt +228 -0
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_TRANSFORMERS.md.tt +120 -0
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_UPSCALERS.md.tt +110 -0
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_VARIATORS.md.tt +120 -0
- data/lib/generators/ruby_llm_agents/templates/skills/SPEAKERS.md.tt +212 -0
- data/lib/generators/ruby_llm_agents/templates/skills/TOOLS.md.tt +227 -0
- data/lib/generators/ruby_llm_agents/templates/skills/TRANSCRIBERS.md.tt +251 -0
- data/lib/generators/ruby_llm_agents/templates/skills/WORKFLOWS.md.tt +300 -0
- data/lib/generators/ruby_llm_agents/templates/speaker.rb.tt +56 -0
- data/lib/generators/ruby_llm_agents/templates/transcriber.rb.tt +51 -0
- data/lib/generators/ruby_llm_agents/transcriber_generator.rb +107 -0
- data/lib/generators/ruby_llm_agents/upgrade_generator.rb +152 -1
- data/lib/ruby_llm/agents/audio/speaker.rb +553 -0
- data/lib/ruby_llm/agents/audio/transcriber.rb +669 -0
- data/lib/ruby_llm/agents/base_agent.rb +675 -0
- data/lib/ruby_llm/agents/core/base/moderation_dsl.rb +181 -0
- data/lib/ruby_llm/agents/core/base/moderation_execution.rb +274 -0
- data/lib/ruby_llm/agents/core/base.rb +135 -0
- data/lib/ruby_llm/agents/core/configuration.rb +981 -0
- data/lib/ruby_llm/agents/core/errors.rb +150 -0
- data/lib/ruby_llm/agents/{instrumentation.rb → core/instrumentation.rb} +93 -4
- data/lib/ruby_llm/agents/core/llm_tenant.rb +358 -0
- data/lib/ruby_llm/agents/core/resolved_config.rb +348 -0
- data/lib/ruby_llm/agents/{version.rb → core/version.rb} +1 -1
- data/lib/ruby_llm/agents/dsl/base.rb +110 -0
- data/lib/ruby_llm/agents/dsl/caching.rb +142 -0
- data/lib/ruby_llm/agents/dsl/reliability.rb +307 -0
- data/lib/ruby_llm/agents/dsl.rb +41 -0
- data/lib/ruby_llm/agents/image/analyzer/dsl.rb +130 -0
- data/lib/ruby_llm/agents/image/analyzer/execution.rb +402 -0
- data/lib/ruby_llm/agents/image/analyzer.rb +90 -0
- data/lib/ruby_llm/agents/image/background_remover/dsl.rb +154 -0
- data/lib/ruby_llm/agents/image/background_remover/execution.rb +240 -0
- data/lib/ruby_llm/agents/image/background_remover.rb +89 -0
- data/lib/ruby_llm/agents/image/concerns/image_operation_dsl.rb +91 -0
- data/lib/ruby_llm/agents/image/concerns/image_operation_execution.rb +165 -0
- data/lib/ruby_llm/agents/image/editor/dsl.rb +56 -0
- data/lib/ruby_llm/agents/image/editor/execution.rb +207 -0
- data/lib/ruby_llm/agents/image/editor.rb +92 -0
- data/lib/ruby_llm/agents/image/generator/active_storage_support.rb +127 -0
- data/lib/ruby_llm/agents/image/generator/content_policy.rb +95 -0
- data/lib/ruby_llm/agents/image/generator/pricing.rb +353 -0
- data/lib/ruby_llm/agents/image/generator/templates.rb +124 -0
- data/lib/ruby_llm/agents/image/generator.rb +455 -0
- data/lib/ruby_llm/agents/image/pipeline/dsl.rb +213 -0
- data/lib/ruby_llm/agents/image/pipeline/execution.rb +382 -0
- data/lib/ruby_llm/agents/image/pipeline.rb +97 -0
- data/lib/ruby_llm/agents/image/transformer/dsl.rb +148 -0
- data/lib/ruby_llm/agents/image/transformer/execution.rb +223 -0
- data/lib/ruby_llm/agents/image/transformer.rb +95 -0
- data/lib/ruby_llm/agents/image/upscaler/dsl.rb +83 -0
- data/lib/ruby_llm/agents/image/upscaler/execution.rb +219 -0
- data/lib/ruby_llm/agents/image/upscaler.rb +81 -0
- data/lib/ruby_llm/agents/image/variator/dsl.rb +62 -0
- data/lib/ruby_llm/agents/image/variator/execution.rb +189 -0
- data/lib/ruby_llm/agents/image/variator.rb +80 -0
- data/lib/ruby_llm/agents/{alert_manager.rb → infrastructure/alert_manager.rb} +17 -22
- data/lib/ruby_llm/agents/infrastructure/budget/budget_query.rb +145 -0
- data/lib/ruby_llm/agents/infrastructure/budget/config_resolver.rb +149 -0
- data/lib/ruby_llm/agents/infrastructure/budget/forecaster.rb +68 -0
- data/lib/ruby_llm/agents/infrastructure/budget/spend_recorder.rb +279 -0
- data/lib/ruby_llm/agents/infrastructure/budget_tracker.rb +275 -0
- data/lib/ruby_llm/agents/{execution_logger_job.rb → infrastructure/execution_logger_job.rb} +17 -1
- data/lib/ruby_llm/agents/{reliability → infrastructure/reliability}/executor.rb +2 -1
- data/lib/ruby_llm/agents/{reliability → infrastructure/reliability}/retry_strategy.rb +9 -3
- data/lib/ruby_llm/agents/{reliability.rb → infrastructure/reliability.rb} +11 -21
- data/lib/ruby_llm/agents/pipeline/builder.rb +215 -0
- data/lib/ruby_llm/agents/pipeline/context.rb +255 -0
- data/lib/ruby_llm/agents/pipeline/executor.rb +86 -0
- data/lib/ruby_llm/agents/pipeline/middleware/base.rb +124 -0
- data/lib/ruby_llm/agents/pipeline/middleware/budget.rb +95 -0
- data/lib/ruby_llm/agents/pipeline/middleware/cache.rb +171 -0
- data/lib/ruby_llm/agents/pipeline/middleware/instrumentation.rb +415 -0
- data/lib/ruby_llm/agents/pipeline/middleware/reliability.rb +276 -0
- data/lib/ruby_llm/agents/pipeline/middleware/tenant.rb +196 -0
- data/lib/ruby_llm/agents/pipeline.rb +68 -0
- data/lib/ruby_llm/agents/{engine.rb → rails/engine.rb} +79 -10
- data/lib/ruby_llm/agents/results/background_removal_result.rb +286 -0
- data/lib/ruby_llm/agents/{result.rb → results/base.rb} +73 -1
- data/lib/ruby_llm/agents/results/embedding_result.rb +243 -0
- data/lib/ruby_llm/agents/results/image_analysis_result.rb +314 -0
- data/lib/ruby_llm/agents/results/image_edit_result.rb +250 -0
- data/lib/ruby_llm/agents/results/image_generation_result.rb +346 -0
- data/lib/ruby_llm/agents/results/image_pipeline_result.rb +399 -0
- data/lib/ruby_llm/agents/results/image_transform_result.rb +251 -0
- data/lib/ruby_llm/agents/results/image_upscale_result.rb +255 -0
- data/lib/ruby_llm/agents/results/image_variation_result.rb +237 -0
- data/lib/ruby_llm/agents/results/moderation_result.rb +158 -0
- data/lib/ruby_llm/agents/results/speech_result.rb +338 -0
- data/lib/ruby_llm/agents/results/transcription_result.rb +408 -0
- data/lib/ruby_llm/agents/text/embedder.rb +444 -0
- data/lib/ruby_llm/agents/text/moderator.rb +237 -0
- data/lib/ruby_llm/agents/workflow/async.rb +220 -0
- data/lib/ruby_llm/agents/workflow/async_executor.rb +156 -0
- data/lib/ruby_llm/agents/{workflow.rb → workflow/orchestrator.rb} +6 -5
- data/lib/ruby_llm/agents/workflow/parallel.rb +34 -17
- data/lib/ruby_llm/agents/workflow/thread_pool.rb +185 -0
- data/lib/ruby_llm/agents.rb +86 -20
- metadata +189 -35
- data/lib/ruby_llm/agents/base/caching.rb +0 -40
- data/lib/ruby_llm/agents/base/cost_calculation.rb +0 -105
- data/lib/ruby_llm/agents/base/dsl.rb +0 -324
- data/lib/ruby_llm/agents/base/execution.rb +0 -283
- data/lib/ruby_llm/agents/base/reliability_dsl.rb +0 -82
- data/lib/ruby_llm/agents/base/reliability_execution.rb +0 -136
- data/lib/ruby_llm/agents/base/response_building.rb +0 -86
- data/lib/ruby_llm/agents/base/tool_tracking.rb +0 -57
- data/lib/ruby_llm/agents/base.rb +0 -209
- data/lib/ruby_llm/agents/budget_tracker.rb +0 -471
- data/lib/ruby_llm/agents/configuration.rb +0 -357
- /data/lib/ruby_llm/agents/{deprecations.rb → core/deprecations.rb} +0 -0
- /data/lib/ruby_llm/agents/{inflections.rb → core/inflections.rb} +0 -0
- /data/lib/ruby_llm/agents/{attempt_tracker.rb → infrastructure/attempt_tracker.rb} +0 -0
- /data/lib/ruby_llm/agents/{cache_helper.rb → infrastructure/cache_helper.rb} +0 -0
- /data/lib/ruby_llm/agents/{circuit_breaker.rb → infrastructure/circuit_breaker.rb} +0 -0
- /data/lib/ruby_llm/agents/{redactor.rb → infrastructure/redactor.rb} +0 -0
- /data/lib/ruby_llm/agents/{reliability → infrastructure/reliability}/breaker_manager.rb +0 -0
- /data/lib/ruby_llm/agents/{reliability → infrastructure/reliability}/execution_constraints.rb +0 -0
- /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
|