conductor_ruby 0.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 +7 -0
- data/CHANGELOG.md +142 -0
- data/LICENSE +190 -0
- data/README.md +517 -0
- data/examples/agentic_workflows/llm_chat.rb +106 -0
- data/examples/dynamic_workflow.rb +177 -0
- data/examples/event_handler.rb +94 -0
- data/examples/event_listener_examples.rb +430 -0
- data/examples/helloworld/greetings_worker.rb +24 -0
- data/examples/helloworld/helloworld.rb +99 -0
- data/examples/kitchensink.rb +213 -0
- data/examples/metadata_journey.rb +189 -0
- data/examples/metrics_example.rb +284 -0
- data/examples/new_dsl_demo.rb +141 -0
- data/examples/orkes/http_poll.rb +83 -0
- data/examples/orkes/secrets_example.rb +69 -0
- data/examples/orkes/wait_for_webhook.rb +90 -0
- data/examples/prompt_journey.rb +245 -0
- data/examples/rag_workflow.rb +167 -0
- data/examples/schedule_journey.rb +244 -0
- data/examples/simple_worker.rb +125 -0
- data/examples/simple_workflow.rb +89 -0
- data/examples/task_context_example.rb +257 -0
- data/examples/task_listener_example.rb +192 -0
- data/examples/worker_configuration_example.rb +282 -0
- data/examples/workflow_dsl.rb +316 -0
- data/examples/workflow_ops.rb +305 -0
- data/lib/conductor/client/authorization_client.rb +238 -0
- data/lib/conductor/client/integration_client.rb +108 -0
- data/lib/conductor/client/metadata_client.rb +139 -0
- data/lib/conductor/client/prompt_client.rb +58 -0
- data/lib/conductor/client/scheduler_client.rb +132 -0
- data/lib/conductor/client/schema_client.rb +32 -0
- data/lib/conductor/client/secret_client.rb +48 -0
- data/lib/conductor/client/task_client.rb +168 -0
- data/lib/conductor/client/workflow_client.rb +242 -0
- data/lib/conductor/configuration/authentication_settings.rb +17 -0
- data/lib/conductor/configuration.rb +103 -0
- data/lib/conductor/exceptions.rb +86 -0
- data/lib/conductor/http/api/application_resource_api.rb +107 -0
- data/lib/conductor/http/api/authorization_resource_api.rb +56 -0
- data/lib/conductor/http/api/event_resource_api.rb +133 -0
- data/lib/conductor/http/api/gateway_auth_resource_api.rb +48 -0
- data/lib/conductor/http/api/group_resource_api.rb +76 -0
- data/lib/conductor/http/api/integration_resource_api.rb +145 -0
- data/lib/conductor/http/api/metadata_resource_api.rb +231 -0
- data/lib/conductor/http/api/prompt_resource_api.rb +81 -0
- data/lib/conductor/http/api/role_resource_api.rb +60 -0
- data/lib/conductor/http/api/scheduler_resource_api.rb +211 -0
- data/lib/conductor/http/api/schema_resource_api.rb +82 -0
- data/lib/conductor/http/api/secret_resource_api.rb +134 -0
- data/lib/conductor/http/api/task_resource_api.rb +321 -0
- data/lib/conductor/http/api/token_resource_api.rb +42 -0
- data/lib/conductor/http/api/user_resource_api.rb +59 -0
- data/lib/conductor/http/api/workflow_bulk_resource_api.rb +91 -0
- data/lib/conductor/http/api/workflow_resource_api.rb +451 -0
- data/lib/conductor/http/api_client.rb +437 -0
- data/lib/conductor/http/models/authentication_config.rb +67 -0
- data/lib/conductor/http/models/authorization_request.rb +39 -0
- data/lib/conductor/http/models/base_model.rb +162 -0
- data/lib/conductor/http/models/bulk_response.rb +39 -0
- data/lib/conductor/http/models/conductor_application.rb +39 -0
- data/lib/conductor/http/models/conductor_user.rb +53 -0
- data/lib/conductor/http/models/create_or_update_application_request.rb +24 -0
- data/lib/conductor/http/models/create_or_update_role_request.rb +27 -0
- data/lib/conductor/http/models/event_handler.rb +130 -0
- data/lib/conductor/http/models/generate_token_request.rb +27 -0
- data/lib/conductor/http/models/group.rb +36 -0
- data/lib/conductor/http/models/integration.rb +70 -0
- data/lib/conductor/http/models/integration_api.rb +53 -0
- data/lib/conductor/http/models/integration_api_update.rb +43 -0
- data/lib/conductor/http/models/integration_update.rb +36 -0
- data/lib/conductor/http/models/permission.rb +24 -0
- data/lib/conductor/http/models/poll_data.rb +33 -0
- data/lib/conductor/http/models/prompt_template.rb +59 -0
- data/lib/conductor/http/models/prompt_template_test_request.rb +43 -0
- data/lib/conductor/http/models/rerun_workflow_request.rb +37 -0
- data/lib/conductor/http/models/role.rb +27 -0
- data/lib/conductor/http/models/schema_def.rb +59 -0
- data/lib/conductor/http/models/search_result.rb +187 -0
- data/lib/conductor/http/models/skip_task_request.rb +27 -0
- data/lib/conductor/http/models/start_workflow_request.rb +68 -0
- data/lib/conductor/http/models/subject_ref.rb +35 -0
- data/lib/conductor/http/models/tag_object.rb +36 -0
- data/lib/conductor/http/models/target_ref.rb +39 -0
- data/lib/conductor/http/models/task.rb +156 -0
- data/lib/conductor/http/models/task_def.rb +95 -0
- data/lib/conductor/http/models/task_exec_log.rb +30 -0
- data/lib/conductor/http/models/task_result.rb +115 -0
- data/lib/conductor/http/models/task_result_status.rb +24 -0
- data/lib/conductor/http/models/token.rb +33 -0
- data/lib/conductor/http/models/upsert_group_request.rb +30 -0
- data/lib/conductor/http/models/upsert_user_request.rb +39 -0
- data/lib/conductor/http/models/workflow.rb +202 -0
- data/lib/conductor/http/models/workflow_def.rb +73 -0
- data/lib/conductor/http/models/workflow_schedule.rb +100 -0
- data/lib/conductor/http/models/workflow_state_update.rb +30 -0
- data/lib/conductor/http/models/workflow_status_constants.rb +57 -0
- data/lib/conductor/http/models/workflow_task.rb +169 -0
- data/lib/conductor/http/models/workflow_test_request.rb +67 -0
- data/lib/conductor/http/rest_client.rb +211 -0
- data/lib/conductor/orkes/models/access_key.rb +56 -0
- data/lib/conductor/orkes/models/granted_permission.rb +27 -0
- data/lib/conductor/orkes/models/metadata_tag.rb +15 -0
- data/lib/conductor/orkes/models/rate_limit_tag.rb +15 -0
- data/lib/conductor/orkes/orkes_clients.rb +69 -0
- data/lib/conductor/version.rb +5 -0
- data/lib/conductor/worker/events/conductor_event.rb +40 -0
- data/lib/conductor/worker/events/global_dispatcher.rb +37 -0
- data/lib/conductor/worker/events/http_events.rb +25 -0
- data/lib/conductor/worker/events/listener_registry.rb +40 -0
- data/lib/conductor/worker/events/listeners.rb +34 -0
- data/lib/conductor/worker/events/sync_event_dispatcher.rb +78 -0
- data/lib/conductor/worker/events/task_runner_events.rb +271 -0
- data/lib/conductor/worker/events/workflow_events.rb +49 -0
- data/lib/conductor/worker/fiber_executor.rb +532 -0
- data/lib/conductor/worker/ractor_task_runner.rb +501 -0
- data/lib/conductor/worker/task_context.rb +114 -0
- data/lib/conductor/worker/task_definition_registrar.rb +322 -0
- data/lib/conductor/worker/task_handler.rb +360 -0
- data/lib/conductor/worker/task_in_progress.rb +60 -0
- data/lib/conductor/worker/task_runner.rb +538 -0
- data/lib/conductor/worker/telemetry/metrics_collector.rb +196 -0
- data/lib/conductor/worker/telemetry/prometheus_backend.rb +224 -0
- data/lib/conductor/worker/worker.rb +355 -0
- data/lib/conductor/worker/worker_config.rb +154 -0
- data/lib/conductor/worker/worker_registry.rb +71 -0
- data/lib/conductor/workflow/dsl/input_ref.rb +37 -0
- data/lib/conductor/workflow/dsl/output_ref.rb +44 -0
- data/lib/conductor/workflow/dsl/parallel_builder.rb +49 -0
- data/lib/conductor/workflow/dsl/switch_builder.rb +74 -0
- data/lib/conductor/workflow/dsl/task_ref.rb +178 -0
- data/lib/conductor/workflow/dsl/workflow_builder.rb +1016 -0
- data/lib/conductor/workflow/dsl/workflow_definition.rb +150 -0
- data/lib/conductor/workflow/llm/chat_message.rb +47 -0
- data/lib/conductor/workflow/llm/embedding_model.rb +19 -0
- data/lib/conductor/workflow/llm/tool_call.rb +43 -0
- data/lib/conductor/workflow/llm/tool_spec.rb +46 -0
- data/lib/conductor/workflow/task_type.rb +68 -0
- data/lib/conductor/workflow/timeout_policy.rb +31 -0
- data/lib/conductor/workflow/workflow_executor.rb +373 -0
- data/lib/conductor.rb +192 -0
- metadata +359 -0
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Worker Configuration Example
|
|
5
|
+
#
|
|
6
|
+
# Demonstrates hierarchical worker configuration using environment variables.
|
|
7
|
+
#
|
|
8
|
+
# This example shows how to override worker settings at deployment time without
|
|
9
|
+
# changing code, using a three-tier configuration hierarchy:
|
|
10
|
+
#
|
|
11
|
+
# 1. Code-level defaults (lowest priority)
|
|
12
|
+
# 2. Global worker config: CONDUCTOR_WORKER_ALL_<PROPERTY>
|
|
13
|
+
# 3. Worker-specific config: CONDUCTOR_WORKER_<WORKER_NAME>_<PROPERTY>
|
|
14
|
+
#
|
|
15
|
+
# Usage:
|
|
16
|
+
# # Run with code defaults
|
|
17
|
+
# ruby worker_configuration_example.rb
|
|
18
|
+
#
|
|
19
|
+
# # Run with global overrides
|
|
20
|
+
# export CONDUCTOR_WORKER_ALL_DOMAIN=production
|
|
21
|
+
# export CONDUCTOR_WORKER_ALL_POLL_INTERVAL=250
|
|
22
|
+
# ruby worker_configuration_example.rb
|
|
23
|
+
#
|
|
24
|
+
# # Run with worker-specific overrides
|
|
25
|
+
# export CONDUCTOR_WORKER_ALL_DOMAIN=production
|
|
26
|
+
# export CONDUCTOR_WORKER_CRITICAL_TASK_THREAD_COUNT=20
|
|
27
|
+
# export CONDUCTOR_WORKER_CRITICAL_TASK_POLL_INTERVAL=100
|
|
28
|
+
# ruby worker_configuration_example.rb
|
|
29
|
+
#
|
|
30
|
+
|
|
31
|
+
require 'bundler/setup'
|
|
32
|
+
require 'conductor'
|
|
33
|
+
|
|
34
|
+
#
|
|
35
|
+
# Example 1: Standard worker with default configuration
|
|
36
|
+
#
|
|
37
|
+
Conductor::Worker.define(
|
|
38
|
+
'process_order',
|
|
39
|
+
poll_interval: 1000,
|
|
40
|
+
domain: 'dev',
|
|
41
|
+
thread_count: 5,
|
|
42
|
+
poll_timeout: 100
|
|
43
|
+
) do |task|
|
|
44
|
+
order_id = task.input_data['order_id'] || 'unknown'
|
|
45
|
+
{
|
|
46
|
+
status: 'processed',
|
|
47
|
+
order_id: order_id,
|
|
48
|
+
worker_type: 'standard'
|
|
49
|
+
}
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
#
|
|
53
|
+
# Example 2: High-priority worker that might need more resources in production
|
|
54
|
+
#
|
|
55
|
+
Conductor::Worker.define(
|
|
56
|
+
'critical_task',
|
|
57
|
+
poll_interval: 1000,
|
|
58
|
+
domain: 'dev',
|
|
59
|
+
thread_count: 5,
|
|
60
|
+
poll_timeout: 100
|
|
61
|
+
) do |task|
|
|
62
|
+
task_id = task.input_data['task_id'] || 'unknown'
|
|
63
|
+
{
|
|
64
|
+
status: 'completed',
|
|
65
|
+
task_id: task_id,
|
|
66
|
+
priority: 'critical'
|
|
67
|
+
}
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
#
|
|
71
|
+
# Example 3: Background worker that can run with fewer resources
|
|
72
|
+
#
|
|
73
|
+
Conductor::Worker.define(
|
|
74
|
+
'background_task',
|
|
75
|
+
poll_interval: 2000,
|
|
76
|
+
domain: 'dev',
|
|
77
|
+
thread_count: 2,
|
|
78
|
+
poll_timeout: 200
|
|
79
|
+
) do |task|
|
|
80
|
+
job_id = task.input_data['job_id'] || 'unknown'
|
|
81
|
+
{
|
|
82
|
+
status: 'completed',
|
|
83
|
+
job_id: job_id,
|
|
84
|
+
priority: 'low'
|
|
85
|
+
}
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def print_configuration_examples
|
|
89
|
+
puts
|
|
90
|
+
puts '=' * 80
|
|
91
|
+
puts 'Worker Configuration Hierarchy Examples'
|
|
92
|
+
puts '=' * 80
|
|
93
|
+
|
|
94
|
+
# Show current environment variables
|
|
95
|
+
puts
|
|
96
|
+
puts 'Current Environment Variables:'
|
|
97
|
+
env_vars = ENV.select { |k, _| k.start_with?('CONDUCTOR_WORKER') }
|
|
98
|
+
if env_vars.any?
|
|
99
|
+
env_vars.sort.each { |key, value| puts " #{key} = #{value}" }
|
|
100
|
+
else
|
|
101
|
+
puts ' (No CONDUCTOR_WORKER_* environment variables set)'
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
puts
|
|
105
|
+
puts '-' * 80
|
|
106
|
+
|
|
107
|
+
# Example 1: process_order configuration
|
|
108
|
+
puts
|
|
109
|
+
puts '1. Standard Worker (process_order):'
|
|
110
|
+
puts " Code defaults: poll_interval=1000, domain='dev', thread_count=5"
|
|
111
|
+
|
|
112
|
+
config1 = Conductor::Worker::WorkerConfig.resolve(
|
|
113
|
+
'process_order',
|
|
114
|
+
poll_interval: 1000,
|
|
115
|
+
domain: 'dev',
|
|
116
|
+
thread_count: 5,
|
|
117
|
+
poll_timeout: 100
|
|
118
|
+
)
|
|
119
|
+
puts
|
|
120
|
+
puts ' Resolved configuration:'
|
|
121
|
+
puts " poll_interval: #{config1[:poll_interval]}"
|
|
122
|
+
puts " domain: #{config1[:domain]}"
|
|
123
|
+
puts " thread_count: #{config1[:thread_count]}"
|
|
124
|
+
puts " poll_timeout: #{config1[:poll_timeout]}"
|
|
125
|
+
|
|
126
|
+
# Example 2: critical_task configuration
|
|
127
|
+
puts
|
|
128
|
+
puts '2. Critical Worker (critical_task):'
|
|
129
|
+
puts " Code defaults: poll_interval=1000, domain='dev', thread_count=5"
|
|
130
|
+
|
|
131
|
+
config2 = Conductor::Worker::WorkerConfig.resolve(
|
|
132
|
+
'critical_task',
|
|
133
|
+
poll_interval: 1000,
|
|
134
|
+
domain: 'dev',
|
|
135
|
+
thread_count: 5,
|
|
136
|
+
poll_timeout: 100
|
|
137
|
+
)
|
|
138
|
+
puts
|
|
139
|
+
puts ' Resolved configuration:'
|
|
140
|
+
puts " poll_interval: #{config2[:poll_interval]}"
|
|
141
|
+
puts " domain: #{config2[:domain]}"
|
|
142
|
+
puts " thread_count: #{config2[:thread_count]}"
|
|
143
|
+
puts " poll_timeout: #{config2[:poll_timeout]}"
|
|
144
|
+
|
|
145
|
+
# Example 3: background_task configuration
|
|
146
|
+
puts
|
|
147
|
+
puts '3. Background Worker (background_task):'
|
|
148
|
+
puts " Code defaults: poll_interval=2000, domain='dev', thread_count=2"
|
|
149
|
+
|
|
150
|
+
config3 = Conductor::Worker::WorkerConfig.resolve(
|
|
151
|
+
'background_task',
|
|
152
|
+
poll_interval: 2000,
|
|
153
|
+
domain: 'dev',
|
|
154
|
+
thread_count: 2,
|
|
155
|
+
poll_timeout: 200
|
|
156
|
+
)
|
|
157
|
+
puts
|
|
158
|
+
puts ' Resolved configuration:'
|
|
159
|
+
puts " poll_interval: #{config3[:poll_interval]}"
|
|
160
|
+
puts " domain: #{config3[:domain]}"
|
|
161
|
+
puts " thread_count: #{config3[:thread_count]}"
|
|
162
|
+
puts " poll_timeout: #{config3[:poll_timeout]}"
|
|
163
|
+
|
|
164
|
+
puts
|
|
165
|
+
puts '-' * 80
|
|
166
|
+
puts
|
|
167
|
+
puts 'Configuration Priority: Worker-specific > Global > Code defaults'
|
|
168
|
+
puts
|
|
169
|
+
puts 'Example Environment Variables:'
|
|
170
|
+
puts ' # Global override (all workers)'
|
|
171
|
+
puts ' export CONDUCTOR_WORKER_ALL_DOMAIN=production'
|
|
172
|
+
puts ' export CONDUCTOR_WORKER_ALL_POLL_INTERVAL=250'
|
|
173
|
+
puts
|
|
174
|
+
puts ' # Worker-specific override (only critical_task)'
|
|
175
|
+
puts ' export CONDUCTOR_WORKER_CRITICAL_TASK_THREAD_COUNT=20'
|
|
176
|
+
puts ' export CONDUCTOR_WORKER_CRITICAL_TASK_POLL_INTERVAL=100'
|
|
177
|
+
puts
|
|
178
|
+
puts '=' * 80
|
|
179
|
+
puts
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def print_configuration_properties
|
|
183
|
+
puts
|
|
184
|
+
puts '=' * 80
|
|
185
|
+
puts 'Available Configuration Properties'
|
|
186
|
+
puts '=' * 80
|
|
187
|
+
puts
|
|
188
|
+
puts 'Property Type Default Description'
|
|
189
|
+
puts '-' * 80
|
|
190
|
+
puts 'poll_interval Integer 100 Polling interval in milliseconds'
|
|
191
|
+
puts 'thread_count Integer 1 Max concurrent tasks per worker'
|
|
192
|
+
puts 'domain String nil Task domain for isolation'
|
|
193
|
+
puts 'worker_id String auto Unique worker identifier'
|
|
194
|
+
puts 'poll_timeout Integer 100 Server-side long poll timeout (ms)'
|
|
195
|
+
puts 'register_task_def Boolean false Auto-register task definition'
|
|
196
|
+
puts 'paused Boolean false Pause worker (stop polling)'
|
|
197
|
+
puts
|
|
198
|
+
puts '=' * 80
|
|
199
|
+
puts
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def main
|
|
203
|
+
print_configuration_examples
|
|
204
|
+
print_configuration_properties
|
|
205
|
+
|
|
206
|
+
puts 'Configuration resolution complete!'
|
|
207
|
+
puts
|
|
208
|
+
puts 'To see different configurations, try setting environment variables:'
|
|
209
|
+
puts
|
|
210
|
+
puts ' # Test global override:'
|
|
211
|
+
puts ' export CONDUCTOR_WORKER_ALL_POLL_INTERVAL=500'
|
|
212
|
+
puts ' ruby worker_configuration_example.rb'
|
|
213
|
+
puts
|
|
214
|
+
puts ' # Test worker-specific override:'
|
|
215
|
+
puts ' export CONDUCTOR_WORKER_CRITICAL_TASK_THREAD_COUNT=20'
|
|
216
|
+
puts ' ruby worker_configuration_example.rb'
|
|
217
|
+
puts
|
|
218
|
+
puts ' # Test production-like scenario:'
|
|
219
|
+
puts ' export CONDUCTOR_WORKER_ALL_DOMAIN=production'
|
|
220
|
+
puts ' export CONDUCTOR_WORKER_ALL_POLL_INTERVAL=250'
|
|
221
|
+
puts ' export CONDUCTOR_WORKER_CRITICAL_TASK_THREAD_COUNT=50'
|
|
222
|
+
puts ' export CONDUCTOR_WORKER_CRITICAL_TASK_POLL_INTERVAL=50'
|
|
223
|
+
puts ' ruby worker_configuration_example.rb'
|
|
224
|
+
puts
|
|
225
|
+
puts '-' * 80
|
|
226
|
+
puts
|
|
227
|
+
puts 'To actually run workers with the resolved configuration:'
|
|
228
|
+
puts
|
|
229
|
+
puts ' # Set configuration and run workers'
|
|
230
|
+
puts ' export CONDUCTOR_SERVER_URL=http://localhost:8080/api'
|
|
231
|
+
puts ' export CONDUCTOR_WORKER_ALL_DOMAIN=production'
|
|
232
|
+
puts ' ruby worker_configuration_example.rb --run'
|
|
233
|
+
puts
|
|
234
|
+
|
|
235
|
+
# Check if --run flag is passed
|
|
236
|
+
return unless ARGV.include?('--run')
|
|
237
|
+
|
|
238
|
+
puts
|
|
239
|
+
puts '=' * 80
|
|
240
|
+
puts 'Running Workers with Resolved Configuration'
|
|
241
|
+
puts '=' * 80
|
|
242
|
+
puts
|
|
243
|
+
|
|
244
|
+
config = Conductor::Configuration.new(
|
|
245
|
+
server_api_url: ENV.fetch('CONDUCTOR_SERVER_URL', 'http://localhost:8080/api'),
|
|
246
|
+
auth_key: ENV.fetch('CONDUCTOR_AUTH_KEY', nil),
|
|
247
|
+
auth_secret: ENV.fetch('CONDUCTOR_AUTH_SECRET', nil)
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
begin
|
|
251
|
+
handler = Conductor::Worker::TaskHandler.new(
|
|
252
|
+
configuration: config,
|
|
253
|
+
scan_for_annotated_workers: true
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
shutdown = false
|
|
257
|
+
Signal.trap('INT') do
|
|
258
|
+
puts "\nShutting down..."
|
|
259
|
+
shutdown = true
|
|
260
|
+
handler.stop
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
puts 'Workers:'
|
|
264
|
+
handler.worker_names.each do |name|
|
|
265
|
+
puts " - #{name}"
|
|
266
|
+
end
|
|
267
|
+
puts
|
|
268
|
+
puts 'Press Ctrl+C to stop...'
|
|
269
|
+
puts
|
|
270
|
+
|
|
271
|
+
handler.start
|
|
272
|
+
handler.join unless shutdown
|
|
273
|
+
rescue Interrupt
|
|
274
|
+
puts 'Interrupted!'
|
|
275
|
+
rescue StandardError => e
|
|
276
|
+
puts "Error: #{e.message}"
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
puts 'Workers stopped.'
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
main if __FILE__ == $PROGRAM_NAME
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Example: Using the Conductor Ruby SDK Workflow DSL
|
|
5
|
+
#
|
|
6
|
+
# This example demonstrates how to define workflows programmatically using
|
|
7
|
+
# the new Ruby-idiomatic DSL, which provides a clean and expressive way to build workflows.
|
|
8
|
+
#
|
|
9
|
+
# Key Features:
|
|
10
|
+
# - Method-per-type: simple(), http(), wait(), terminate(), etc.
|
|
11
|
+
# - Auto-generated reference names
|
|
12
|
+
# - Hash-style [] for output references: task[:field]
|
|
13
|
+
# - wf[:param] for workflow inputs
|
|
14
|
+
# - Block-based control flow: parallel, decide, loop_times
|
|
15
|
+
#
|
|
16
|
+
# Run with: bundle exec ruby examples/workflow_dsl.rb
|
|
17
|
+
|
|
18
|
+
require_relative '../lib/conductor'
|
|
19
|
+
|
|
20
|
+
puts '=' * 70
|
|
21
|
+
puts 'Conductor Ruby SDK - New DSL Examples'
|
|
22
|
+
puts '=' * 70
|
|
23
|
+
puts
|
|
24
|
+
|
|
25
|
+
# ============================================================================
|
|
26
|
+
# Example 1: Simple Sequential Workflow
|
|
27
|
+
# ============================================================================
|
|
28
|
+
puts '=== Example 1: Simple Sequential Workflow ==='
|
|
29
|
+
|
|
30
|
+
simple_workflow = Conductor.workflow :ruby_simple_workflow, version: 1 do
|
|
31
|
+
description 'A simple sequential workflow created with the new Ruby DSL'
|
|
32
|
+
timeout 3600
|
|
33
|
+
owner_email 'developer@example.com'
|
|
34
|
+
|
|
35
|
+
# Tasks are created with simple method calls
|
|
36
|
+
# wf[:userName] returns "${workflow.input.userName}"
|
|
37
|
+
greet = simple :greet_user, name: wf[:userName]
|
|
38
|
+
|
|
39
|
+
# Reference previous task outputs with task[:field]
|
|
40
|
+
# greet[:message] returns "${greet_user_ref.output.message}"
|
|
41
|
+
process = simple :process_greeting, greeting: greet[:message]
|
|
42
|
+
|
|
43
|
+
# Set workflow output
|
|
44
|
+
output result: process[:result]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
puts "Workflow: #{simple_workflow.name}"
|
|
48
|
+
puts "Tasks: #{simple_workflow.builder.tasks.size}"
|
|
49
|
+
puts
|
|
50
|
+
|
|
51
|
+
# ============================================================================
|
|
52
|
+
# Example 2: Parallel Execution
|
|
53
|
+
# ============================================================================
|
|
54
|
+
puts '=== Example 2: Parallel Execution ==='
|
|
55
|
+
|
|
56
|
+
parallel_workflow = Conductor.workflow :ruby_parallel_workflow, version: 1 do
|
|
57
|
+
description 'Workflow with parallel task execution'
|
|
58
|
+
timeout 300
|
|
59
|
+
|
|
60
|
+
# Initial task
|
|
61
|
+
fetch = simple :fetch_data, source: wf[:dataSource]
|
|
62
|
+
|
|
63
|
+
# Parallel block - tasks execute concurrently
|
|
64
|
+
parallel do
|
|
65
|
+
simple :process_type_a, data: fetch[:data]
|
|
66
|
+
simple :process_type_b, data: fetch[:data]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Aggregation after parallel tasks complete
|
|
70
|
+
# Note: Access parallel outputs via the specific task ref names
|
|
71
|
+
aggregate = simple :aggregate_results,
|
|
72
|
+
result_a: '${process_type_a_ref.output.result}',
|
|
73
|
+
result_b: '${process_type_b_ref.output.result}'
|
|
74
|
+
|
|
75
|
+
output combined: aggregate[:result]
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
puts "Workflow: #{parallel_workflow.name}"
|
|
79
|
+
puts "Task count: #{parallel_workflow.builder.tasks.size} (includes fork/join)"
|
|
80
|
+
puts
|
|
81
|
+
|
|
82
|
+
# ============================================================================
|
|
83
|
+
# Example 3: Conditional Branching with Switch
|
|
84
|
+
# ============================================================================
|
|
85
|
+
puts '=== Example 3: Conditional Branching with Switch ==='
|
|
86
|
+
|
|
87
|
+
conditional_workflow = Conductor.workflow :ruby_conditional_workflow, version: 1 do
|
|
88
|
+
description 'Workflow with conditional branching'
|
|
89
|
+
|
|
90
|
+
# Classification task
|
|
91
|
+
classify = simple :classify_request, request: wf[:request]
|
|
92
|
+
|
|
93
|
+
# Switch based on the classification output
|
|
94
|
+
decide classify[:requestType] do
|
|
95
|
+
on 'TYPE_A' do
|
|
96
|
+
simple :handle_type_a, data: classify[:data]
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
on 'TYPE_B' do
|
|
100
|
+
simple :handle_type_b, data: classify[:data]
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
otherwise do
|
|
104
|
+
simple :handle_default, data: classify[:data]
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
output status: 'completed'
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
puts "Workflow: #{conditional_workflow.name}"
|
|
112
|
+
puts 'Switch cases: TYPE_A, TYPE_B, default'
|
|
113
|
+
puts
|
|
114
|
+
|
|
115
|
+
# ============================================================================
|
|
116
|
+
# Example 4: HTTP Task
|
|
117
|
+
# ============================================================================
|
|
118
|
+
puts '=== Example 4: HTTP Task ==='
|
|
119
|
+
|
|
120
|
+
http_workflow = Conductor.workflow :ruby_http_workflow, version: 1 do
|
|
121
|
+
description 'Workflow with HTTP calls'
|
|
122
|
+
|
|
123
|
+
# HTTP task with dynamic URL from workflow input
|
|
124
|
+
api_call = http :call_api,
|
|
125
|
+
url: 'https://api.example.com/users/${workflow.input.userId}',
|
|
126
|
+
method: :get,
|
|
127
|
+
headers: { 'Authorization' => 'Bearer ${workflow.input.token}' }
|
|
128
|
+
|
|
129
|
+
# Process the response
|
|
130
|
+
process = simple :process_response,
|
|
131
|
+
status_code: api_call[:statusCode],
|
|
132
|
+
body: api_call[:body]
|
|
133
|
+
|
|
134
|
+
output result: process[:result]
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
puts "Workflow: #{http_workflow.name}"
|
|
138
|
+
puts 'HTTP endpoint: api.example.com/users'
|
|
139
|
+
puts
|
|
140
|
+
|
|
141
|
+
# ============================================================================
|
|
142
|
+
# Example 5: Sub-Workflow Task
|
|
143
|
+
# ============================================================================
|
|
144
|
+
puts '=== Example 5: Sub-Workflow Task ==='
|
|
145
|
+
|
|
146
|
+
parent_workflow = Conductor.workflow :ruby_parent_workflow, version: 1 do
|
|
147
|
+
description 'Parent workflow that calls a sub-workflow'
|
|
148
|
+
|
|
149
|
+
prepare = simple :prepare_data, input: wf[:data]
|
|
150
|
+
|
|
151
|
+
# Call an existing workflow as a sub-workflow
|
|
152
|
+
child_result = sub_workflow :call_child,
|
|
153
|
+
workflow: 'child_workflow_name',
|
|
154
|
+
version: 1,
|
|
155
|
+
data: prepare[:preparedData]
|
|
156
|
+
|
|
157
|
+
process = simple :process_child_result, child_output: child_result[:output]
|
|
158
|
+
|
|
159
|
+
output final_result: process[:result]
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
puts "Workflow: #{parent_workflow.name}"
|
|
163
|
+
puts 'Calls sub-workflow: child_workflow_name'
|
|
164
|
+
puts
|
|
165
|
+
|
|
166
|
+
# ============================================================================
|
|
167
|
+
# Example 6: Loop with Times
|
|
168
|
+
# ============================================================================
|
|
169
|
+
puts '=== Example 6: Loop with Times ==='
|
|
170
|
+
|
|
171
|
+
loop_workflow = Conductor.workflow :ruby_loop_workflow, version: 1 do
|
|
172
|
+
description 'Workflow with loop iteration'
|
|
173
|
+
|
|
174
|
+
init = simple :initialize, count: 0
|
|
175
|
+
|
|
176
|
+
# Loop 3 times
|
|
177
|
+
loop_times 3 do
|
|
178
|
+
simple :process_batch, iteration: '${do_while_ref.output.iteration}'
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
finalize = simple :finalize, batches_processed: 3
|
|
182
|
+
|
|
183
|
+
output result: finalize[:summary]
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
puts "Workflow: #{loop_workflow.name}"
|
|
187
|
+
puts 'Loops 3 times'
|
|
188
|
+
puts
|
|
189
|
+
|
|
190
|
+
# ============================================================================
|
|
191
|
+
# Example 7: Inline Workflow Definition
|
|
192
|
+
# ============================================================================
|
|
193
|
+
puts '=== Example 7: Inline Workflow ==='
|
|
194
|
+
|
|
195
|
+
inline_demo = Conductor.workflow :ruby_inline_workflow, version: 1 do
|
|
196
|
+
description 'Workflow with inline sub-workflow'
|
|
197
|
+
|
|
198
|
+
start = simple :start_processing, data: wf[:input_data]
|
|
199
|
+
|
|
200
|
+
# Define a sub-workflow inline
|
|
201
|
+
inline_workflow :process_order, version: 1 do
|
|
202
|
+
validate = simple :validate_order
|
|
203
|
+
simple :charge_payment, amount: validate[:total]
|
|
204
|
+
simple :ship_order
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
complete = simple :complete_processing
|
|
208
|
+
|
|
209
|
+
output status: 'done'
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
puts "Workflow: #{inline_demo.name}"
|
|
213
|
+
puts 'Contains inline sub-workflow'
|
|
214
|
+
puts
|
|
215
|
+
|
|
216
|
+
# ============================================================================
|
|
217
|
+
# Example 8: Wait and Human Tasks
|
|
218
|
+
# ============================================================================
|
|
219
|
+
puts '=== Example 8: Wait and Human Tasks ==='
|
|
220
|
+
|
|
221
|
+
approval_workflow = Conductor.workflow :ruby_approval_workflow, version: 1 do
|
|
222
|
+
description 'Workflow with wait and human tasks'
|
|
223
|
+
|
|
224
|
+
submit = simple :submit_request, request: wf[:request]
|
|
225
|
+
|
|
226
|
+
# Human task for approval
|
|
227
|
+
approval = human :manager_approval,
|
|
228
|
+
assignee: wf[:manager_email],
|
|
229
|
+
display_name: 'Approve Request'
|
|
230
|
+
|
|
231
|
+
# Conditional based on approval
|
|
232
|
+
decide approval[:decision] do
|
|
233
|
+
on 'approved' do
|
|
234
|
+
simple :process_approved
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
on 'rejected' do
|
|
238
|
+
terminate :failed, 'Request rejected by manager'
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
otherwise do
|
|
242
|
+
# Wait for escalation
|
|
243
|
+
wait 86_400 # 24 hours
|
|
244
|
+
simple :escalate_request
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
output status: 'completed'
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
puts "Workflow: #{approval_workflow.name}"
|
|
252
|
+
puts 'Includes human task and wait'
|
|
253
|
+
puts
|
|
254
|
+
|
|
255
|
+
# ============================================================================
|
|
256
|
+
# Example 9: Complete Workflow with All Features
|
|
257
|
+
# ============================================================================
|
|
258
|
+
puts '=== Example 9: Complete Example Workflow ==='
|
|
259
|
+
|
|
260
|
+
complete_workflow = Conductor.workflow :ruby_complete_example, version: 1 do
|
|
261
|
+
description 'Complete example workflow demonstrating all features'
|
|
262
|
+
timeout 7200
|
|
263
|
+
owner_email 'team@example.com'
|
|
264
|
+
|
|
265
|
+
# Initialize
|
|
266
|
+
init = simple :initialize, user_id: wf[:userId]
|
|
267
|
+
|
|
268
|
+
# Validate
|
|
269
|
+
validate = simple :validate_input, data: init[:output]
|
|
270
|
+
|
|
271
|
+
# Process
|
|
272
|
+
process = simple :process_action, validated_data: validate[:output]
|
|
273
|
+
|
|
274
|
+
# Notify
|
|
275
|
+
notify = simple :notify_user,
|
|
276
|
+
user_id: wf[:userId],
|
|
277
|
+
result: process[:result]
|
|
278
|
+
|
|
279
|
+
# Set output parameters
|
|
280
|
+
output(
|
|
281
|
+
status: notify[:status],
|
|
282
|
+
completed_at: notify[:timestamp]
|
|
283
|
+
)
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
# Convert to workflow definition (for inspection)
|
|
287
|
+
workflow_def = complete_workflow.to_workflow_def
|
|
288
|
+
puts "Workflow: #{complete_workflow.name}"
|
|
289
|
+
puts "Version: #{workflow_def.version}"
|
|
290
|
+
puts "Task count: #{workflow_def.tasks.length}"
|
|
291
|
+
puts "Timeout: #{workflow_def.timeout_seconds} seconds"
|
|
292
|
+
puts
|
|
293
|
+
|
|
294
|
+
# ============================================================================
|
|
295
|
+
# Summary
|
|
296
|
+
# ============================================================================
|
|
297
|
+
puts '=' * 70
|
|
298
|
+
puts 'Examples completed!'
|
|
299
|
+
puts '=' * 70
|
|
300
|
+
puts
|
|
301
|
+
puts 'New DSL Features Demonstrated:'
|
|
302
|
+
puts ' - Conductor.workflow :name do...end - Entry point'
|
|
303
|
+
puts ' - simple, http, wait, terminate, sub_workflow, human - Task methods'
|
|
304
|
+
puts ' - parallel do...end - Concurrent execution'
|
|
305
|
+
puts ' - decide expr do...end - Conditional branching'
|
|
306
|
+
puts ' - loop_times N do...end - Loop iteration'
|
|
307
|
+
puts ' - inline_workflow :name do...end - Inline sub-workflows'
|
|
308
|
+
puts ' - wf[:param] - Workflow input references'
|
|
309
|
+
puts ' - task[:field] - Task output references'
|
|
310
|
+
puts ' - output key: value - Workflow output'
|
|
311
|
+
puts
|
|
312
|
+
puts 'To run workflows against a Conductor server:'
|
|
313
|
+
puts ' 1. Set CONDUCTOR_SERVER_URL environment variable'
|
|
314
|
+
puts ' 2. Add executor: parameter to Conductor.workflow'
|
|
315
|
+
puts ' 3. Call workflow.register(overwrite: true)'
|
|
316
|
+
puts ' 4. Call workflow.execute(input: { ... })'
|