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.
Files changed (143) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +142 -0
  3. data/LICENSE +190 -0
  4. data/README.md +517 -0
  5. data/examples/agentic_workflows/llm_chat.rb +106 -0
  6. data/examples/dynamic_workflow.rb +177 -0
  7. data/examples/event_handler.rb +94 -0
  8. data/examples/event_listener_examples.rb +430 -0
  9. data/examples/helloworld/greetings_worker.rb +24 -0
  10. data/examples/helloworld/helloworld.rb +99 -0
  11. data/examples/kitchensink.rb +213 -0
  12. data/examples/metadata_journey.rb +189 -0
  13. data/examples/metrics_example.rb +284 -0
  14. data/examples/new_dsl_demo.rb +141 -0
  15. data/examples/orkes/http_poll.rb +83 -0
  16. data/examples/orkes/secrets_example.rb +69 -0
  17. data/examples/orkes/wait_for_webhook.rb +90 -0
  18. data/examples/prompt_journey.rb +245 -0
  19. data/examples/rag_workflow.rb +167 -0
  20. data/examples/schedule_journey.rb +244 -0
  21. data/examples/simple_worker.rb +125 -0
  22. data/examples/simple_workflow.rb +89 -0
  23. data/examples/task_context_example.rb +257 -0
  24. data/examples/task_listener_example.rb +192 -0
  25. data/examples/worker_configuration_example.rb +282 -0
  26. data/examples/workflow_dsl.rb +316 -0
  27. data/examples/workflow_ops.rb +305 -0
  28. data/lib/conductor/client/authorization_client.rb +238 -0
  29. data/lib/conductor/client/integration_client.rb +108 -0
  30. data/lib/conductor/client/metadata_client.rb +139 -0
  31. data/lib/conductor/client/prompt_client.rb +58 -0
  32. data/lib/conductor/client/scheduler_client.rb +132 -0
  33. data/lib/conductor/client/schema_client.rb +32 -0
  34. data/lib/conductor/client/secret_client.rb +48 -0
  35. data/lib/conductor/client/task_client.rb +168 -0
  36. data/lib/conductor/client/workflow_client.rb +242 -0
  37. data/lib/conductor/configuration/authentication_settings.rb +17 -0
  38. data/lib/conductor/configuration.rb +103 -0
  39. data/lib/conductor/exceptions.rb +86 -0
  40. data/lib/conductor/http/api/application_resource_api.rb +107 -0
  41. data/lib/conductor/http/api/authorization_resource_api.rb +56 -0
  42. data/lib/conductor/http/api/event_resource_api.rb +133 -0
  43. data/lib/conductor/http/api/gateway_auth_resource_api.rb +48 -0
  44. data/lib/conductor/http/api/group_resource_api.rb +76 -0
  45. data/lib/conductor/http/api/integration_resource_api.rb +145 -0
  46. data/lib/conductor/http/api/metadata_resource_api.rb +231 -0
  47. data/lib/conductor/http/api/prompt_resource_api.rb +81 -0
  48. data/lib/conductor/http/api/role_resource_api.rb +60 -0
  49. data/lib/conductor/http/api/scheduler_resource_api.rb +211 -0
  50. data/lib/conductor/http/api/schema_resource_api.rb +82 -0
  51. data/lib/conductor/http/api/secret_resource_api.rb +134 -0
  52. data/lib/conductor/http/api/task_resource_api.rb +321 -0
  53. data/lib/conductor/http/api/token_resource_api.rb +42 -0
  54. data/lib/conductor/http/api/user_resource_api.rb +59 -0
  55. data/lib/conductor/http/api/workflow_bulk_resource_api.rb +91 -0
  56. data/lib/conductor/http/api/workflow_resource_api.rb +451 -0
  57. data/lib/conductor/http/api_client.rb +437 -0
  58. data/lib/conductor/http/models/authentication_config.rb +67 -0
  59. data/lib/conductor/http/models/authorization_request.rb +39 -0
  60. data/lib/conductor/http/models/base_model.rb +162 -0
  61. data/lib/conductor/http/models/bulk_response.rb +39 -0
  62. data/lib/conductor/http/models/conductor_application.rb +39 -0
  63. data/lib/conductor/http/models/conductor_user.rb +53 -0
  64. data/lib/conductor/http/models/create_or_update_application_request.rb +24 -0
  65. data/lib/conductor/http/models/create_or_update_role_request.rb +27 -0
  66. data/lib/conductor/http/models/event_handler.rb +130 -0
  67. data/lib/conductor/http/models/generate_token_request.rb +27 -0
  68. data/lib/conductor/http/models/group.rb +36 -0
  69. data/lib/conductor/http/models/integration.rb +70 -0
  70. data/lib/conductor/http/models/integration_api.rb +53 -0
  71. data/lib/conductor/http/models/integration_api_update.rb +43 -0
  72. data/lib/conductor/http/models/integration_update.rb +36 -0
  73. data/lib/conductor/http/models/permission.rb +24 -0
  74. data/lib/conductor/http/models/poll_data.rb +33 -0
  75. data/lib/conductor/http/models/prompt_template.rb +59 -0
  76. data/lib/conductor/http/models/prompt_template_test_request.rb +43 -0
  77. data/lib/conductor/http/models/rerun_workflow_request.rb +37 -0
  78. data/lib/conductor/http/models/role.rb +27 -0
  79. data/lib/conductor/http/models/schema_def.rb +59 -0
  80. data/lib/conductor/http/models/search_result.rb +187 -0
  81. data/lib/conductor/http/models/skip_task_request.rb +27 -0
  82. data/lib/conductor/http/models/start_workflow_request.rb +68 -0
  83. data/lib/conductor/http/models/subject_ref.rb +35 -0
  84. data/lib/conductor/http/models/tag_object.rb +36 -0
  85. data/lib/conductor/http/models/target_ref.rb +39 -0
  86. data/lib/conductor/http/models/task.rb +156 -0
  87. data/lib/conductor/http/models/task_def.rb +95 -0
  88. data/lib/conductor/http/models/task_exec_log.rb +30 -0
  89. data/lib/conductor/http/models/task_result.rb +115 -0
  90. data/lib/conductor/http/models/task_result_status.rb +24 -0
  91. data/lib/conductor/http/models/token.rb +33 -0
  92. data/lib/conductor/http/models/upsert_group_request.rb +30 -0
  93. data/lib/conductor/http/models/upsert_user_request.rb +39 -0
  94. data/lib/conductor/http/models/workflow.rb +202 -0
  95. data/lib/conductor/http/models/workflow_def.rb +73 -0
  96. data/lib/conductor/http/models/workflow_schedule.rb +100 -0
  97. data/lib/conductor/http/models/workflow_state_update.rb +30 -0
  98. data/lib/conductor/http/models/workflow_status_constants.rb +57 -0
  99. data/lib/conductor/http/models/workflow_task.rb +169 -0
  100. data/lib/conductor/http/models/workflow_test_request.rb +67 -0
  101. data/lib/conductor/http/rest_client.rb +211 -0
  102. data/lib/conductor/orkes/models/access_key.rb +56 -0
  103. data/lib/conductor/orkes/models/granted_permission.rb +27 -0
  104. data/lib/conductor/orkes/models/metadata_tag.rb +15 -0
  105. data/lib/conductor/orkes/models/rate_limit_tag.rb +15 -0
  106. data/lib/conductor/orkes/orkes_clients.rb +69 -0
  107. data/lib/conductor/version.rb +5 -0
  108. data/lib/conductor/worker/events/conductor_event.rb +40 -0
  109. data/lib/conductor/worker/events/global_dispatcher.rb +37 -0
  110. data/lib/conductor/worker/events/http_events.rb +25 -0
  111. data/lib/conductor/worker/events/listener_registry.rb +40 -0
  112. data/lib/conductor/worker/events/listeners.rb +34 -0
  113. data/lib/conductor/worker/events/sync_event_dispatcher.rb +78 -0
  114. data/lib/conductor/worker/events/task_runner_events.rb +271 -0
  115. data/lib/conductor/worker/events/workflow_events.rb +49 -0
  116. data/lib/conductor/worker/fiber_executor.rb +532 -0
  117. data/lib/conductor/worker/ractor_task_runner.rb +501 -0
  118. data/lib/conductor/worker/task_context.rb +114 -0
  119. data/lib/conductor/worker/task_definition_registrar.rb +322 -0
  120. data/lib/conductor/worker/task_handler.rb +360 -0
  121. data/lib/conductor/worker/task_in_progress.rb +60 -0
  122. data/lib/conductor/worker/task_runner.rb +538 -0
  123. data/lib/conductor/worker/telemetry/metrics_collector.rb +196 -0
  124. data/lib/conductor/worker/telemetry/prometheus_backend.rb +224 -0
  125. data/lib/conductor/worker/worker.rb +355 -0
  126. data/lib/conductor/worker/worker_config.rb +154 -0
  127. data/lib/conductor/worker/worker_registry.rb +71 -0
  128. data/lib/conductor/workflow/dsl/input_ref.rb +37 -0
  129. data/lib/conductor/workflow/dsl/output_ref.rb +44 -0
  130. data/lib/conductor/workflow/dsl/parallel_builder.rb +49 -0
  131. data/lib/conductor/workflow/dsl/switch_builder.rb +74 -0
  132. data/lib/conductor/workflow/dsl/task_ref.rb +178 -0
  133. data/lib/conductor/workflow/dsl/workflow_builder.rb +1016 -0
  134. data/lib/conductor/workflow/dsl/workflow_definition.rb +150 -0
  135. data/lib/conductor/workflow/llm/chat_message.rb +47 -0
  136. data/lib/conductor/workflow/llm/embedding_model.rb +19 -0
  137. data/lib/conductor/workflow/llm/tool_call.rb +43 -0
  138. data/lib/conductor/workflow/llm/tool_spec.rb +46 -0
  139. data/lib/conductor/workflow/task_type.rb +68 -0
  140. data/lib/conductor/workflow/timeout_policy.rb +31 -0
  141. data/lib/conductor/workflow/workflow_executor.rb +373 -0
  142. data/lib/conductor.rb +192 -0
  143. 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: { ... })'