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,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conductor
4
+ module Http
5
+ module Models
6
+ # Request to test a workflow with mocked task outputs
7
+ class WorkflowTestRequest < BaseModel
8
+ SWAGGER_TYPES = {
9
+ workflow_def: 'WorkflowDef',
10
+ name: 'String',
11
+ version: 'Integer',
12
+ input: 'Hash<String, Object>',
13
+ correlation_id: 'String',
14
+ task_ref_to_mock_output: 'Hash<String, Array<WorkflowTestTaskResult>>',
15
+ external_input_payload_storage_path: 'String',
16
+ sub_workflow_test_request: 'Hash<String, WorkflowTestRequest>'
17
+ }.freeze
18
+
19
+ ATTRIBUTE_MAP = {
20
+ workflow_def: :workflowDef,
21
+ name: :name,
22
+ version: :version,
23
+ input: :input,
24
+ correlation_id: :correlationId,
25
+ task_ref_to_mock_output: :taskRefToMockOutput,
26
+ external_input_payload_storage_path: :externalInputPayloadStoragePath,
27
+ sub_workflow_test_request: :subWorkflowTestRequest
28
+ }.freeze
29
+
30
+ attr_accessor :workflow_def, :name, :version, :input, :correlation_id,
31
+ :task_ref_to_mock_output, :external_input_payload_storage_path,
32
+ :sub_workflow_test_request
33
+
34
+ def initialize(params = {})
35
+ @workflow_def = params[:workflow_def]
36
+ @name = params[:name]
37
+ @version = params[:version]
38
+ @input = params[:input] || {}
39
+ @correlation_id = params[:correlation_id]
40
+ @task_ref_to_mock_output = params[:task_ref_to_mock_output] || {}
41
+ @external_input_payload_storage_path = params[:external_input_payload_storage_path]
42
+ @sub_workflow_test_request = params[:sub_workflow_test_request] || {}
43
+ end
44
+ end
45
+
46
+ # Mock task result for workflow testing
47
+ class WorkflowTestTaskResult < BaseModel
48
+ SWAGGER_TYPES = {
49
+ status: 'String',
50
+ output: 'Hash<String, Object>'
51
+ }.freeze
52
+
53
+ ATTRIBUTE_MAP = {
54
+ status: :status,
55
+ output: :output
56
+ }.freeze
57
+
58
+ attr_accessor :status, :output
59
+
60
+ def initialize(params = {})
61
+ @status = params[:status] || 'COMPLETED'
62
+ @output = params[:output] || {}
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,211 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+ require 'faraday/net_http_persistent'
5
+ require 'faraday/retry'
6
+ require 'json'
7
+ require 'logger'
8
+ require 'uri'
9
+
10
+ module Conductor
11
+ module Http
12
+ # Faraday-based REST client with HTTP/2 support
13
+ class RestClient
14
+ attr_reader :connection
15
+
16
+ def initialize(configuration = nil, logger: nil)
17
+ @configuration = configuration
18
+ @logger = logger || Logger.new(File::NULL)
19
+ @connection = build_connection
20
+ end
21
+
22
+ # Main request method
23
+ def request(method, url, query: nil, headers: nil, body: nil, metric_uri: nil)
24
+ method = method.to_s.upcase
25
+ raise ArgumentError, "Invalid HTTP method: #{method}" unless valid_method?(method)
26
+
27
+ headers ||= {}
28
+ headers['Content-Type'] ||= 'application/json' if %w[POST PUT PATCH DELETE OPTIONS].include?(method)
29
+
30
+ timing = http_metrics_enabled?
31
+ start_time = Time.now if timing
32
+ status_code = '0'
33
+
34
+ begin
35
+ response = @connection.run_request(method.downcase.to_sym, url, nil, headers) do |req|
36
+ req.params = query if query
37
+ req.body = serialize_body(body, headers['Content-Type']) if body
38
+ end
39
+ status_code = response.status.to_s
40
+
41
+ result = handle_response(response)
42
+ emit_http_event(method, url, status_code, start_time, metric_uri: metric_uri) if timing
43
+ result
44
+ rescue Faraday::TimeoutError => e
45
+ emit_http_event(method, url, '0', start_time, metric_uri: metric_uri) if timing
46
+ raise ApiError.new("Request timeout: #{e.message}", status: 0, reason: 'Timeout')
47
+ rescue Faraday::ConnectionFailed => e
48
+ emit_http_event(method, url, '0', start_time, metric_uri: metric_uri) if timing
49
+ raise ApiError.new("Connection error: #{e.message}", status: 0, reason: 'ConnectionFailed')
50
+ rescue ApiError, AuthorizationError
51
+ emit_http_event(method, url, status_code, start_time, metric_uri: metric_uri) if timing
52
+ raise
53
+ end
54
+ end
55
+
56
+ # Convenience methods
57
+ def get(url, query: nil, headers: nil)
58
+ request('GET', url, query: query, headers: headers)
59
+ end
60
+
61
+ def post(url, body: nil, query: nil, headers: nil)
62
+ request('POST', url, query: query, headers: headers, body: body)
63
+ end
64
+
65
+ def put(url, body: nil, query: nil, headers: nil)
66
+ request('PUT', url, query: query, headers: headers, body: body)
67
+ end
68
+
69
+ def patch(url, body: nil, query: nil, headers: nil)
70
+ request('PATCH', url, query: query, headers: headers, body: body)
71
+ end
72
+
73
+ def delete(url, body: nil, query: nil, headers: nil)
74
+ request('DELETE', url, query: query, headers: headers, body: body)
75
+ end
76
+
77
+ def head(url, query: nil, headers: nil)
78
+ request('HEAD', url, query: query, headers: headers)
79
+ end
80
+
81
+ def options(url, body: nil, query: nil, headers: nil)
82
+ request('OPTIONS', url, query: query, headers: headers, body: body)
83
+ end
84
+
85
+ def close
86
+ @connection&.close
87
+ end
88
+
89
+ private
90
+
91
+ def http_metrics_enabled?
92
+ defined?(Conductor::Worker::Events::GlobalDispatcher) &&
93
+ Conductor::Worker::Events::GlobalDispatcher.http_metrics_enabled?
94
+ end
95
+
96
+ def emit_http_event(method, url, status, start_time, metric_uri: nil)
97
+ duration_ms = (Time.now - start_time) * 1000
98
+ uri_path = metric_uri || URI.parse(url).request_uri
99
+ event = Conductor::Worker::Events::HttpApiRequest.new(
100
+ method: method, uri: uri_path, status: status, duration_ms: duration_ms
101
+ )
102
+ Conductor::Worker::Events::GlobalDispatcher.publish(event)
103
+ rescue StandardError => e
104
+ @logger.debug { "Telemetry error (non-fatal): #{e.class}: #{e.message}" }
105
+ end
106
+
107
+ def build_connection
108
+ Faraday.new do |conn|
109
+ # HTTP/2 adapter with persistent connections
110
+ conn.adapter :net_http_persistent do |http|
111
+ http.idle_timeout = 30
112
+ end
113
+
114
+ # Retry middleware (3 retries with exponential backoff)
115
+ conn.request :retry,
116
+ max: 3,
117
+ interval: 0.5,
118
+ backoff_factor: 2,
119
+ retry_statuses: [408, 429, 500, 502, 503, 504],
120
+ methods: %i[get post put patch delete]
121
+
122
+ # Connection settings
123
+ conn.options.timeout = 120 # 120s total timeout
124
+ conn.options.open_timeout = 10 # 10s connection timeout
125
+
126
+ # SSL settings from configuration
127
+ if @configuration
128
+ conn.ssl.verify = @configuration.verify_ssl
129
+ conn.ssl.ca_file = @configuration.ssl_ca_cert if @configuration.ssl_ca_cert
130
+ conn.ssl.client_cert = @configuration.cert_file if @configuration.cert_file
131
+ conn.ssl.client_key = @configuration.key_file if @configuration.key_file
132
+ end
133
+
134
+ # Proxy settings
135
+ conn.proxy = @configuration.proxy if @configuration&.proxy
136
+ end
137
+ end
138
+
139
+ def valid_method?(method)
140
+ %w[GET HEAD DELETE POST PUT PATCH OPTIONS].include?(method)
141
+ end
142
+
143
+ def serialize_body(body, content_type)
144
+ return body if body.is_a?(String)
145
+
146
+ if content_type&.include?('json')
147
+ body.is_a?(Hash) || body.is_a?(Array) ? JSON.generate(body) : body.to_s
148
+ else
149
+ body
150
+ end
151
+ end
152
+
153
+ def handle_response(response)
154
+ # Check for auth errors first (401/403)
155
+ if [401, 403].include?(response.status)
156
+ raise AuthorizationError.new(
157
+ "Authorization failed: #{response.status}",
158
+ status: response.status,
159
+ body: response.body,
160
+ headers: response.headers.to_h
161
+ )
162
+ end
163
+
164
+ # Check for other non-2xx responses
165
+ unless (200..299).cover?(response.status)
166
+ error_message = parse_error_message(response)
167
+ raise ApiError.new(
168
+ error_message,
169
+ status: response.status,
170
+ code: response.status,
171
+ reason: response.reason_phrase,
172
+ body: response.body,
173
+ headers: response.headers.to_h
174
+ )
175
+ end
176
+
177
+ # Return successful response
178
+ RestResponse.new(response)
179
+ end
180
+
181
+ def parse_error_message(response)
182
+ # Try to parse JSON error message
183
+ data = begin
184
+ JSON.parse(response.body)
185
+ rescue StandardError
186
+ nil
187
+ end
188
+ message = data&.dig('message') || response.reason_phrase || "HTTP #{response.status}"
189
+ "(#{response.status}) #{message}"
190
+ end
191
+ end
192
+
193
+ # Wrapper for Faraday response
194
+ class RestResponse
195
+ attr_reader :status, :reason, :body, :headers
196
+
197
+ def initialize(faraday_response)
198
+ @status = faraday_response.status
199
+ @reason = faraday_response.reason_phrase
200
+ @body = faraday_response.body
201
+ @headers = faraday_response.headers.to_h
202
+ end
203
+
204
+ def json
205
+ @json ||= JSON.parse(@body) if @body && !@body.empty?
206
+ rescue JSON::ParserError
207
+ nil
208
+ end
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conductor
4
+ module Orkes
5
+ module Models
6
+ # AccessKeyStatus constants
7
+ module AccessKeyStatus
8
+ ACTIVE = 'ACTIVE'
9
+ INACTIVE = 'INACTIVE'
10
+ end
11
+
12
+ # AccessKey model - represents an application access key
13
+ class AccessKey < Conductor::Http::Models::BaseModel
14
+ SWAGGER_TYPES = {
15
+ id: 'String',
16
+ status: 'String',
17
+ created_at: 'Integer'
18
+ }.freeze
19
+
20
+ ATTRIBUTE_MAP = {
21
+ id: :id,
22
+ status: :status,
23
+ created_at: :createdAt
24
+ }.freeze
25
+
26
+ attr_accessor :id, :status, :created_at
27
+
28
+ def initialize(params = {})
29
+ @id = params[:id]
30
+ @status = params[:status] || AccessKeyStatus::ACTIVE
31
+ @created_at = params[:created_at]
32
+ end
33
+ end
34
+
35
+ # CreatedAccessKey model - returned when creating a new access key
36
+ class CreatedAccessKey < Conductor::Http::Models::BaseModel
37
+ SWAGGER_TYPES = {
38
+ id: 'String',
39
+ secret: 'String'
40
+ }.freeze
41
+
42
+ ATTRIBUTE_MAP = {
43
+ id: :id,
44
+ secret: :secret
45
+ }.freeze
46
+
47
+ attr_accessor :id, :secret
48
+
49
+ def initialize(params = {})
50
+ @id = params[:id]
51
+ @secret = params[:secret]
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conductor
4
+ module Orkes
5
+ module Models
6
+ # GrantedPermission model - represents a permission granted on a target
7
+ class GrantedPermission < Conductor::Http::Models::BaseModel
8
+ SWAGGER_TYPES = {
9
+ target: 'TargetRef',
10
+ access: 'Array<String>'
11
+ }.freeze
12
+
13
+ ATTRIBUTE_MAP = {
14
+ target: :target,
15
+ access: :access
16
+ }.freeze
17
+
18
+ attr_accessor :target, :access
19
+
20
+ def initialize(params = {})
21
+ @target = params[:target]
22
+ @access = params[:access]
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conductor
4
+ module Orkes
5
+ module Models
6
+ # MetadataTag - a tag with type METADATA
7
+ # Convenience subclass of TagObject that sets type to METADATA
8
+ class MetadataTag < Conductor::Http::Models::TagObject
9
+ def initialize(key:, value:)
10
+ super(key: key, type: Conductor::Http::Models::TagType::METADATA, value: value)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conductor
4
+ module Orkes
5
+ module Models
6
+ # RateLimitTag - a tag with type RATE_LIMIT
7
+ # Convenience subclass of TagObject that sets type to RATE_LIMIT
8
+ class RateLimitTag < Conductor::Http::Models::TagObject
9
+ def initialize(key:, value:)
10
+ super(key: key, type: Conductor::Http::Models::TagType::RATE_LIMIT, value: value)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conductor
4
+ module Orkes
5
+ # OrkesClients - Factory class that creates all high-level clients from a single configuration
6
+ # This is the primary entry point for Orkes Conductor users.
7
+ #
8
+ # Usage:
9
+ # config = Conductor::Configuration.new
10
+ # config.server_url = 'https://developer.orkescloud.com/api'
11
+ # config.authentication_settings = Conductor::Configuration::AuthenticationSettings.new(
12
+ # key_id: 'your_key', key_secret: 'your_secret'
13
+ # )
14
+ # clients = Conductor::Orkes::OrkesClients.new(config)
15
+ #
16
+ # workflow_client = clients.get_workflow_client
17
+ # task_client = clients.get_task_client
18
+ # secret_client = clients.get_secret_client
19
+ #
20
+ class OrkesClients
21
+ attr_reader :configuration, :api_client
22
+
23
+ def initialize(configuration = nil)
24
+ @configuration = configuration || Configuration.new
25
+ @api_client = Http::ApiClient.new(configuration: @configuration)
26
+ end
27
+
28
+ def get_workflow_client
29
+ Client::WorkflowClient.new(@configuration)
30
+ end
31
+
32
+ def get_task_client
33
+ Client::TaskClient.new(@configuration)
34
+ end
35
+
36
+ def get_metadata_client
37
+ Client::MetadataClient.new(@configuration)
38
+ end
39
+
40
+ def get_scheduler_client
41
+ Client::SchedulerClient.new(@configuration)
42
+ end
43
+
44
+ def get_authorization_client
45
+ Client::AuthorizationClient.new(@api_client)
46
+ end
47
+
48
+ def get_secret_client
49
+ Client::SecretClient.new(@api_client)
50
+ end
51
+
52
+ def get_integration_client
53
+ Client::IntegrationClient.new(@api_client)
54
+ end
55
+
56
+ def get_prompt_client
57
+ Client::PromptClient.new(@api_client)
58
+ end
59
+
60
+ def get_schema_client
61
+ Client::SchemaClient.new(@api_client)
62
+ end
63
+
64
+ def get_workflow_executor
65
+ Workflow::WorkflowExecutor.new(@configuration)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conductor
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conductor
4
+ module Worker
5
+ module Events
6
+ # Base class for all Conductor events
7
+ # Provides a timestamp for when the event occurred
8
+ class ConductorEvent
9
+ # @return [Time] UTC timestamp when event was created
10
+ attr_reader :timestamp
11
+
12
+ def initialize
13
+ @timestamp = Time.now.utc
14
+ end
15
+
16
+ # @return [Hash] Event as a hash for serialization
17
+ def to_h
18
+ { timestamp: @timestamp.iso8601(3) }
19
+ end
20
+ end
21
+
22
+ # Base class for task runner events
23
+ # All events related to task polling and execution inherit from this
24
+ class TaskRunnerEvent < ConductorEvent
25
+ # @return [String] The task type/definition name
26
+ attr_reader :task_type
27
+
28
+ # @param task_type [String] Task definition name
29
+ def initialize(task_type:)
30
+ super()
31
+ @task_type = task_type
32
+ end
33
+
34
+ def to_h
35
+ super.merge(task_type: @task_type)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'sync_event_dispatcher'
4
+
5
+ module Conductor
6
+ module Worker
7
+ module Events
8
+ # Process-wide default event dispatcher.
9
+ #
10
+ # Used by library layers (e.g. HTTP client) that don't have an obvious
11
+ # owner to receive a dispatcher reference. Listeners can subscribe to this
12
+ # singleton to receive events regardless of which RestClient/ApiClient
13
+ # instance generated them.
14
+ class GlobalDispatcher
15
+ class << self
16
+ def instance
17
+ @instance ||= SyncEventDispatcher.new
18
+ end
19
+
20
+ def reset!
21
+ @instance = SyncEventDispatcher.new
22
+ end
23
+
24
+ def publish(event)
25
+ instance.publish(event)
26
+ end
27
+
28
+ def http_metrics_enabled?
29
+ instance.has_listeners?(HttpApiRequest)
30
+ rescue StandardError
31
+ false
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'conductor_event'
4
+
5
+ module Conductor
6
+ module Worker
7
+ module Events
8
+ class HttpApiRequest < ConductorEvent
9
+ attr_reader :method, :uri, :status, :duration_ms
10
+
11
+ def initialize(method:, uri:, status:, duration_ms:)
12
+ super()
13
+ @method = method.to_s.upcase
14
+ @uri = uri.to_s
15
+ @status = status.to_s
16
+ @duration_ms = duration_ms
17
+ end
18
+
19
+ def to_h
20
+ super.merge(method: @method, uri: @uri, status: @status, duration_ms: @duration_ms)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'task_runner_events'
4
+ require_relative 'workflow_events'
5
+ require_relative 'http_events'
6
+
7
+ module Conductor
8
+ module Worker
9
+ module Events
10
+ class ListenerRegistry
11
+ EVENT_METHOD_MAP = {
12
+ PollStarted => :on_poll_started,
13
+ PollCompleted => :on_poll_completed,
14
+ PollFailure => :on_poll_failure,
15
+ TaskExecutionStarted => :on_task_execution_started,
16
+ TaskExecutionCompleted => :on_task_execution_completed,
17
+ TaskExecutionFailure => :on_task_execution_failure,
18
+ TaskUpdateCompleted => :on_task_update_completed,
19
+ TaskUpdateFailure => :on_task_update_failure,
20
+ TaskPaused => :on_task_paused,
21
+ ThreadUncaughtException => :on_thread_uncaught_exception,
22
+ ActiveWorkersChanged => :on_active_workers_changed,
23
+ WorkflowStartError => :on_workflow_start_error,
24
+ WorkflowInputSize => :on_workflow_input_size,
25
+ HttpApiRequest => :on_http_api_request
26
+ }.freeze
27
+
28
+ def self.register_task_runner_listener(listener, dispatcher)
29
+ EVENT_METHOD_MAP.each do |event_class, method_name|
30
+ dispatcher.register(event_class, ->(event) { listener.send(method_name, event) }) if listener.respond_to?(method_name)
31
+ end
32
+ end
33
+
34
+ def self.register_all(listeners, dispatcher)
35
+ listeners.each { |listener| register_task_runner_listener(listener, dispatcher) }
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conductor
4
+ module Worker
5
+ module Events
6
+ # Listener protocol for task runner events.
7
+ # All methods are optional - the dispatcher uses duck typing (respond_to?).
8
+ module TaskRunnerEventsListener
9
+ def on_poll_started(event); end
10
+ def on_poll_completed(event); end
11
+ def on_poll_failure(event); end
12
+ def on_task_execution_started(event); end
13
+ def on_task_execution_completed(event); end
14
+ def on_task_execution_failure(event); end
15
+ def on_task_update_completed(event); end
16
+ def on_task_update_failure(event); end
17
+ def on_task_paused(event); end
18
+ def on_thread_uncaught_exception(event); end
19
+ def on_active_workers_changed(event); end
20
+ end
21
+
22
+ # Listener protocol for workflow-lifecycle events
23
+ module WorkflowEventsListener
24
+ def on_workflow_start_error(event); end
25
+ def on_workflow_input_size(event); end
26
+ end
27
+
28
+ # Listener protocol for HTTP API client events
29
+ module HttpEventsListener
30
+ def on_http_api_request(event); end
31
+ end
32
+ end
33
+ end
34
+ end