ruby_llm-agents 1.3.4 → 2.1.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 +112 -336
- data/app/controllers/concerns/ruby_llm/agents/sortable.rb +0 -1
- data/app/controllers/ruby_llm/agents/agents_controller.rb +5 -56
- data/app/controllers/ruby_llm/agents/dashboard_controller.rb +22 -106
- data/app/controllers/ruby_llm/agents/executions_controller.rb +4 -114
- data/app/controllers/ruby_llm/agents/tenants_controller.rb +30 -2
- data/app/helpers/ruby_llm/agents/application_helper.rb +19 -53
- data/app/models/ruby_llm/agents/execution/analytics.rb +13 -54
- data/app/models/ruby_llm/agents/execution/scopes.rb +61 -14
- data/app/models/ruby_llm/agents/execution.rb +52 -12
- data/app/models/ruby_llm/agents/execution_detail.rb +18 -0
- data/app/models/ruby_llm/agents/tenant/budgetable.rb +132 -24
- data/app/models/ruby_llm/agents/tenant/incrementable.rb +117 -0
- data/app/models/ruby_llm/agents/tenant/resettable.rb +128 -0
- data/app/models/ruby_llm/agents/tenant/trackable.rb +46 -12
- data/app/models/ruby_llm/agents/tenant.rb +2 -3
- data/app/models/ruby_llm/agents/tenant_budget.rb +6 -3
- data/app/services/ruby_llm/agents/agent_registry.rb +6 -112
- data/app/views/layouts/ruby_llm/agents/application.html.erb +89 -252
- data/app/views/ruby_llm/agents/agents/_config_agent.html.erb +71 -218
- data/app/views/ruby_llm/agents/agents/_config_embedder.html.erb +20 -63
- data/app/views/ruby_llm/agents/agents/_config_image_generator.html.erb +44 -131
- data/app/views/ruby_llm/agents/agents/_config_moderator.html.erb +16 -57
- data/app/views/ruby_llm/agents/agents/_config_speaker.html.erb +39 -104
- data/app/views/ruby_llm/agents/agents/_config_transcriber.html.erb +29 -82
- data/app/views/ruby_llm/agents/agents/_empty_state.html.erb +4 -14
- data/app/views/ruby_llm/agents/agents/index.html.erb +105 -274
- data/app/views/ruby_llm/agents/agents/show.html.erb +248 -378
- data/app/views/ruby_llm/agents/dashboard/_action_center.html.erb +29 -52
- data/app/views/ruby_llm/agents/dashboard/_tenant_budget.html.erb +73 -99
- data/app/views/ruby_llm/agents/dashboard/index.html.erb +228 -433
- data/app/views/ruby_llm/agents/executions/_execution.html.erb +1 -1
- data/app/views/ruby_llm/agents/executions/_filters.html.erb +4 -25
- data/app/views/ruby_llm/agents/executions/_list.html.erb +111 -152
- data/app/views/ruby_llm/agents/executions/index.html.erb +5 -7
- data/app/views/ruby_llm/agents/executions/show.html.erb +526 -1037
- data/app/views/ruby_llm/agents/shared/_agent_type_badge.html.erb +5 -21
- data/app/views/ruby_llm/agents/shared/_executions_table.html.erb +70 -191
- data/app/views/ruby_llm/agents/shared/_filter_dropdown.html.erb +16 -44
- data/app/views/ruby_llm/agents/shared/_select_dropdown.html.erb +12 -41
- data/app/views/ruby_llm/agents/shared/_status_badge.html.erb +11 -65
- data/app/views/ruby_llm/agents/shared/_tenant_filter.html.erb +6 -5
- data/app/views/ruby_llm/agents/system_config/show.html.erb +240 -351
- data/app/views/ruby_llm/agents/tenants/_form.html.erb +67 -77
- data/app/views/ruby_llm/agents/tenants/edit.html.erb +7 -9
- data/app/views/ruby_llm/agents/tenants/index.html.erb +100 -122
- data/app/views/ruby_llm/agents/tenants/show.html.erb +146 -336
- data/config/routes.rb +0 -13
- data/lib/generators/ruby_llm_agents/install_generator.rb +13 -17
- data/lib/generators/ruby_llm_agents/migrate_structure_generator.rb +2 -12
- data/lib/generators/ruby_llm_agents/restructure_generator.rb +0 -2
- data/lib/generators/ruby_llm_agents/templates/add_usage_counters_to_tenants_migration.rb.tt +37 -0
- data/lib/generators/ruby_llm_agents/templates/agent.rb.tt +1 -2
- data/lib/generators/ruby_llm_agents/templates/application_agent.rb.tt +1 -1
- data/lib/generators/ruby_llm_agents/templates/application_image_pipeline.rb.tt +0 -1
- data/lib/generators/ruby_llm_agents/templates/create_execution_details_migration.rb.tt +27 -0
- data/lib/generators/ruby_llm_agents/templates/create_tenants_migration.rb.tt +25 -0
- data/lib/generators/ruby_llm_agents/templates/image_pipeline.rb.tt +0 -1
- data/lib/generators/ruby_llm_agents/templates/initializer.rb.tt +33 -12
- data/lib/generators/ruby_llm_agents/templates/migration.rb.tt +40 -71
- data/lib/generators/ruby_llm_agents/templates/remove_agent_version_migration.rb.tt +13 -0
- data/lib/generators/ruby_llm_agents/templates/remove_workflow_columns_migration.rb.tt +19 -0
- data/lib/generators/ruby_llm_agents/templates/skills/AGENTS.md.tt +2 -4
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_PIPELINES.md.tt +0 -1
- data/lib/generators/ruby_llm_agents/templates/split_execution_details_migration.rb.tt +232 -0
- data/lib/generators/ruby_llm_agents/upgrade_generator.rb +77 -259
- data/lib/ruby_llm/agents/audio/speaker.rb +0 -1
- data/lib/ruby_llm/agents/audio/transcriber.rb +0 -1
- data/lib/ruby_llm/agents/base_agent.rb +54 -23
- data/lib/ruby_llm/agents/core/base/callbacks.rb +142 -0
- data/lib/ruby_llm/agents/core/base.rb +23 -55
- data/lib/ruby_llm/agents/core/configuration.rb +97 -117
- data/lib/ruby_llm/agents/core/errors.rb +0 -58
- data/lib/ruby_llm/agents/core/instrumentation.rb +157 -110
- data/lib/ruby_llm/agents/core/llm_tenant.rb +8 -7
- data/lib/ruby_llm/agents/core/version.rb +1 -1
- data/lib/ruby_llm/agents/dsl/base.rb +157 -17
- data/lib/ruby_llm/agents/dsl/caching.rb +33 -2
- data/lib/ruby_llm/agents/dsl/reliability.rb +148 -0
- data/lib/ruby_llm/agents/dsl.rb +1 -2
- data/lib/ruby_llm/agents/image/analyzer/execution.rb +1 -2
- data/lib/ruby_llm/agents/image/background_remover/execution.rb +1 -2
- data/lib/ruby_llm/agents/image/concerns/image_operation_dsl.rb +1 -13
- data/lib/ruby_llm/agents/image/concerns/image_operation_execution.rb +2 -2
- data/lib/ruby_llm/agents/image/editor/dsl.rb +0 -14
- data/lib/ruby_llm/agents/image/editor/execution.rb +1 -10
- data/lib/ruby_llm/agents/image/editor.rb +0 -1
- data/lib/ruby_llm/agents/image/generator.rb +0 -21
- data/lib/ruby_llm/agents/image/pipeline/dsl.rb +0 -13
- data/lib/ruby_llm/agents/image/pipeline/execution.rb +0 -1
- data/lib/ruby_llm/agents/image/transformer/dsl.rb +0 -13
- data/lib/ruby_llm/agents/image/transformer/execution.rb +1 -10
- data/lib/ruby_llm/agents/image/transformer.rb +0 -1
- data/lib/ruby_llm/agents/image/upscaler/execution.rb +1 -2
- data/lib/ruby_llm/agents/image/variator/execution.rb +1 -2
- data/lib/ruby_llm/agents/infrastructure/alert_manager.rb +78 -173
- data/lib/ruby_llm/agents/infrastructure/budget/budget_query.rb +66 -2
- data/lib/ruby_llm/agents/infrastructure/budget/spend_recorder.rb +0 -12
- data/lib/ruby_llm/agents/infrastructure/circuit_breaker.rb +10 -13
- data/lib/ruby_llm/agents/infrastructure/execution_logger_job.rb +8 -0
- data/lib/ruby_llm/agents/pipeline/context.rb +0 -1
- data/lib/ruby_llm/agents/pipeline/middleware/budget.rb +28 -4
- data/lib/ruby_llm/agents/pipeline/middleware/cache.rb +3 -10
- data/lib/ruby_llm/agents/pipeline/middleware/instrumentation.rb +88 -55
- data/lib/ruby_llm/agents/pipeline/middleware/tenant.rb +5 -41
- data/lib/ruby_llm/agents/rails/engine.rb +6 -6
- data/lib/ruby_llm/agents/results/base.rb +1 -49
- data/lib/ruby_llm/agents/text/embedder.rb +0 -1
- data/lib/ruby_llm/agents.rb +1 -9
- data/lib/tasks/ruby_llm_agents.rake +34 -0
- metadata +14 -83
- data/app/controllers/ruby_llm/agents/api_configurations_controller.rb +0 -214
- data/app/controllers/ruby_llm/agents/workflows_controller.rb +0 -544
- data/app/mailers/ruby_llm/agents/alert_mailer.rb +0 -84
- data/app/mailers/ruby_llm/agents/application_mailer.rb +0 -28
- data/app/models/ruby_llm/agents/api_configuration.rb +0 -386
- data/app/models/ruby_llm/agents/execution/workflow.rb +0 -170
- data/app/models/ruby_llm/agents/tenant/configurable.rb +0 -135
- data/app/views/ruby_llm/agents/agents/_agent.html.erb +0 -98
- data/app/views/ruby_llm/agents/agents/_version_comparison.html.erb +0 -186
- data/app/views/ruby_llm/agents/agents/_workflow.html.erb +0 -126
- data/app/views/ruby_llm/agents/alert_mailer/alert_notification.html.erb +0 -107
- data/app/views/ruby_llm/agents/alert_mailer/alert_notification.text.erb +0 -18
- data/app/views/ruby_llm/agents/api_configurations/_api_key_field.html.erb +0 -34
- data/app/views/ruby_llm/agents/api_configurations/_form.html.erb +0 -288
- data/app/views/ruby_llm/agents/api_configurations/edit.html.erb +0 -95
- data/app/views/ruby_llm/agents/api_configurations/edit_tenant.html.erb +0 -97
- data/app/views/ruby_llm/agents/api_configurations/show.html.erb +0 -214
- data/app/views/ruby_llm/agents/api_configurations/tenant.html.erb +0 -179
- data/app/views/ruby_llm/agents/dashboard/_agent_comparison.html.erb +0 -73
- data/app/views/ruby_llm/agents/dashboard/_alerts_feed.html.erb +0 -62
- data/app/views/ruby_llm/agents/dashboard/_breaker_strip.html.erb +0 -47
- data/app/views/ruby_llm/agents/dashboard/_budgets_bar.html.erb +0 -75
- data/app/views/ruby_llm/agents/dashboard/_model_comparison.html.erb +0 -56
- data/app/views/ruby_llm/agents/dashboard/_model_cost_breakdown.html.erb +0 -115
- data/app/views/ruby_llm/agents/dashboard/_now_strip.html.erb +0 -59
- data/app/views/ruby_llm/agents/dashboard/_top_errors.html.erb +0 -60
- data/app/views/ruby_llm/agents/executions/_workflow_summary.html.erb +0 -86
- data/app/views/ruby_llm/agents/executions/dry_run.html.erb +0 -149
- data/app/views/ruby_llm/agents/shared/_breadcrumbs.html.erb +0 -48
- data/app/views/ruby_llm/agents/shared/_nav_link.html.erb +0 -27
- data/app/views/ruby_llm/agents/shared/_stat_card.html.erb +0 -14
- data/app/views/ruby_llm/agents/shared/_workflow_type_badge.html.erb +0 -35
- data/app/views/ruby_llm/agents/workflows/_empty_state.html.erb +0 -22
- data/app/views/ruby_llm/agents/workflows/_step_performance.html.erb +0 -228
- data/app/views/ruby_llm/agents/workflows/_structure_dsl.html.erb +0 -539
- data/app/views/ruby_llm/agents/workflows/_structure_parallel.html.erb +0 -76
- data/app/views/ruby_llm/agents/workflows/_structure_pipeline.html.erb +0 -74
- data/app/views/ruby_llm/agents/workflows/_structure_router.html.erb +0 -108
- data/app/views/ruby_llm/agents/workflows/_workflow_diagram.html.erb +0 -920
- data/app/views/ruby_llm/agents/workflows/index.html.erb +0 -179
- data/app/views/ruby_llm/agents/workflows/show.html.erb +0 -467
- data/lib/generators/ruby_llm_agents/api_configuration_generator.rb +0 -100
- data/lib/generators/ruby_llm_agents/templates/add_workflow_migration.rb.tt +0 -38
- data/lib/generators/ruby_llm_agents/templates/application_workflow.rb.tt +0 -48
- data/lib/generators/ruby_llm_agents/templates/create_api_configurations_migration.rb.tt +0 -90
- data/lib/generators/ruby_llm_agents/templates/skills/WORKFLOWS.md.tt +0 -551
- data/lib/ruby_llm/agents/core/base/moderation_dsl.rb +0 -181
- data/lib/ruby_llm/agents/core/base/moderation_execution.rb +0 -274
- data/lib/ruby_llm/agents/core/resolved_config.rb +0 -348
- data/lib/ruby_llm/agents/image/generator/content_policy.rb +0 -95
- data/lib/ruby_llm/agents/infrastructure/redactor.rb +0 -130
- data/lib/ruby_llm/agents/results/moderation_result.rb +0 -158
- data/lib/ruby_llm/agents/text/moderator.rb +0 -237
- data/lib/ruby_llm/agents/workflow/approval.rb +0 -205
- data/lib/ruby_llm/agents/workflow/approval_store.rb +0 -179
- data/lib/ruby_llm/agents/workflow/async.rb +0 -220
- data/lib/ruby_llm/agents/workflow/async_executor.rb +0 -156
- data/lib/ruby_llm/agents/workflow/dsl/executor.rb +0 -467
- data/lib/ruby_llm/agents/workflow/dsl/input_schema.rb +0 -244
- data/lib/ruby_llm/agents/workflow/dsl/iteration_executor.rb +0 -289
- data/lib/ruby_llm/agents/workflow/dsl/parallel_group.rb +0 -107
- data/lib/ruby_llm/agents/workflow/dsl/route_builder.rb +0 -150
- data/lib/ruby_llm/agents/workflow/dsl/schedule_helpers.rb +0 -187
- data/lib/ruby_llm/agents/workflow/dsl/step_config.rb +0 -352
- data/lib/ruby_llm/agents/workflow/dsl/step_executor.rb +0 -415
- data/lib/ruby_llm/agents/workflow/dsl/wait_config.rb +0 -257
- data/lib/ruby_llm/agents/workflow/dsl/wait_executor.rb +0 -317
- data/lib/ruby_llm/agents/workflow/dsl.rb +0 -576
- data/lib/ruby_llm/agents/workflow/instrumentation.rb +0 -249
- data/lib/ruby_llm/agents/workflow/notifiers/base.rb +0 -117
- data/lib/ruby_llm/agents/workflow/notifiers/email.rb +0 -117
- data/lib/ruby_llm/agents/workflow/notifiers/slack.rb +0 -180
- data/lib/ruby_llm/agents/workflow/notifiers/webhook.rb +0 -121
- data/lib/ruby_llm/agents/workflow/notifiers.rb +0 -70
- data/lib/ruby_llm/agents/workflow/orchestrator.rb +0 -416
- data/lib/ruby_llm/agents/workflow/result.rb +0 -592
- data/lib/ruby_llm/agents/workflow/thread_pool.rb +0 -185
- data/lib/ruby_llm/agents/workflow/throttle_manager.rb +0 -206
- data/lib/ruby_llm/agents/workflow/wait_result.rb +0 -213
|
@@ -1,551 +0,0 @@
|
|
|
1
|
-
# 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
|
-
| **DSL Workflow** | `Workflow` | Declarative DSL for mixed sequential/parallel/routing |
|
|
10
|
-
| Pipeline | `Workflow::Pipeline` | Legacy: Sequential execution, data flows between steps |
|
|
11
|
-
| Parallel | `Workflow::Parallel` | Legacy: Concurrent execution with aggregation |
|
|
12
|
-
| Router | `Workflow::Router` | Legacy: Conditional dispatch based on classification |
|
|
13
|
-
|
|
14
|
-
## Declarative DSL Workflows (Recommended)
|
|
15
|
-
|
|
16
|
-
The new declarative DSL provides a clean, expressive syntax for defining workflows with minimal boilerplate. It supports sequential steps, parallel execution, conditional routing, and input validation all in one workflow class.
|
|
17
|
-
|
|
18
|
-
### Minimal Workflow
|
|
19
|
-
|
|
20
|
-
```ruby
|
|
21
|
-
module Workflows
|
|
22
|
-
class SimpleWorkflow < RubyLLM::Agents::Workflow
|
|
23
|
-
step :fetch, FetcherAgent
|
|
24
|
-
step :process, ProcessorAgent
|
|
25
|
-
step :save, SaverAgent
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# Usage
|
|
30
|
-
result = Workflows::SimpleWorkflow.call(order_id: "ORD-123")
|
|
31
|
-
result.success? # => true
|
|
32
|
-
result.content # => final step output
|
|
33
|
-
result.steps[:process] # => ProcessorAgent result
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
### Full-Featured Workflow
|
|
37
|
-
|
|
38
|
-
```ruby
|
|
39
|
-
module Workflows
|
|
40
|
-
class OrderWorkflow < RubyLLM::Agents::Workflow
|
|
41
|
-
description "Process customer orders end-to-end"
|
|
42
|
-
version "2.0"
|
|
43
|
-
timeout 300
|
|
44
|
-
max_cost 2.00
|
|
45
|
-
|
|
46
|
-
# Input validation with defaults
|
|
47
|
-
input do
|
|
48
|
-
required :order_id, String
|
|
49
|
-
required :user_id, Integer
|
|
50
|
-
optional :priority, String, default: "normal"
|
|
51
|
-
optional :expedited, :boolean, default: false
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# Sequential steps
|
|
55
|
-
step :fetch, FetcherAgent, timeout: 30
|
|
56
|
-
step :validate, ValidatorAgent
|
|
57
|
-
|
|
58
|
-
# Conditional routing based on previous step result
|
|
59
|
-
step :process, on: -> { validate.tier } do |route|
|
|
60
|
-
route.premium PremiumProcessorAgent
|
|
61
|
-
route.standard StandardProcessorAgent
|
|
62
|
-
route.default BasicProcessorAgent
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
# Parallel execution block
|
|
66
|
-
parallel do
|
|
67
|
-
step :analyze, AnalyzerAgent
|
|
68
|
-
step :summarize, SummarizerAgent
|
|
69
|
-
step :notify, NotifierAgent
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
# Conditional step execution
|
|
73
|
-
step :expedite, ExpediteAgent, if: :expedited?
|
|
74
|
-
|
|
75
|
-
private
|
|
76
|
-
|
|
77
|
-
def expedited?
|
|
78
|
-
input.expedited == true
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
### DSL Reference
|
|
85
|
-
|
|
86
|
-
#### Input Schema
|
|
87
|
-
|
|
88
|
-
Define required and optional inputs with type validation:
|
|
89
|
-
|
|
90
|
-
```ruby
|
|
91
|
-
input do
|
|
92
|
-
required :order_id, String
|
|
93
|
-
required :amount, Numeric
|
|
94
|
-
optional :priority, String, default: "normal"
|
|
95
|
-
optional :debug, :boolean, default: false
|
|
96
|
-
|
|
97
|
-
# With enum validation
|
|
98
|
-
optional :status, String, in: %w[pending active completed]
|
|
99
|
-
end
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
#### Step Options
|
|
103
|
-
|
|
104
|
-
```ruby
|
|
105
|
-
# Basic step
|
|
106
|
-
step :name, AgentClass
|
|
107
|
-
|
|
108
|
-
# With description
|
|
109
|
-
step :validate, ValidatorAgent, "Validates order data"
|
|
110
|
-
|
|
111
|
-
# With timeout (seconds)
|
|
112
|
-
step :fetch, FetcherAgent, timeout: 30
|
|
113
|
-
|
|
114
|
-
# Optional step (workflow continues on failure)
|
|
115
|
-
step :enrich, EnricherAgent, optional: true
|
|
116
|
-
|
|
117
|
-
# With default value on failure
|
|
118
|
-
step :lookup, LookupAgent, optional: true, default: { found: false }
|
|
119
|
-
|
|
120
|
-
# Conditional execution
|
|
121
|
-
step :notify, NotifierAgent, if: :should_notify?
|
|
122
|
-
step :skip_this, SkipAgent, unless: :condition_met?
|
|
123
|
-
|
|
124
|
-
# With retry
|
|
125
|
-
step :api_call, ApiAgent, retry: 3
|
|
126
|
-
step :api_call, ApiAgent, retry: { max: 3, delay: 2, backoff: :exponential }
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
#### Conditional Routing
|
|
130
|
-
|
|
131
|
-
Route to different agents based on a value:
|
|
132
|
-
|
|
133
|
-
```ruby
|
|
134
|
-
# Route based on lambda returning a value
|
|
135
|
-
step :process, on: -> { validate.tier } do |route|
|
|
136
|
-
route.premium PremiumAgent
|
|
137
|
-
route.standard StandardAgent
|
|
138
|
-
route.default FallbackAgent # Required fallback
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
# With custom input mapping per route
|
|
142
|
-
step :handle, on: -> { classify.type } do |route|
|
|
143
|
-
route.billing BillingAgent, input: -> { { account: input.account_id } }
|
|
144
|
-
route.support SupportAgent, input: -> { { ticket: input.ticket_id } }
|
|
145
|
-
route.default GeneralAgent
|
|
146
|
-
end
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
#### Parallel Execution
|
|
150
|
-
|
|
151
|
-
Execute steps concurrently:
|
|
152
|
-
|
|
153
|
-
```ruby
|
|
154
|
-
# Anonymous parallel group
|
|
155
|
-
parallel do
|
|
156
|
-
step :sentiment, SentimentAgent
|
|
157
|
-
step :keywords, KeywordAgent
|
|
158
|
-
step :entities, EntityAgent
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
# Named parallel group with options
|
|
162
|
-
parallel :analysis, fail_fast: true, concurrency: 2 do
|
|
163
|
-
step :deep_analyze, DeepAnalyzer
|
|
164
|
-
step :quick_scan, QuickScanner
|
|
165
|
-
end
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
#### Lifecycle Hooks
|
|
169
|
-
|
|
170
|
-
```ruby
|
|
171
|
-
class MyWorkflow < RubyLLM::Agents::Workflow
|
|
172
|
-
step :process, ProcessorAgent
|
|
173
|
-
step :save, SaverAgent
|
|
174
|
-
|
|
175
|
-
# Workflow-level hooks
|
|
176
|
-
before_workflow { Rails.logger.info("Starting workflow") }
|
|
177
|
-
after_workflow { Rails.logger.info("Workflow complete") }
|
|
178
|
-
|
|
179
|
-
# Step-level hooks
|
|
180
|
-
before_step(:process) { |context| log_step_start(:process) }
|
|
181
|
-
after_step(:process) { |result, duration_ms| log_step_end(:process, duration_ms) }
|
|
182
|
-
|
|
183
|
-
# Error hooks
|
|
184
|
-
on_step_failure(:process) { |error, context| handle_error(error) }
|
|
185
|
-
end
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
#### Accessing Step Results
|
|
189
|
-
|
|
190
|
-
Within the workflow, access previous step results:
|
|
191
|
-
|
|
192
|
-
```ruby
|
|
193
|
-
step :validate, ValidatorAgent
|
|
194
|
-
|
|
195
|
-
# In routing lambda
|
|
196
|
-
step :process, on: -> { validate.tier } do |route|
|
|
197
|
-
# validate.tier returns validate step's result[:tier]
|
|
198
|
-
end
|
|
199
|
-
|
|
200
|
-
# In condition methods
|
|
201
|
-
def should_notify?
|
|
202
|
-
validate.valid? && input.callback_url.present?
|
|
203
|
-
end
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
### Workflow Result API
|
|
207
|
-
|
|
208
|
-
```ruby
|
|
209
|
-
result = MyWorkflow.call(order_id: "123")
|
|
210
|
-
|
|
211
|
-
# Status
|
|
212
|
-
result.success? # All steps succeeded
|
|
213
|
-
result.partial? # Some optional steps failed
|
|
214
|
-
result.error? # Required step failed
|
|
215
|
-
result.status # "success", "partial", or "error"
|
|
216
|
-
|
|
217
|
-
# Content
|
|
218
|
-
result.content # Final step output
|
|
219
|
-
|
|
220
|
-
# Step results
|
|
221
|
-
result.steps # Hash of all step results
|
|
222
|
-
result.steps[:validate] # Specific step result
|
|
223
|
-
result.steps[:validate].content # Step output content
|
|
224
|
-
|
|
225
|
-
# Metrics
|
|
226
|
-
result.total_cost # Combined cost of all steps
|
|
227
|
-
result.input_tokens # Total input tokens
|
|
228
|
-
result.output_tokens # Total output tokens
|
|
229
|
-
result.duration_ms # Total workflow duration
|
|
230
|
-
|
|
231
|
-
# Errors
|
|
232
|
-
result.errors # Hash of step errors
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
### Dry Run / Validation
|
|
236
|
-
|
|
237
|
-
Validate workflow configuration without executing:
|
|
238
|
-
|
|
239
|
-
```ruby
|
|
240
|
-
validation = MyWorkflow.dry_run(order_id: "123")
|
|
241
|
-
|
|
242
|
-
validation[:valid] # true/false
|
|
243
|
-
validation[:input_errors] # Array of validation errors
|
|
244
|
-
validation[:steps] # List of step names
|
|
245
|
-
validation[:agents] # List of agent classes
|
|
246
|
-
validation[:parallel_groups] # Parallel group info
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
## Creating Workflows (Legacy Patterns)
|
|
250
|
-
|
|
251
|
-
### Pipeline (Sequential)
|
|
252
|
-
|
|
253
|
-
Execute agents in order, passing each step's output to the next:
|
|
254
|
-
|
|
255
|
-
```ruby
|
|
256
|
-
module Workflows
|
|
257
|
-
class ContentPipeline < RubyLLM::Agents::Workflow::Pipeline
|
|
258
|
-
version "1.0"
|
|
259
|
-
timeout 120
|
|
260
|
-
max_cost 0.50
|
|
261
|
-
|
|
262
|
-
step :extract, agent: ExtractorAgent
|
|
263
|
-
step :validate, agent: ValidatorAgent
|
|
264
|
-
step :format, agent: FormatterAgent
|
|
265
|
-
end
|
|
266
|
-
end
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
### Parallel (Concurrent)
|
|
270
|
-
|
|
271
|
-
Execute multiple agents simultaneously:
|
|
272
|
-
|
|
273
|
-
```ruby
|
|
274
|
-
module Workflows
|
|
275
|
-
class ReviewAnalyzer < RubyLLM::Agents::Workflow::Parallel
|
|
276
|
-
version "1.0"
|
|
277
|
-
|
|
278
|
-
branch :sentiment, agent: SentimentAgent
|
|
279
|
-
branch :summary, agent: SummaryAgent
|
|
280
|
-
branch :categories, agent: CategoryAgent
|
|
281
|
-
|
|
282
|
-
def aggregate(results)
|
|
283
|
-
{
|
|
284
|
-
sentiment: results[:sentiment]&.content,
|
|
285
|
-
summary: results[:summary]&.content,
|
|
286
|
-
categories: results[:categories]&.content
|
|
287
|
-
}
|
|
288
|
-
end
|
|
289
|
-
end
|
|
290
|
-
end
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
### Router (Conditional)
|
|
294
|
-
|
|
295
|
-
Route to different agents based on classification:
|
|
296
|
-
|
|
297
|
-
```ruby
|
|
298
|
-
module Workflows
|
|
299
|
-
class SupportRouter < RubyLLM::Agents::Workflow::Router
|
|
300
|
-
version "1.0"
|
|
301
|
-
classifier ClassificationAgent
|
|
302
|
-
|
|
303
|
-
route :billing, agent: BillingAgent
|
|
304
|
-
route :technical, agent: TechnicalAgent
|
|
305
|
-
route :general, agent: GeneralAgent
|
|
306
|
-
|
|
307
|
-
default_route :general
|
|
308
|
-
end
|
|
309
|
-
end
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
## DSL Reference
|
|
313
|
-
|
|
314
|
-
### Shared Options
|
|
315
|
-
|
|
316
|
-
```ruby
|
|
317
|
-
version "1.0" # Version for tracking changes
|
|
318
|
-
timeout 120 # Total workflow timeout in seconds
|
|
319
|
-
max_cost 0.50 # Maximum allowed cost in USD
|
|
320
|
-
description "..." # Human-readable description
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
### Pipeline DSL
|
|
324
|
-
|
|
325
|
-
```ruby
|
|
326
|
-
# Define steps
|
|
327
|
-
step :name, agent: AgentClass
|
|
328
|
-
|
|
329
|
-
# Conditional skipping
|
|
330
|
-
step :validate, agent: Validator, skip_on: ->(ctx) { ctx[:skip_validation] }
|
|
331
|
-
|
|
332
|
-
# Optional steps (failures won't stop pipeline)
|
|
333
|
-
step :enrich, agent: Enricher, optional: true
|
|
334
|
-
|
|
335
|
-
# Continue on error
|
|
336
|
-
step :notify, agent: Notifier, continue_on_error: true
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
### Parallel DSL
|
|
340
|
-
|
|
341
|
-
```ruby
|
|
342
|
-
# Define branches
|
|
343
|
-
branch :name, agent: AgentClass
|
|
344
|
-
|
|
345
|
-
# Optional branches
|
|
346
|
-
branch :extra, agent: ExtraAgent, optional: true
|
|
347
|
-
|
|
348
|
-
# Custom input transformation
|
|
349
|
-
branch :process, agent: Processor, input: ->(opts) { { text: opts[:content] } }
|
|
350
|
-
|
|
351
|
-
# Fail-fast (stop all on first required failure)
|
|
352
|
-
fail_fast true
|
|
353
|
-
|
|
354
|
-
# Limit concurrency
|
|
355
|
-
concurrency 3
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
### Router DSL
|
|
359
|
-
|
|
360
|
-
```ruby
|
|
361
|
-
# Set classifier
|
|
362
|
-
classifier ClassificationAgent
|
|
363
|
-
|
|
364
|
-
# Define routes
|
|
365
|
-
route :category, agent: AgentClass
|
|
366
|
-
|
|
367
|
-
# Default route
|
|
368
|
-
default_route :fallback
|
|
369
|
-
|
|
370
|
-
# Custom routing logic
|
|
371
|
-
def select_route(classification)
|
|
372
|
-
case classification[:type]
|
|
373
|
-
when "urgent" then :priority
|
|
374
|
-
else :standard
|
|
375
|
-
end
|
|
376
|
-
end
|
|
377
|
-
```
|
|
378
|
-
|
|
379
|
-
## Using Workflows
|
|
380
|
-
|
|
381
|
-
### Basic Execution
|
|
382
|
-
|
|
383
|
-
```ruby
|
|
384
|
-
result = Workflows::ContentPipeline.call(text: "raw input")
|
|
385
|
-
|
|
386
|
-
result.success? # All steps succeeded
|
|
387
|
-
result.partial? # Some steps succeeded
|
|
388
|
-
result.error? # Workflow failed
|
|
389
|
-
result.content # Final output
|
|
390
|
-
result.total_cost # Combined cost
|
|
391
|
-
result.duration_ms # Total duration
|
|
392
|
-
```
|
|
393
|
-
|
|
394
|
-
### Pipeline Results
|
|
395
|
-
|
|
396
|
-
```ruby
|
|
397
|
-
result = Workflows::ContentPipeline.call(text: "input")
|
|
398
|
-
|
|
399
|
-
result.steps # Hash of all step results
|
|
400
|
-
result.steps[:extract].content # Specific step output
|
|
401
|
-
result.errors # Hash of step errors
|
|
402
|
-
```
|
|
403
|
-
|
|
404
|
-
### Parallel Results
|
|
405
|
-
|
|
406
|
-
```ruby
|
|
407
|
-
result = Workflows::ReviewAnalyzer.call(text: "Great product!")
|
|
408
|
-
|
|
409
|
-
result.branches # Hash of branch results
|
|
410
|
-
result.branches[:sentiment].content # Specific branch output
|
|
411
|
-
```
|
|
412
|
-
|
|
413
|
-
### Router Results
|
|
414
|
-
|
|
415
|
-
```ruby
|
|
416
|
-
result = Workflows::SupportRouter.call(question: "How do I pay?")
|
|
417
|
-
|
|
418
|
-
result.classification # What route was selected
|
|
419
|
-
result.route # Which agent handled it
|
|
420
|
-
result.content # Response from routed agent
|
|
421
|
-
```
|
|
422
|
-
|
|
423
|
-
## Advanced Patterns
|
|
424
|
-
|
|
425
|
-
### Input Transformation
|
|
426
|
-
|
|
427
|
-
Transform data between pipeline steps:
|
|
428
|
-
|
|
429
|
-
```ruby
|
|
430
|
-
module Workflows
|
|
431
|
-
class TransformPipeline < RubyLLM::Agents::Workflow::Pipeline
|
|
432
|
-
step :analyze, agent: AnalyzerAgent
|
|
433
|
-
step :enrich, agent: EnricherAgent
|
|
434
|
-
|
|
435
|
-
# Called before :enrich step
|
|
436
|
-
def before_enrich(context)
|
|
437
|
-
{
|
|
438
|
-
data: context[:analyze].content,
|
|
439
|
-
extra_field: "additional context"
|
|
440
|
-
}
|
|
441
|
-
end
|
|
442
|
-
end
|
|
443
|
-
end
|
|
444
|
-
```
|
|
445
|
-
|
|
446
|
-
### Custom Aggregation
|
|
447
|
-
|
|
448
|
-
Combine parallel results:
|
|
449
|
-
|
|
450
|
-
```ruby
|
|
451
|
-
module Workflows
|
|
452
|
-
class SafetyChecker < RubyLLM::Agents::Workflow::Parallel
|
|
453
|
-
branch :toxicity, agent: ToxicityAgent
|
|
454
|
-
branch :spam, agent: SpamAgent
|
|
455
|
-
branch :pii, agent: PiiAgent
|
|
456
|
-
|
|
457
|
-
def aggregate(results)
|
|
458
|
-
{
|
|
459
|
-
is_safe: results.values.none? { |r| r&.content == "flagged" },
|
|
460
|
-
flags: results.select { |_, r| r&.content == "flagged" }.keys
|
|
461
|
-
}
|
|
462
|
-
end
|
|
463
|
-
end
|
|
464
|
-
end
|
|
465
|
-
```
|
|
466
|
-
|
|
467
|
-
### Error Handling
|
|
468
|
-
|
|
469
|
-
Handle step failures:
|
|
470
|
-
|
|
471
|
-
```ruby
|
|
472
|
-
module Workflows
|
|
473
|
-
class ResilientPipeline < RubyLLM::Agents::Workflow::Pipeline
|
|
474
|
-
step :primary, agent: PrimaryAgent
|
|
475
|
-
step :backup, agent: BackupAgent, optional: true
|
|
476
|
-
|
|
477
|
-
# Called when :primary fails
|
|
478
|
-
def on_primary_failure(error, context)
|
|
479
|
-
Rails.logger.warn("Primary failed: #{error.message}")
|
|
480
|
-
:skip # Continue to backup
|
|
481
|
-
# :abort would stop the pipeline
|
|
482
|
-
end
|
|
483
|
-
end
|
|
484
|
-
end
|
|
485
|
-
```
|
|
486
|
-
|
|
487
|
-
### Cost Limits
|
|
488
|
-
|
|
489
|
-
Stop workflow if cost exceeds threshold:
|
|
490
|
-
|
|
491
|
-
```ruby
|
|
492
|
-
module Workflows
|
|
493
|
-
class BudgetedWorkflow < RubyLLM::Agents::Workflow::Pipeline
|
|
494
|
-
max_cost 1.00 # Stop if workflow exceeds $1.00
|
|
495
|
-
|
|
496
|
-
step :expensive_analysis, agent: DeepAnalysisAgent
|
|
497
|
-
step :synthesis, agent: SynthesisAgent
|
|
498
|
-
end
|
|
499
|
-
end
|
|
500
|
-
```
|
|
501
|
-
|
|
502
|
-
## Testing Workflows
|
|
503
|
-
|
|
504
|
-
```ruby
|
|
505
|
-
RSpec.describe Workflows::ContentPipeline do
|
|
506
|
-
describe ".call" do
|
|
507
|
-
it "processes content through all steps" do
|
|
508
|
-
result = described_class.call(text: "test input")
|
|
509
|
-
|
|
510
|
-
expect(result.success?).to be true
|
|
511
|
-
expect(result.steps.keys).to eq([:extract, :validate, :format])
|
|
512
|
-
end
|
|
513
|
-
|
|
514
|
-
it "handles step failures" do
|
|
515
|
-
allow(ExtractorAgent).to receive(:call).and_raise("Network error")
|
|
516
|
-
|
|
517
|
-
result = described_class.call(text: "test")
|
|
518
|
-
|
|
519
|
-
expect(result.error?).to be true
|
|
520
|
-
expect(result.errors[:extract]).to be_present
|
|
521
|
-
end
|
|
522
|
-
end
|
|
523
|
-
end
|
|
524
|
-
```
|
|
525
|
-
|
|
526
|
-
## Best Practices
|
|
527
|
-
|
|
528
|
-
### General
|
|
529
|
-
|
|
530
|
-
1. **Version your workflows** - Track changes for debugging and rollback
|
|
531
|
-
2. **Set timeouts** - Prevent runaway workflows at both workflow and step level
|
|
532
|
-
3. **Use max_cost** - Control spending on expensive operations
|
|
533
|
-
4. **Handle errors gracefully** - Use optional steps, defaults, and error handlers
|
|
534
|
-
5. **Keep workflows focused** - One workflow, one purpose
|
|
535
|
-
6. **Test step interactions** - Unit test agents, integration test workflows
|
|
536
|
-
|
|
537
|
-
### DSL Workflows (Recommended)
|
|
538
|
-
|
|
539
|
-
1. **Use input schemas** - Validate inputs early with `required`/`optional` declarations
|
|
540
|
-
2. **Prefer DSL over legacy patterns** - The new DSL is more flexible and composable
|
|
541
|
-
3. **Use parallel for independent operations** - Group unrelated steps for concurrent execution
|
|
542
|
-
4. **Use routing for classification** - Clean syntax for conditional agent selection
|
|
543
|
-
5. **Leverage step result access** - Use `step_name.field` syntax in conditions/routing
|
|
544
|
-
6. **Add lifecycle hooks** - Log, track, and handle errors at appropriate points
|
|
545
|
-
7. **Use dry_run for validation** - Check workflow configuration before execution
|
|
546
|
-
|
|
547
|
-
### Legacy Patterns
|
|
548
|
-
|
|
549
|
-
1. **Use Pipeline for sequential data processing** - Data flows naturally between steps
|
|
550
|
-
2. **Use Parallel for fan-out operations** - Independent operations with aggregation
|
|
551
|
-
3. **Use Router for classification-based dispatch** - When route selection depends on LLM classification
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module RubyLLM
|
|
4
|
-
module Agents
|
|
5
|
-
# DSL for configuring content moderation on agents
|
|
6
|
-
#
|
|
7
|
-
# Provides declarative configuration for moderating user input
|
|
8
|
-
# and/or LLM output against safety policies.
|
|
9
|
-
#
|
|
10
|
-
# @example Basic input moderation
|
|
11
|
-
# class MyAgent < ApplicationAgent
|
|
12
|
-
# moderation :input
|
|
13
|
-
# end
|
|
14
|
-
#
|
|
15
|
-
# @example Moderate both input and output
|
|
16
|
-
# class MyAgent < ApplicationAgent
|
|
17
|
-
# moderation :both
|
|
18
|
-
# end
|
|
19
|
-
#
|
|
20
|
-
# @example With configuration options
|
|
21
|
-
# class MyAgent < ApplicationAgent
|
|
22
|
-
# moderation :input,
|
|
23
|
-
# model: 'omni-moderation-latest',
|
|
24
|
-
# threshold: 0.8,
|
|
25
|
-
# categories: [:hate, :violence],
|
|
26
|
-
# on_flagged: :raise
|
|
27
|
-
# end
|
|
28
|
-
#
|
|
29
|
-
# @example Block-based DSL
|
|
30
|
-
# class MyAgent < ApplicationAgent
|
|
31
|
-
# moderation do
|
|
32
|
-
# input enabled: true, threshold: 0.7
|
|
33
|
-
# output enabled: true, threshold: 0.9
|
|
34
|
-
# model 'omni-moderation-latest'
|
|
35
|
-
# categories :hate, :violence
|
|
36
|
-
# on_flagged :block
|
|
37
|
-
# end
|
|
38
|
-
# end
|
|
39
|
-
#
|
|
40
|
-
# @api public
|
|
41
|
-
module ModerationDSL
|
|
42
|
-
# Configures content moderation for this agent
|
|
43
|
-
#
|
|
44
|
-
# @param phases [Array<Symbol>] Phases to moderate (:input, :output, :both)
|
|
45
|
-
# @param model [String] Moderation model to use
|
|
46
|
-
# @param threshold [Float] Score threshold (0.0-1.0) for flagging
|
|
47
|
-
# @param categories [Array<Symbol>] Categories to check
|
|
48
|
-
# @param on_flagged [Symbol] Action when flagged (:block, :raise, :warn, :log)
|
|
49
|
-
# @param custom_handler [Symbol] Method name for custom handling
|
|
50
|
-
# @yield Block for advanced configuration
|
|
51
|
-
# @return [Hash, nil] The moderation configuration
|
|
52
|
-
#
|
|
53
|
-
# @example Simple input moderation
|
|
54
|
-
# moderation :input
|
|
55
|
-
#
|
|
56
|
-
# @example Input and output moderation
|
|
57
|
-
# moderation :input, :output
|
|
58
|
-
# # or
|
|
59
|
-
# moderation :both
|
|
60
|
-
#
|
|
61
|
-
# @example With options
|
|
62
|
-
# moderation :input, threshold: 0.8, on_flagged: :raise
|
|
63
|
-
def moderation(*phases, **options, &block)
|
|
64
|
-
if block_given?
|
|
65
|
-
builder = ModerationBuilder.new
|
|
66
|
-
builder.instance_eval(&block)
|
|
67
|
-
@moderation_config = builder.config
|
|
68
|
-
else
|
|
69
|
-
# Handle :both shorthand
|
|
70
|
-
phases = [:input, :output] if phases.include?(:both)
|
|
71
|
-
phases = [:input] if phases.empty?
|
|
72
|
-
|
|
73
|
-
@moderation_config = {
|
|
74
|
-
phases: phases.flatten.map(&:to_sym),
|
|
75
|
-
model: options[:model],
|
|
76
|
-
threshold: options[:threshold],
|
|
77
|
-
categories: options[:categories],
|
|
78
|
-
on_flagged: options[:on_flagged] || :block,
|
|
79
|
-
custom_handler: options[:custom_handler]
|
|
80
|
-
}
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
# Returns the moderation configuration for this agent
|
|
85
|
-
#
|
|
86
|
-
# @return [Hash, nil] The moderation configuration or nil if not configured
|
|
87
|
-
def moderation_config
|
|
88
|
-
@moderation_config || inherited_or_default(:moderation_config, nil)
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
# Returns whether moderation is enabled for this agent
|
|
92
|
-
#
|
|
93
|
-
# @return [Boolean] true if moderation is configured
|
|
94
|
-
def moderation_enabled?
|
|
95
|
-
!!moderation_config
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
private
|
|
99
|
-
|
|
100
|
-
def inherited_or_default(method, default)
|
|
101
|
-
return default unless superclass.respond_to?(method)
|
|
102
|
-
|
|
103
|
-
superclass.send(method)
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
# Builder class for block-based moderation configuration
|
|
108
|
-
#
|
|
109
|
-
# @api private
|
|
110
|
-
class ModerationBuilder
|
|
111
|
-
attr_reader :config
|
|
112
|
-
|
|
113
|
-
def initialize
|
|
114
|
-
@config = {
|
|
115
|
-
phases: [],
|
|
116
|
-
on_flagged: :block
|
|
117
|
-
}
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
# Enables input moderation
|
|
121
|
-
#
|
|
122
|
-
# @param enabled [Boolean] Whether to enable input moderation
|
|
123
|
-
# @param threshold [Float, nil] Score threshold for input phase
|
|
124
|
-
# @return [void]
|
|
125
|
-
def input(enabled: true, threshold: nil)
|
|
126
|
-
@config[:phases] << :input if enabled
|
|
127
|
-
@config[:input_threshold] = threshold if threshold
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
# Enables output moderation
|
|
131
|
-
#
|
|
132
|
-
# @param enabled [Boolean] Whether to enable output moderation
|
|
133
|
-
# @param threshold [Float, nil] Score threshold for output phase
|
|
134
|
-
# @return [void]
|
|
135
|
-
def output(enabled: true, threshold: nil)
|
|
136
|
-
@config[:phases] << :output if enabled
|
|
137
|
-
@config[:output_threshold] = threshold if threshold
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
# Sets the moderation model
|
|
141
|
-
#
|
|
142
|
-
# @param model_name [String] Model identifier
|
|
143
|
-
# @return [void]
|
|
144
|
-
def model(model_name)
|
|
145
|
-
@config[:model] = model_name
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
# Sets the global threshold
|
|
149
|
-
#
|
|
150
|
-
# @param value [Float] Score threshold (0.0-1.0)
|
|
151
|
-
# @return [void]
|
|
152
|
-
def threshold(value)
|
|
153
|
-
@config[:threshold] = value
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
# Sets categories to check
|
|
157
|
-
#
|
|
158
|
-
# @param cats [Array<Symbol>] Category symbols
|
|
159
|
-
# @return [void]
|
|
160
|
-
def categories(*cats)
|
|
161
|
-
@config[:categories] = cats.flatten.map(&:to_sym)
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
# Sets the action when content is flagged
|
|
165
|
-
#
|
|
166
|
-
# @param action [Symbol] :block, :raise, :warn, or :log
|
|
167
|
-
# @return [void]
|
|
168
|
-
def on_flagged(action)
|
|
169
|
-
@config[:on_flagged] = action
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
# Sets a custom handler method
|
|
173
|
-
#
|
|
174
|
-
# @param method_name [Symbol] Method name to call on the agent
|
|
175
|
-
# @return [void]
|
|
176
|
-
def custom_handler(method_name)
|
|
177
|
-
@config[:custom_handler] = method_name
|
|
178
|
-
end
|
|
179
|
-
end
|
|
180
|
-
end
|
|
181
|
-
end
|