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,373 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'logger'
5
+ require 'securerandom'
6
+ require_relative '../configuration'
7
+ require_relative '../http/api_client'
8
+ require_relative '../http/api/workflow_resource_api'
9
+ require_relative '../http/api/metadata_resource_api'
10
+ require_relative '../http/api/task_resource_api'
11
+ require_relative '../http/models/start_workflow_request'
12
+ require_relative '../worker/events/workflow_events'
13
+ require_relative '../worker/events/sync_event_dispatcher'
14
+
15
+ module Conductor
16
+ module Workflow
17
+ # WorkflowExecutor provides a high-level interface for executing workflows
18
+ # Supports both synchronous (wait for completion) and asynchronous execution
19
+ class WorkflowExecutor
20
+ attr_reader :workflow_api, :metadata_api, :task_api, :event_dispatcher
21
+
22
+ # Initialize WorkflowExecutor
23
+ # @param [Configuration] configuration Optional configuration
24
+ # @param [Worker::Events::SyncEventDispatcher, nil] event_dispatcher Optional event dispatcher
25
+ def initialize(configuration = nil, event_dispatcher: nil, logger: nil)
26
+ @configuration = configuration || Configuration.new
27
+ @logger = logger || Logger.new(File::NULL)
28
+ api_client = Http::ApiClient.new(configuration: @configuration)
29
+ @workflow_api = Http::Api::WorkflowResourceApi.new(api_client)
30
+ @metadata_api = Http::Api::MetadataResourceApi.new(api_client)
31
+ @task_api = Http::Api::TaskResourceApi.new(api_client)
32
+ @event_dispatcher = event_dispatcher || Worker::Events::SyncEventDispatcher.new
33
+ end
34
+
35
+ # ==========================================
36
+ # Workflow Definition Operations
37
+ # ==========================================
38
+
39
+ # Register a workflow definition
40
+ # @param [WorkflowDef, ConductorWorkflow] workflow Workflow definition or ConductorWorkflow DSL
41
+ # @param [Boolean] overwrite Overwrite existing definition (default: true)
42
+ # @return [Object] Response
43
+ def register_workflow(workflow, overwrite: true)
44
+ workflow_def = workflow.respond_to?(:to_workflow_def) ? workflow.to_workflow_def : workflow
45
+ @metadata_api.update_workflows([workflow_def], overwrite: overwrite)
46
+ end
47
+
48
+ # ==========================================
49
+ # Workflow Execution Operations
50
+ # ==========================================
51
+
52
+ # Start a workflow asynchronously (returns immediately with workflow ID)
53
+ # @param [StartWorkflowRequest] request Start workflow request
54
+ # @return [String] Workflow ID
55
+ def start_workflow(request)
56
+ publish_workflow_input_size(request)
57
+ @workflow_api.start_workflow(request)
58
+ rescue StandardError => e
59
+ publish_workflow_start_error(request, e)
60
+ raise
61
+ end
62
+
63
+ # Start multiple workflows
64
+ # @param [Array<StartWorkflowRequest>] requests List of start workflow requests
65
+ # @return [Array<String>] List of workflow IDs
66
+ def start_workflows(*requests)
67
+ requests.flatten.map { |request| start_workflow(request) }
68
+ end
69
+
70
+ # Execute a workflow synchronously and wait for completion
71
+ # @param [StartWorkflowRequest] request Start workflow request
72
+ # @param [String] wait_until_task_ref Wait until this task completes (optional)
73
+ # @param [Integer] wait_for_seconds Maximum time to wait (default: 10)
74
+ # @param [String] request_id Unique request ID for idempotency (auto-generated if not provided)
75
+ # @return [WorkflowRun] Workflow run result
76
+ def execute_workflow(request, wait_until_task_ref: nil, wait_for_seconds: 10, request_id: nil)
77
+ request_id ||= SecureRandom.uuid
78
+
79
+ @workflow_api.execute_workflow(
80
+ request,
81
+ name: request.name,
82
+ version: request.version || 1,
83
+ request_id: request_id,
84
+ wait_until_task_ref: wait_until_task_ref,
85
+ wait_for_seconds: wait_for_seconds
86
+ )
87
+ end
88
+
89
+ # Execute a workflow by name with input (convenience method)
90
+ # @param [String] name Workflow name
91
+ # @param [Hash] input Workflow input (default: {})
92
+ # @param [Integer] version Workflow version (optional)
93
+ # @param [String] correlation_id Correlation ID (optional)
94
+ # @param [String] domain Task domain for all tasks (optional)
95
+ # @param [String] wait_until_task_ref Wait until this task completes (optional)
96
+ # @param [Integer] wait_for_seconds Maximum time to wait (default: 10)
97
+ # @param [String] request_id Unique request ID (optional)
98
+ # @return [WorkflowRun] Workflow run result
99
+ def execute(name, input: {}, version: nil, correlation_id: nil, domain: nil,
100
+ wait_until_task_ref: nil, wait_for_seconds: 10, request_id: nil)
101
+ request = Http::Models::StartWorkflowRequest.new(
102
+ name: name,
103
+ input: input,
104
+ version: version,
105
+ correlation_id: correlation_id
106
+ )
107
+ request.task_to_domain = { '*' => domain } if domain
108
+
109
+ execute_workflow(
110
+ request,
111
+ wait_until_task_ref: wait_until_task_ref,
112
+ wait_for_seconds: wait_for_seconds,
113
+ request_id: request_id
114
+ )
115
+ end
116
+
117
+ # ==========================================
118
+ # Workflow Status Operations
119
+ # ==========================================
120
+
121
+ # Get workflow execution details
122
+ # @param [String] workflow_id Workflow ID
123
+ # @param [Boolean] include_tasks Include task details (default: true)
124
+ # @return [Workflow] Workflow object
125
+ def get_workflow(workflow_id, include_tasks: true)
126
+ @workflow_api.get_execution_status(workflow_id, include_tasks: include_tasks)
127
+ end
128
+
129
+ # Get workflow status (lightweight)
130
+ # @param [String] workflow_id Workflow ID
131
+ # @param [Boolean] include_output Include workflow output (default: false)
132
+ # @param [Boolean] include_variables Include workflow variables (default: false)
133
+ # @return [Hash] Workflow status
134
+ def get_workflow_status(workflow_id, include_output: false, include_variables: false)
135
+ @workflow_api.get_workflow_status(
136
+ workflow_id,
137
+ include_output: include_output,
138
+ include_variables: include_variables
139
+ )
140
+ end
141
+
142
+ # ==========================================
143
+ # Workflow Control Operations
144
+ # ==========================================
145
+
146
+ # Pause a running workflow
147
+ # @param [String] workflow_id Workflow ID
148
+ # @return [void]
149
+ def pause(workflow_id)
150
+ @workflow_api.pause_workflow(workflow_id)
151
+ end
152
+
153
+ # Resume a paused workflow
154
+ # @param [String] workflow_id Workflow ID
155
+ # @return [void]
156
+ def resume(workflow_id)
157
+ @workflow_api.resume_workflow(workflow_id)
158
+ end
159
+
160
+ # Terminate a running workflow
161
+ # @param [String] workflow_id Workflow ID
162
+ # @param [String] reason Termination reason (optional)
163
+ # @return [void]
164
+ def terminate(workflow_id, reason: nil)
165
+ @workflow_api.terminate(workflow_id, reason: reason)
166
+ end
167
+
168
+ # Restart a completed workflow
169
+ # @param [String] workflow_id Workflow ID
170
+ # @param [Boolean] use_latest_definitions Use latest workflow definition (default: false)
171
+ # @return [void]
172
+ def restart(workflow_id, use_latest_definitions: false)
173
+ @workflow_api.restart(workflow_id, use_latest_def: use_latest_definitions)
174
+ end
175
+
176
+ # Retry a failed workflow
177
+ # @param [String] workflow_id Workflow ID
178
+ # @param [Boolean] resume_subworkflow_tasks Resume subworkflow tasks (default: false)
179
+ # @return [void]
180
+ def retry(workflow_id, resume_subworkflow_tasks: false)
181
+ @workflow_api.retry(workflow_id, resume_subworkflow_tasks: resume_subworkflow_tasks)
182
+ end
183
+
184
+ # Rerun a workflow from a specific task
185
+ # @param [String] workflow_id Workflow ID
186
+ # @param [Hash, RerunWorkflowRequest] rerun_request Rerun configuration
187
+ # @return [String] New workflow ID
188
+ def rerun(workflow_id, rerun_request)
189
+ @workflow_api.rerun(workflow_id, rerun_request)
190
+ end
191
+
192
+ # Remove (delete) a workflow permanently
193
+ # @param [String] workflow_id Workflow ID
194
+ # @param [Boolean] archive_workflow Archive before deleting (default: true)
195
+ # @return [void]
196
+ def remove_workflow(workflow_id, archive_workflow: true)
197
+ @workflow_api.delete(workflow_id, archive_workflow: archive_workflow)
198
+ end
199
+
200
+ # Skip a task in a running workflow
201
+ # @param [String] workflow_id Workflow ID
202
+ # @param [String] task_reference_name Task reference name to skip
203
+ # @param [Hash] request Skip task request (optional)
204
+ # @return [void]
205
+ def skip_task_from_workflow(workflow_id, task_reference_name, request: nil)
206
+ @workflow_api.skip_task_from_workflow(workflow_id, task_reference_name, request: request)
207
+ end
208
+
209
+ # ==========================================
210
+ # Task Operations
211
+ # ==========================================
212
+
213
+ # Update a task result
214
+ # @param [String] workflow_id Workflow ID
215
+ # @param [String] task_id Task ID
216
+ # @param [Hash] task_output Task output data
217
+ # @param [String] status Task status (COMPLETED, FAILED, etc.)
218
+ # @return [String] Task update result
219
+ def update_task(workflow_id, task_id, task_output, status)
220
+ task_result = Http::Models::TaskResult.new(
221
+ workflow_instance_id: workflow_id,
222
+ task_id: task_id,
223
+ output_data: task_output,
224
+ status: status
225
+ )
226
+ @task_api.update_task(task_result)
227
+ end
228
+
229
+ # Update a task by reference name
230
+ # @param [String] workflow_id Workflow ID
231
+ # @param [String] task_reference_name Task reference name
232
+ # @param [Hash] task_output Task output data
233
+ # @param [String] status Task status
234
+ # @return [String] Task update result
235
+ def update_task_by_ref_name(workflow_id, task_reference_name, task_output, status)
236
+ @task_api.update_task_by_ref_name(
237
+ task_output,
238
+ workflow_id: workflow_id,
239
+ task_ref_name: task_reference_name,
240
+ status: status
241
+ )
242
+ end
243
+
244
+ # Get a task by ID
245
+ # @param [String] task_id Task ID
246
+ # @return [Task] Task object
247
+ def get_task(task_id)
248
+ @task_api.get_task(task_id)
249
+ end
250
+
251
+ # ==========================================
252
+ # Correlation ID Operations
253
+ # ==========================================
254
+
255
+ # Get workflows by correlation ID
256
+ # @param [String] workflow_name Workflow name
257
+ # @param [String] correlation_id Correlation ID
258
+ # @param [Boolean] include_closed Include closed workflows (default: false)
259
+ # @param [Boolean] include_tasks Include task details (default: false)
260
+ # @return [Array<Workflow>] List of workflows
261
+ def get_by_correlation_id(workflow_name, correlation_id, include_closed: false, include_tasks: false)
262
+ @workflow_api.get_workflows(
263
+ workflow_name,
264
+ correlation_id,
265
+ include_closed: include_closed,
266
+ include_tasks: include_tasks
267
+ )
268
+ end
269
+
270
+ # Get workflows by multiple correlation IDs
271
+ # @param [String] workflow_name Workflow name
272
+ # @param [Array<String>] correlation_ids List of correlation IDs
273
+ # @param [Boolean] include_closed Include closed workflows (default: false)
274
+ # @param [Boolean] include_tasks Include task details (default: false)
275
+ # @return [Hash<String, Array<Workflow>>] Map of correlation ID to workflows
276
+ def get_by_correlation_ids(workflow_name, correlation_ids, include_closed: false, include_tasks: false)
277
+ # NOTE: This would require a batch API endpoint; for now, iterate
278
+ result = {}
279
+ correlation_ids.each do |correlation_id|
280
+ result[correlation_id] = get_by_correlation_id(
281
+ workflow_name,
282
+ correlation_id,
283
+ include_closed: include_closed,
284
+ include_tasks: include_tasks
285
+ )
286
+ end
287
+ result
288
+ end
289
+
290
+ # ==========================================
291
+ # Polling Helpers
292
+ # ==========================================
293
+
294
+ # Wait for a workflow to complete
295
+ # @param [String] workflow_id Workflow ID
296
+ # @param [Integer] timeout_seconds Maximum time to wait (default: 60)
297
+ # @param [Float] poll_interval_seconds Polling interval (default: 1.0)
298
+ # @return [Workflow] Completed workflow
299
+ # @raise [Timeout::Error] If workflow doesn't complete within timeout
300
+ def wait_for_workflow(workflow_id, timeout_seconds: 60, poll_interval_seconds: 1.0)
301
+ deadline = Time.now + timeout_seconds
302
+
303
+ loop do
304
+ workflow = get_workflow(workflow_id, include_tasks: false)
305
+ return workflow if workflow.terminal?
306
+
307
+ raise Timeout::Error, "Workflow #{workflow_id} did not complete within #{timeout_seconds} seconds" if Time.now >= deadline
308
+
309
+ sleep(poll_interval_seconds)
310
+ end
311
+ end
312
+
313
+ # Execute a workflow and wait for completion (with polling fallback)
314
+ # @param [String] name Workflow name
315
+ # @param [Hash] input Workflow input
316
+ # @param [Integer] timeout_seconds Maximum time to wait
317
+ # @param [Hash] options Additional options (version, correlation_id, etc.)
318
+ # @return [Workflow] Completed workflow
319
+ def execute_and_wait(name, input: {}, timeout_seconds: 60, **options)
320
+ # First try synchronous execution
321
+ result = execute(
322
+ name,
323
+ input: input,
324
+ wait_for_seconds: [timeout_seconds, 30].min, # Server-side wait capped at 30s typically
325
+ **options
326
+ )
327
+
328
+ # If still running, poll for completion
329
+ if result.running?
330
+ wait_for_workflow(result.workflow_id, timeout_seconds: timeout_seconds)
331
+ else
332
+ get_workflow(result.workflow_id)
333
+ end
334
+ end
335
+
336
+ private
337
+
338
+ def publish_workflow_input_size(request)
339
+ return unless @event_dispatcher.has_listeners?(Worker::Events::WorkflowInputSize)
340
+
341
+ name = request.respond_to?(:name) ? request.name : nil
342
+ return unless name
343
+
344
+ input_bytes = begin
345
+ (request.respond_to?(:input) ? request.input : nil).to_json.bytesize
346
+ rescue StandardError
347
+ 0
348
+ end
349
+
350
+ @event_dispatcher.publish(Worker::Events::WorkflowInputSize.new(
351
+ workflow_type: name,
352
+ version: request.respond_to?(:version) ? request.version : nil,
353
+ size_bytes: input_bytes
354
+ ))
355
+ rescue StandardError => e
356
+ @logger.debug { "Telemetry error (non-fatal): #{e.class}: #{e.message}" }
357
+ end
358
+
359
+ def publish_workflow_start_error(request, error)
360
+ wf_name = (request.respond_to?(:name) ? request.name : nil) || 'unknown'
361
+ wf_version = request.respond_to?(:version) ? request.version : nil
362
+
363
+ @event_dispatcher.publish(Worker::Events::WorkflowStartError.new(
364
+ workflow_type: wf_name,
365
+ version: wf_version,
366
+ cause: error
367
+ ))
368
+ rescue StandardError => e
369
+ @logger.debug { "Telemetry error (non-fatal): #{e.class}: #{e.message}" }
370
+ end
371
+ end
372
+ end
373
+ end
data/lib/conductor.rb ADDED
@@ -0,0 +1,192 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'conductor/version'
4
+ require_relative 'conductor/configuration'
5
+ require_relative 'conductor/configuration/authentication_settings'
6
+ require_relative 'conductor/exceptions'
7
+ require_relative 'conductor/http/rest_client'
8
+ require_relative 'conductor/http/api_client'
9
+ require_relative 'conductor/http/models/base_model'
10
+ require_relative 'conductor/http/models/token'
11
+ require_relative 'conductor/http/models/task_result_status'
12
+ require_relative 'conductor/http/models/task'
13
+ require_relative 'conductor/http/models/task_result'
14
+ require_relative 'conductor/http/models/workflow_status_constants'
15
+ require_relative 'conductor/http/models/start_workflow_request'
16
+ require_relative 'conductor/http/models/workflow_task'
17
+ require_relative 'conductor/http/models/workflow_def'
18
+ require_relative 'conductor/http/models/workflow'
19
+ require_relative 'conductor/http/models/task_def'
20
+ require_relative 'conductor/http/models/task_exec_log'
21
+ require_relative 'conductor/http/models/rerun_workflow_request'
22
+ require_relative 'conductor/http/models/skip_task_request'
23
+ require_relative 'conductor/http/models/bulk_response'
24
+ require_relative 'conductor/http/models/poll_data'
25
+ require_relative 'conductor/http/models/workflow_state_update'
26
+ require_relative 'conductor/http/models/workflow_test_request'
27
+ require_relative 'conductor/http/models/search_result'
28
+ require_relative 'conductor/http/models/event_handler'
29
+ require_relative 'conductor/http/models/workflow_schedule'
30
+ # Orkes-specific models
31
+ require_relative 'conductor/http/models/tag_object'
32
+ require_relative 'conductor/http/models/subject_ref'
33
+ require_relative 'conductor/http/models/target_ref'
34
+ require_relative 'conductor/http/models/authorization_request'
35
+ require_relative 'conductor/http/models/permission'
36
+ require_relative 'conductor/http/models/role'
37
+ require_relative 'conductor/http/models/group'
38
+ require_relative 'conductor/http/models/conductor_user'
39
+ require_relative 'conductor/http/models/conductor_application'
40
+ require_relative 'conductor/http/models/upsert_user_request'
41
+ require_relative 'conductor/http/models/upsert_group_request'
42
+ require_relative 'conductor/http/models/create_or_update_application_request'
43
+ require_relative 'conductor/http/models/create_or_update_role_request'
44
+ require_relative 'conductor/http/models/generate_token_request'
45
+ require_relative 'conductor/http/models/authentication_config'
46
+ require_relative 'conductor/http/models/prompt_template'
47
+ require_relative 'conductor/http/models/prompt_template_test_request'
48
+ require_relative 'conductor/http/models/schema_def'
49
+ require_relative 'conductor/http/models/integration'
50
+ require_relative 'conductor/http/models/integration_api'
51
+ require_relative 'conductor/http/models/integration_update'
52
+ require_relative 'conductor/http/models/integration_api_update'
53
+ # OSS API resources
54
+ require_relative 'conductor/http/api/workflow_resource_api'
55
+ require_relative 'conductor/http/api/task_resource_api'
56
+ require_relative 'conductor/http/api/metadata_resource_api'
57
+ require_relative 'conductor/http/api/workflow_bulk_resource_api'
58
+ require_relative 'conductor/http/api/event_resource_api'
59
+ require_relative 'conductor/http/api/scheduler_resource_api'
60
+ # Orkes API resources
61
+ require_relative 'conductor/http/api/secret_resource_api'
62
+ require_relative 'conductor/http/api/application_resource_api'
63
+ require_relative 'conductor/http/api/user_resource_api'
64
+ require_relative 'conductor/http/api/group_resource_api'
65
+ require_relative 'conductor/http/api/authorization_resource_api'
66
+ require_relative 'conductor/http/api/role_resource_api'
67
+ require_relative 'conductor/http/api/token_resource_api'
68
+ require_relative 'conductor/http/api/gateway_auth_resource_api'
69
+ require_relative 'conductor/http/api/schema_resource_api'
70
+ require_relative 'conductor/http/api/integration_resource_api'
71
+ require_relative 'conductor/http/api/prompt_resource_api'
72
+ # OSS Clients
73
+ require_relative 'conductor/client/workflow_client'
74
+ require_relative 'conductor/client/task_client'
75
+ require_relative 'conductor/client/metadata_client'
76
+ require_relative 'conductor/client/scheduler_client'
77
+ # Orkes Clients
78
+ require_relative 'conductor/client/secret_client'
79
+ require_relative 'conductor/client/authorization_client'
80
+ require_relative 'conductor/client/schema_client'
81
+ require_relative 'conductor/client/integration_client'
82
+ require_relative 'conductor/client/prompt_client'
83
+ # Orkes-specific models
84
+ require_relative 'conductor/orkes/models/metadata_tag'
85
+ require_relative 'conductor/orkes/models/rate_limit_tag'
86
+ require_relative 'conductor/orkes/models/access_key'
87
+ require_relative 'conductor/orkes/models/granted_permission'
88
+ # Orkes factory
89
+ require_relative 'conductor/orkes/orkes_clients'
90
+ # Worker Infrastructure
91
+ require_relative 'conductor/worker/events/conductor_event'
92
+ require_relative 'conductor/worker/events/task_runner_events'
93
+ require_relative 'conductor/worker/events/workflow_events'
94
+ require_relative 'conductor/worker/events/http_events'
95
+ require_relative 'conductor/worker/events/sync_event_dispatcher'
96
+ require_relative 'conductor/worker/events/global_dispatcher'
97
+ require_relative 'conductor/worker/events/listeners'
98
+ require_relative 'conductor/worker/events/listener_registry'
99
+ require_relative 'conductor/worker/task_context'
100
+ require_relative 'conductor/worker/task_in_progress'
101
+ require_relative 'conductor/worker/worker_config'
102
+ require_relative 'conductor/worker/worker_registry'
103
+ require_relative 'conductor/worker/worker'
104
+ require_relative 'conductor/worker/task_runner'
105
+ require_relative 'conductor/worker/task_handler'
106
+ require_relative 'conductor/worker/telemetry/metrics_collector'
107
+ require_relative 'conductor/worker/task_definition_registrar'
108
+ # Optional executors - these are lazy-loaded when their dependencies are available
109
+ # require_relative 'conductor/worker/ractor_task_runner' # Requires Ruby 3.1+
110
+ # require_relative 'conductor/worker/fiber_executor' # Requires async gem
111
+ # require_relative 'conductor/worker/telemetry/prometheus_backend' # Requires prometheus-client gem
112
+
113
+ # Workflow DSL
114
+ require_relative 'conductor/workflow/task_type'
115
+ require_relative 'conductor/workflow/timeout_policy'
116
+ require_relative 'conductor/workflow/workflow_executor'
117
+ # LLM/AI helpers (used by DSL)
118
+ require_relative 'conductor/workflow/llm/chat_message'
119
+ require_relative 'conductor/workflow/llm/tool_call'
120
+ require_relative 'conductor/workflow/llm/tool_spec'
121
+ require_relative 'conductor/workflow/llm/embedding_model'
122
+ # Workflow DSL
123
+ require_relative 'conductor/workflow/dsl/output_ref'
124
+ require_relative 'conductor/workflow/dsl/input_ref'
125
+ require_relative 'conductor/workflow/dsl/task_ref'
126
+ require_relative 'conductor/workflow/dsl/workflow_builder'
127
+ require_relative 'conductor/workflow/dsl/parallel_builder'
128
+ require_relative 'conductor/workflow/dsl/switch_builder'
129
+ require_relative 'conductor/workflow/dsl/workflow_definition'
130
+
131
+ # Main Conductor module
132
+ # Provides convenience methods for configuration
133
+ module Conductor
134
+ class << self
135
+ # Get or set the default configuration
136
+ # @return [Conductor::Configuration] The default configuration
137
+ def config
138
+ @config ||= Conductor::Configuration.new
139
+ end
140
+
141
+ # Set the default configuration
142
+ # @param [Conductor::Configuration] configuration The configuration to set
143
+ attr_writer :config
144
+
145
+ # Configure Conductor with a block
146
+ # @yield [Conductor::Configuration] The configuration object
147
+ # @example
148
+ # Conductor.configure do |config|
149
+ # config.server_url = 'http://localhost:7001/api'
150
+ # config.authentication_settings = Conductor::Configuration::AuthenticationSettings.new(
151
+ # key_id: 'my_key',
152
+ # key_secret: 'my_secret'
153
+ # )
154
+ # end
155
+ def configure
156
+ yield(config) if block_given?
157
+ end
158
+
159
+ # Define a workflow using the new Ruby-idiomatic DSL
160
+ # @param name [Symbol, String] Workflow name
161
+ # @param version [Integer, nil] Workflow version (optional)
162
+ # @param description [String, nil] Workflow description (optional)
163
+ # @param executor [WorkflowExecutor, nil] Optional executor for .register() and .execute()
164
+ # @yield Block containing workflow definition
165
+ # @return [Workflow::Dsl::WorkflowDefinition] The workflow definition
166
+ # @example
167
+ # workflow = Conductor.workflow :order_processing, version: 1, executor: executor do
168
+ # user = simple :get_user, user_id: wf[:user_id]
169
+ # order = simple :create_order, user_email: user[:email]
170
+ #
171
+ # parallel do
172
+ # simple :send_confirmation, email: user[:email]
173
+ # simple :update_inventory, order_id: order[:id]
174
+ # end
175
+ #
176
+ # output order_id: order[:id], status: 'created'
177
+ # end
178
+ #
179
+ # workflow.register(overwrite: true)
180
+ # result = workflow.execute(input: { user_id: 123 })
181
+ def workflow(name, version: nil, description: nil, executor: nil, &block)
182
+ builder = Workflow::Dsl::WorkflowBuilder.new(
183
+ name.to_s,
184
+ version: version,
185
+ description: description,
186
+ executor: executor
187
+ )
188
+ builder.instance_eval(&block)
189
+ Workflow::Dsl::WorkflowDefinition.new(builder, executor: executor)
190
+ end
191
+ end
192
+ end